From 54288e10ac0a2bb4af6437756191554e833c6533 Mon Sep 17 00:00:00 2001 From: Daniel Spangenberg Date: Tue, 30 Nov 2021 09:44:06 +0100 Subject: [PATCH] Enhance config configuratiguration recorder with status (#301) * Enhance config configuratiguration recorder with status Co-authored-by: roneli <38083777+roneli@users.noreply.github.com> --- client/mocks/builders_test.go | 3 +- client/mocks/mock_configservice.go | 20 +++++ client/mocks/resources_test.go | 7 +- client/services.go | 1 + .../aws_config_configuration_recorders.md | 7 ++ resources/config_configuration_recorders.go | 86 ++++++++++++++++++- .../config_configuration_recorders_test.go | 23 +++-- resources/migrations/8_v0.7.1.down.sql | 7 ++ resources/migrations/9_v0.7.1.up.sql | 7 ++ 9 files changed, 144 insertions(+), 17 deletions(-) create mode 100644 resources/migrations/8_v0.7.1.down.sql create mode 100644 resources/migrations/9_v0.7.1.up.sql diff --git a/client/mocks/builders_test.go b/client/mocks/builders_test.go index f61f04629..5345cd861 100644 --- a/client/mocks/builders_test.go +++ b/client/mocks/builders_test.go @@ -35,9 +35,8 @@ import ( route53Types "github.com/aws/aws-sdk-go-v2/service/route53/types" "github.com/aws/aws-sdk-go-v2/service/sns" snsTypes "github.com/aws/aws-sdk-go-v2/service/sns/types" - "github.com/golang/mock/gomock" - "github.com/cloudquery/faker/v3" + "github.com/golang/mock/gomock" "github.com/cloudquery/cq-provider-aws/client" "github.com/cloudquery/cq-provider-aws/client/mocks" diff --git a/client/mocks/mock_configservice.go b/client/mocks/mock_configservice.go index ec8ca9403..b1c64c211 100644 --- a/client/mocks/mock_configservice.go +++ b/client/mocks/mock_configservice.go @@ -35,6 +35,26 @@ func (m *MockConfigServiceClient) EXPECT() *MockConfigServiceClientMockRecorder return m.recorder } +// DescribeConfigurationRecorderStatus mocks base method. +func (m *MockConfigServiceClient) DescribeConfigurationRecorderStatus(arg0 context.Context, arg1 *configservice.DescribeConfigurationRecorderStatusInput, arg2 ...func(*configservice.Options)) (*configservice.DescribeConfigurationRecorderStatusOutput, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeConfigurationRecorderStatus", varargs...) + ret0, _ := ret[0].(*configservice.DescribeConfigurationRecorderStatusOutput) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeConfigurationRecorderStatus indicates an expected call of DescribeConfigurationRecorderStatus. +func (mr *MockConfigServiceClientMockRecorder) DescribeConfigurationRecorderStatus(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeConfigurationRecorderStatus", reflect.TypeOf((*MockConfigServiceClient)(nil).DescribeConfigurationRecorderStatus), varargs...) +} + // DescribeConfigurationRecorders mocks base method. func (m *MockConfigServiceClient) DescribeConfigurationRecorders(arg0 context.Context, arg1 *configservice.DescribeConfigurationRecordersInput, arg2 ...func(*configservice.Options)) (*configservice.DescribeConfigurationRecordersOutput, error) { m.ctrl.T.Helper() diff --git a/client/mocks/resources_test.go b/client/mocks/resources_test.go index b414432c6..d167ee08b 100644 --- a/client/mocks/resources_test.go +++ b/client/mocks/resources_test.go @@ -5,14 +5,15 @@ import ( "os" "testing" - "github.com/cloudquery/cq-provider-sdk/logging" - "github.com/cloudquery/cq-provider-sdk/provider/schema" - providertest "github.com/cloudquery/cq-provider-sdk/provider/testing" "github.com/cloudquery/faker/v3" "github.com/golang/mock/gomock" "github.com/hashicorp/go-hclog" "github.com/jackc/pgx/v4" + "github.com/cloudquery/cq-provider-sdk/logging" + "github.com/cloudquery/cq-provider-sdk/provider/schema" + providertest "github.com/cloudquery/cq-provider-sdk/provider/testing" + "github.com/cloudquery/cq-provider-aws/client" "github.com/cloudquery/cq-provider-aws/resources" ) diff --git a/client/services.go b/client/services.go index c8251289a..3abd71ea8 100644 --- a/client/services.go +++ b/client/services.go @@ -142,6 +142,7 @@ type CognitoUserPoolsClient interface { //go:generate mockgen -package=mocks -destination=./mocks/mock_configservice.go . ConfigServiceClient type ConfigServiceClient interface { DescribeConfigurationRecorders(ctx context.Context, params *configservice.DescribeConfigurationRecordersInput, optFns ...func(*configservice.Options)) (*configservice.DescribeConfigurationRecordersOutput, error) + DescribeConfigurationRecorderStatus(ctx context.Context, params *configservice.DescribeConfigurationRecorderStatusInput, optFns ...func(*configservice.Options)) (*configservice.DescribeConfigurationRecorderStatusOutput, error) configservice.DescribeConformancePacksAPIClient } diff --git a/docs/tables/aws_config_configuration_recorders.md b/docs/tables/aws_config_configuration_recorders.md index 40931fdfc..41856aaea 100644 --- a/docs/tables/aws_config_configuration_recorders.md +++ b/docs/tables/aws_config_configuration_recorders.md @@ -12,3 +12,10 @@ An object that represents the recording of configuration changes of an AWS resou |recording_group_include_global_resource_types|boolean|Specifies whether AWS Config includes all supported types of global resources (for example, IAM resources) with the resources that it records.| |recording_group_resource_types|text[]|A comma-separated list that specifies the types of AWS resources for which AWS Config records configuration changes (for example, AWS::EC2::Instance or AWS::CloudTrail::Trail).| |role_arn|text|Amazon Resource Name (ARN) of the IAM role used to describe the AWS resources associated with the account.| +|status_last_error_code|text|The error code indicating that the recording failed.| +|status_last_error_message|text|The message indicating that the recording failed due to an error.| +|status_last_start_time|timestamp without time zone|The time the recorder was last started.| +|status_last_status|text|The last (previous) status of the recorder.| +|status_last_status_change_time|timestamp without time zone|The time when the status was last changed.| +|status_last_stop_time|timestamp without time zone|The time the recorder was last stopped.| +|status_recording|boolean|Specifies whether or not the recorder is currently recording.| diff --git a/resources/config_configuration_recorders.go b/resources/config_configuration_recorders.go index 4b2f47f68..ce1148ff6 100644 --- a/resources/config_configuration_recorders.go +++ b/resources/config_configuration_recorders.go @@ -3,12 +3,14 @@ package resources import ( "context" "fmt" + "time" + "github.com/aws/aws-sdk-go-v2/service/configservice" "github.com/aws/aws-sdk-go-v2/service/configservice/types" - "github.com/aws/aws-sdk-go-v2/service/configservice" - "github.com/cloudquery/cq-provider-aws/client" "github.com/cloudquery/cq-provider-sdk/provider/schema" + + "github.com/cloudquery/cq-provider-aws/client" ) func ConfigConfigurationRecorders() *schema.Table { @@ -68,6 +70,41 @@ func ConfigConfigurationRecorders() *schema.Table { Type: schema.TypeString, Resolver: schema.PathResolver("RoleARN"), }, + { + Name: "status_last_error_code", + Description: "The error code indicating that the recording failed.", + Type: schema.TypeString, + }, + { + Name: "status_last_error_message", + Description: "The message indicating that the recording failed due to an error.", + Type: schema.TypeString, + }, + { + Name: "status_last_start_time", + Description: "The time the recorder was last started.", + Type: schema.TypeTimestamp, + }, + { + Name: "status_last_status", + Description: "The last (previous) status of the recorder.", + Type: schema.TypeString, + }, + { + Name: "status_last_status_change_time", + Description: "The time when the status was last changed.", + Type: schema.TypeTimestamp, + }, + { + Name: "status_last_stop_time", + Description: "The time the recorder was last stopped.", + Type: schema.TypeTimestamp, + }, + { + Name: "status_recording", + Description: "Specifies whether or not the recorder is currently recording.", + Type: schema.TypeBool, + }, }, } } @@ -83,16 +120,57 @@ func fetchConfigConfigurationRecorders(ctx context.Context, meta schema.ClientMe if err != nil { return err } - res <- resp.ConfigurationRecorders + if len(resp.ConfigurationRecorders) == 0 { + return nil + } + names := make([]string, len(resp.ConfigurationRecorders)) + for i, configurationRecorder := range resp.ConfigurationRecorders { + names[i] = *configurationRecorder.Name + } + status, err := c.Services().ConfigService.DescribeConfigurationRecorderStatus(ctx, &configservice.DescribeConfigurationRecorderStatusInput{ + ConfigurationRecorderNames: names, + }) + if err != nil { + return err + } + for _, configurationRecorder := range resp.ConfigurationRecorders { + var configurationRecorderStatus types.ConfigurationRecorderStatus + for _, s := range status.ConfigurationRecordersStatus { + if s.Name == configurationRecorder.Name { + configurationRecorderStatus = s + break + } + } + res <- configurationRecorderWrapper{ + ConfigurationRecorder: configurationRecorder, + StatusLastErrorCode: configurationRecorderStatus.LastErrorCode, + StatusLastErrorMessage: configurationRecorderStatus.LastErrorMessage, + StatusLastStartTime: configurationRecorderStatus.LastStartTime, + StatusLastStatus: configurationRecorderStatus.LastStatus, + StatusLastStatusChangeTime: configurationRecorderStatus.LastStatusChangeTime, + StatusLastStopTime: configurationRecorderStatus.LastStopTime, + StatusRecording: configurationRecorderStatus.Recording, + } + } return nil } func generateConfigRecorderArn(_ context.Context, meta schema.ClientMeta, resource *schema.Resource, c schema.Column) error { cl := meta.(*client.Client) - cfg, ok := resource.Item.(types.ConfigurationRecorder) + cfg, ok := resource.Item.(configurationRecorderWrapper) if !ok { return fmt.Errorf("not config config recorder") } return resource.Set(c.Name, client.GenerateResourceARN("config", "config-recorder", *cfg.Name, cl.Region, cl.AccountID)) +} +type configurationRecorderWrapper struct { + types.ConfigurationRecorder + StatusLastErrorCode *string + StatusLastErrorMessage *string + StatusLastStartTime *time.Time + StatusLastStatus types.RecorderStatus + StatusLastStatusChangeTime *time.Time + StatusLastStopTime *time.Time + StatusRecording bool } diff --git a/resources/config_configuration_recorders_test.go b/resources/config_configuration_recorders_test.go index b4b0a17b4..45116c32b 100644 --- a/resources/config_configuration_recorders_test.go +++ b/resources/config_configuration_recorders_test.go @@ -5,25 +5,32 @@ import ( "github.com/aws/aws-sdk-go-v2/service/configservice" "github.com/aws/aws-sdk-go-v2/service/configservice/types" - "github.com/cloudquery/cq-provider-aws/client" - "github.com/cloudquery/cq-provider-aws/client/mocks" "github.com/cloudquery/faker/v3" "github.com/golang/mock/gomock" + + "github.com/cloudquery/cq-provider-aws/client" + "github.com/cloudquery/cq-provider-aws/client/mocks" ) func buildConfigConfigurationRecorders(t *testing.T, ctrl *gomock.Controller) client.Services { m := mocks.NewMockConfigServiceClient(ctrl) - - cr := types.ConfigurationRecorder{} - err := faker.FakeData(&cr) - if err != nil { + l := types.ConfigurationRecorder{} + if err := faker.FakeData(&l); err != nil { + t.Fatal(err) + } + sl := types.ConfigurationRecorderStatus{} + if err := faker.FakeData(&sl); err != nil { t.Fatal(err) } + sl.Name = l.Name + m.EXPECT().DescribeConfigurationRecorderStatus(gomock.Any(), gomock.Any(), gomock.Any()).Return( + &configservice.DescribeConfigurationRecorderStatusOutput{ + ConfigurationRecordersStatus: []types.ConfigurationRecorderStatus{sl}, + }, nil) m.EXPECT().DescribeConfigurationRecorders(gomock.Any(), gomock.Any(), gomock.Any()).Return( &configservice.DescribeConfigurationRecordersOutput{ - ConfigurationRecorders: []types.ConfigurationRecorder{cr}, + ConfigurationRecorders: []types.ConfigurationRecorder{l}, }, nil) - return client.Services{ ConfigService: m, } diff --git a/resources/migrations/8_v0.7.1.down.sql b/resources/migrations/8_v0.7.1.down.sql new file mode 100644 index 000000000..38943f324 --- /dev/null +++ b/resources/migrations/8_v0.7.1.down.sql @@ -0,0 +1,7 @@ +ALTER TABLE IF EXISTS "aws_config_configuration_recorders" DROP COLUMN status_last_error_code, + DROP COLUMN status_last_error_message, + DROP COLUMN status_last_start_time, + DROP COLUMN status_last_status, + DROP COLUMN status_last_status_change_time, + DROP COLUMN status_last_stop_time, + DROP COLUMN status_recording; diff --git a/resources/migrations/9_v0.7.1.up.sql b/resources/migrations/9_v0.7.1.up.sql new file mode 100644 index 000000000..078a10afb --- /dev/null +++ b/resources/migrations/9_v0.7.1.up.sql @@ -0,0 +1,7 @@ +ALTER TABLE IF EXISTS "aws_config_configuration_recorders" ADD COLUMN status_last_error_code text, + ADD COLUMN status_last_error_message text, + ADD COLUMN status_last_start_time timestamp without time zone, + ADD COLUMN status_last_status text, + ADD COLUMN status_last_status_change_time timestamp without time zone, + ADD COLUMN status_last_stop_time timestamp without time zone, + ADD COLUMN status_recording boolean;