From df1ceaa4c2586335425c9a17f837d7e90f59952f Mon Sep 17 00:00:00 2001 From: Keisuke Nitta Date: Tue, 22 Oct 2024 18:11:03 +0900 Subject: [PATCH 1/6] refactor: split AWSIntergrationServices --- internal/mackerel/aws_integration.go | 6 +- internal/mackerel/aws_integration_test.go | 208 +++++++++++----------- 2 files changed, 111 insertions(+), 103 deletions(-) diff --git a/internal/mackerel/aws_integration.go b/internal/mackerel/aws_integration.go index df7ec81..015570a 100644 --- a/internal/mackerel/aws_integration.go +++ b/internal/mackerel/aws_integration.go @@ -22,6 +22,10 @@ type AWSIntegrationModel struct { IncludedTags types.String `tfsdk:"included_tags"` ExcludedTags types.String `tfsdk:"excluded_tags"` + AWSIntegrationSerfvices +} + +type AWSIntegrationSerfvices struct { EC2 AWSIntegrationServiceWithRetireAutomaticallyOpt `tfsdk:"ec2"` ELB AWSIntegrationServiceOpt `tfsdk:"elb"` ALB AWSIntegrationServiceOpt `tfsdk:"alb"` @@ -259,7 +263,7 @@ func (m *AWSIntegrationModel) merge(newModel AWSIntegrationModel) { type awsServiceEachFunc func(name string, service *AWSIntegrationService) *AWSIntegrationService // Iterates and updates over services by name -func (m *AWSIntegrationModel) each(fn awsServiceEachFunc) { +func (m *AWSIntegrationSerfvices) each(fn awsServiceEachFunc) { m.EC2.each("EC2", fn) m.ELB.each("ELB", fn) m.ALB.each("ALB", fn) diff --git a/internal/mackerel/aws_integration_test.go b/internal/mackerel/aws_integration_test.go index b7e0d6d..493adca 100644 --- a/internal/mackerel/aws_integration_test.go +++ b/internal/mackerel/aws_integration_test.go @@ -192,57 +192,59 @@ func Test_AWSIntegration_fromAPI(t *testing.T) { IncludedTags: types.StringValue("Name:production-server,Environment:production"), ExcludedTags: types.StringValue("Name:staging-server,Environment:staging"), - EC2: []AWSIntegrationServiceWithRetireAutomatically{{ - Enable: types.BoolValue(true), - ExcludedMetrics: []string{}, - RetireAutomatically: types.BoolValue(true), - }}, - ELB: []AWSIntegrationService{}, - ALB: []AWSIntegrationService{{ - Enable: types.BoolValue(true), - Role: types.StringValue("service: role"), - ExcludedMetrics: []string{"alb.request.count", "alb.bytes.processed"}, - RetireAutomatically: types.BoolValue(false), - }}, - NLB: []AWSIntegrationService{{ - Enable: types.BoolValue(true), - Role: types.StringNull(), - ExcludedMetrics: []string{}, - RetireAutomatically: types.BoolValue(false), - }}, - RDS: []AWSIntegrationServiceWithRetireAutomatically{{ - Enable: types.BoolValue(true), - Role: types.StringValue("service: role"), - ExcludedMetrics: []string{"rds.cpu.used"}, - RetireAutomatically: types.BoolValue(false), - }}, - Redshift: []AWSIntegrationService{}, - ElastiCache: []AWSIntegrationServiceWithRetireAutomatically{}, - SQS: []AWSIntegrationService{}, - Lambda: []AWSIntegrationService{{ - Enable: types.BoolValue(true), - Role: types.StringNull(), - ExcludedMetrics: []string{}, - RetireAutomatically: types.BoolValue(false), - }}, - DynamoDB: []AWSIntegrationService{}, - CloudFront: []AWSIntegrationService{}, - APIGateway: []AWSIntegrationService{}, - Kinesis: []AWSIntegrationService{}, - S3: []AWSIntegrationService{}, - ES: []AWSIntegrationService{}, - ECSCluster: []AWSIntegrationService{}, - SES: []AWSIntegrationService{}, - States: []AWSIntegrationService{}, - EFS: []AWSIntegrationService{}, - Firehose: []AWSIntegrationService{}, - Batch: []AWSIntegrationService{}, - WAF: []AWSIntegrationService{}, - Billing: []AWSIntegrationService{}, - Route53: []AWSIntegrationService{}, - Connect: []AWSIntegrationService{}, - DocDB: []AWSIntegrationService{}, - CodeBuild: []AWSIntegrationService{}, + AWSIntegrationSerfvices: AWSIntegrationSerfvices{ + EC2: []AWSIntegrationServiceWithRetireAutomatically{{ + Enable: types.BoolValue(true), + ExcludedMetrics: []string{}, + RetireAutomatically: types.BoolValue(true), + }}, + ELB: []AWSIntegrationService{}, + ALB: []AWSIntegrationService{{ + Enable: types.BoolValue(true), + Role: types.StringValue("service: role"), + ExcludedMetrics: []string{"alb.request.count", "alb.bytes.processed"}, + RetireAutomatically: types.BoolValue(false), + }}, + NLB: []AWSIntegrationService{{ + Enable: types.BoolValue(true), + Role: types.StringNull(), + ExcludedMetrics: []string{}, + RetireAutomatically: types.BoolValue(false), + }}, + RDS: []AWSIntegrationServiceWithRetireAutomatically{{ + Enable: types.BoolValue(true), + Role: types.StringValue("service: role"), + ExcludedMetrics: []string{"rds.cpu.used"}, + RetireAutomatically: types.BoolValue(false), + }}, + Redshift: []AWSIntegrationService{}, + ElastiCache: []AWSIntegrationServiceWithRetireAutomatically{}, + SQS: []AWSIntegrationService{}, + Lambda: []AWSIntegrationService{{ + Enable: types.BoolValue(true), + Role: types.StringNull(), + ExcludedMetrics: []string{}, + RetireAutomatically: types.BoolValue(false), + }}, + DynamoDB: []AWSIntegrationService{}, + CloudFront: []AWSIntegrationService{}, + APIGateway: []AWSIntegrationService{}, + Kinesis: []AWSIntegrationService{}, + S3: []AWSIntegrationService{}, + ES: []AWSIntegrationService{}, + ECSCluster: []AWSIntegrationService{}, + SES: []AWSIntegrationService{}, + States: []AWSIntegrationService{}, + EFS: []AWSIntegrationService{}, + Firehose: []AWSIntegrationService{}, + Batch: []AWSIntegrationService{}, + WAF: []AWSIntegrationService{}, + Billing: []AWSIntegrationService{}, + Route53: []AWSIntegrationService{}, + Connect: []AWSIntegrationService{}, + DocDB: []AWSIntegrationService{}, + CodeBuild: []AWSIntegrationService{}, + }, }, }, } @@ -285,57 +287,59 @@ func Test_AWSIntegration_toAPI(t *testing.T) { IncludedTags: types.StringValue("Name:production-server,Environment:production"), ExcludedTags: types.StringValue("Name:staging-server,Environment:staging"), - EC2: []AWSIntegrationServiceWithRetireAutomatically{{ - Enable: types.BoolValue(true), - ExcludedMetrics: []string{}, - RetireAutomatically: types.BoolValue(true), - }}, - ELB: []AWSIntegrationService{}, - ALB: []AWSIntegrationService{{ - Enable: types.BoolValue(true), - Role: types.StringValue("service: role"), - ExcludedMetrics: []string{"alb.request.count", "alb.bytes.processed"}, - RetireAutomatically: types.BoolValue(false), - }}, - NLB: []AWSIntegrationService{{ - Enable: types.BoolValue(true), - Role: types.StringNull(), - ExcludedMetrics: []string{}, - RetireAutomatically: types.BoolValue(false), - }}, - RDS: []AWSIntegrationServiceWithRetireAutomatically{{ - Enable: types.BoolValue(true), - Role: types.StringValue("service: role"), - ExcludedMetrics: []string{"rds.cpu.used"}, - RetireAutomatically: types.BoolValue(false), - }}, - Redshift: []AWSIntegrationService{}, - ElastiCache: []AWSIntegrationServiceWithRetireAutomatically{}, - SQS: []AWSIntegrationService{}, - Lambda: []AWSIntegrationService{{ - Enable: types.BoolValue(true), - Role: types.StringNull(), - ExcludedMetrics: []string{}, - RetireAutomatically: types.BoolValue(false), - }}, - DynamoDB: []AWSIntegrationService{}, - CloudFront: []AWSIntegrationService{}, - APIGateway: []AWSIntegrationService{}, - Kinesis: []AWSIntegrationService{}, - S3: []AWSIntegrationService{}, - ES: []AWSIntegrationService{}, - ECSCluster: []AWSIntegrationService{}, - SES: []AWSIntegrationService{}, - States: []AWSIntegrationService{}, - EFS: []AWSIntegrationService{}, - Firehose: []AWSIntegrationService{}, - Batch: []AWSIntegrationService{}, - WAF: []AWSIntegrationService{}, - Billing: []AWSIntegrationService{}, - Route53: []AWSIntegrationService{}, - Connect: []AWSIntegrationService{}, - DocDB: []AWSIntegrationService{}, - CodeBuild: []AWSIntegrationService{}, + AWSIntegrationSerfvices: AWSIntegrationSerfvices{ + EC2: []AWSIntegrationServiceWithRetireAutomatically{{ + Enable: types.BoolValue(true), + ExcludedMetrics: []string{}, + RetireAutomatically: types.BoolValue(true), + }}, + ELB: []AWSIntegrationService{}, + ALB: []AWSIntegrationService{{ + Enable: types.BoolValue(true), + Role: types.StringValue("service: role"), + ExcludedMetrics: []string{"alb.request.count", "alb.bytes.processed"}, + RetireAutomatically: types.BoolValue(false), + }}, + NLB: []AWSIntegrationService{{ + Enable: types.BoolValue(true), + Role: types.StringNull(), + ExcludedMetrics: []string{}, + RetireAutomatically: types.BoolValue(false), + }}, + RDS: []AWSIntegrationServiceWithRetireAutomatically{{ + Enable: types.BoolValue(true), + Role: types.StringValue("service: role"), + ExcludedMetrics: []string{"rds.cpu.used"}, + RetireAutomatically: types.BoolValue(false), + }}, + Redshift: []AWSIntegrationService{}, + ElastiCache: []AWSIntegrationServiceWithRetireAutomatically{}, + SQS: []AWSIntegrationService{}, + Lambda: []AWSIntegrationService{{ + Enable: types.BoolValue(true), + Role: types.StringNull(), + ExcludedMetrics: []string{}, + RetireAutomatically: types.BoolValue(false), + }}, + DynamoDB: []AWSIntegrationService{}, + CloudFront: []AWSIntegrationService{}, + APIGateway: []AWSIntegrationService{}, + Kinesis: []AWSIntegrationService{}, + S3: []AWSIntegrationService{}, + ES: []AWSIntegrationService{}, + ECSCluster: []AWSIntegrationService{}, + SES: []AWSIntegrationService{}, + States: []AWSIntegrationService{}, + EFS: []AWSIntegrationService{}, + Firehose: []AWSIntegrationService{}, + Batch: []AWSIntegrationService{}, + WAF: []AWSIntegrationService{}, + Billing: []AWSIntegrationService{}, + Route53: []AWSIntegrationService{}, + Connect: []AWSIntegrationService{}, + DocDB: []AWSIntegrationService{}, + CodeBuild: []AWSIntegrationService{}, + }, }, param: mackerel.CreateAWSIntegrationParam{ Name: "aws-integration", From caf8d1c75169d7018a6241a230f00b38076e499e Mon Sep 17 00:00:00 2001 From: Keisuke Nitta Date: Tue, 22 Oct 2024 18:11:53 +0900 Subject: [PATCH 2/6] feat: add model for data source --- internal/mackerel/aws_integration.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/internal/mackerel/aws_integration.go b/internal/mackerel/aws_integration.go index 015570a..2c546e9 100644 --- a/internal/mackerel/aws_integration.go +++ b/internal/mackerel/aws_integration.go @@ -25,6 +25,21 @@ type AWSIntegrationModel struct { AWSIntegrationSerfvices } +type AWSIntegrationDataSourceModel struct { + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Memo types.String `tfsdk:"memo"` + Key types.String `tfsdk:"key"` + SecretKey types.String `tfsdk:"-"` + RoleARN types.String `tfsdk:"role_arn"` + ExternalID types.String `tfsdk:"external_id"` + Region types.String `tfsdk:"region"` + IncludedTags types.String `tfsdk:"included_tags"` + ExcludedTags types.String `tfsdk:"excluded_tags"` + + AWSIntegrationSerfvices +} + type AWSIntegrationSerfvices struct { EC2 AWSIntegrationServiceWithRetireAutomaticallyOpt `tfsdk:"ec2"` ELB AWSIntegrationServiceOpt `tfsdk:"elb"` From 27994f02a40e4a790bc3a31cd03be0c0c951ebe0 Mon Sep 17 00:00:00 2001 From: Keisuke Nitta Date: Tue, 22 Oct 2024 18:13:02 +0900 Subject: [PATCH 3/6] feat: publish ReadAWSIntegration --- internal/mackerel/aws_integration.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/mackerel/aws_integration.go b/internal/mackerel/aws_integration.go index 2c546e9..a23cbd0 100644 --- a/internal/mackerel/aws_integration.go +++ b/internal/mackerel/aws_integration.go @@ -88,6 +88,10 @@ type AWSIntegrationServiceWithRetireAutomatically struct { type AWSIntegrationServiceWithRetireAutomaticallyOpt []AWSIntegrationServiceWithRetireAutomatically // length <= 1 +func ReadAWSIntegration(_ context.Context, client *Client, id string) (*AWSIntegrationModel, error) { + return readAWSIntegration(client, id) +} + func readAWSIntegration(client *Client, id string) (*AWSIntegrationModel, error) { mackerelAWSIntegration, err := client.FindAWSIntegration(id) if err != nil { From 97b413d31669b668b036122613363f205311c011 Mon Sep 17 00:00:00 2001 From: Keisuke Nitta Date: Tue, 22 Oct 2024 18:18:17 +0900 Subject: [PATCH 4/6] feat: reimplement mackerel_aws_integration data source --- .../data_source_mackerel_aws_integration.go | 138 ++++++++++++++++++ ...ta_source_mackerel_aws_integration_test.go | 25 ++++ internal/provider/provider.go | 1 + mackerel/provider.go | 1 + 4 files changed, 165 insertions(+) create mode 100644 internal/provider/data_source_mackerel_aws_integration.go create mode 100644 internal/provider/data_source_mackerel_aws_integration_test.go diff --git a/internal/provider/data_source_mackerel_aws_integration.go b/internal/provider/data_source_mackerel_aws_integration.go new file mode 100644 index 0000000..35fa7f3 --- /dev/null +++ b/internal/provider/data_source_mackerel_aws_integration.go @@ -0,0 +1,138 @@ +package provider + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/mackerelio-labs/terraform-provider-mackerel/internal/mackerel" +) + +var ( + _ datasource.DataSource = (*mackerelAWSIntegrationDataSource)(nil) + _ datasource.DataSourceWithConfigure = (*mackerelAWSIntegrationDataSource)(nil) +) + +type mackerelAWSIntegrationDataSource struct { + Client *mackerel.Client +} + +func NewMackerelAWSIntegrationDataSource() datasource.DataSource { + return &mackerelAWSIntegrationDataSource{} +} + +func (d *mackerelAWSIntegrationDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_aws_integration" +} + +func (d *mackerelAWSIntegrationDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schemaAWSIntegrationDataSource() +} + +func (d *mackerelAWSIntegrationDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + client, diags := retrieveClient(ctx, req.ProviderData) + resp.Diagnostics.Append(diags...) + if diags.HasError() { + return + } + d.Client = client +} + +func (d *mackerelAWSIntegrationDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var config mackerel.AWSIntegrationDataSourceModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + + data, err := mackerel.ReadAWSIntegration(ctx, d.Client, config.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + "Unable to read an AWS integration", + err.Error(), + ) + return + } + dataSourceModel := mackerel.AWSIntegrationDataSourceModel(*data) + + resp.Diagnostics.Append(resp.State.Set(ctx, &dataSourceModel)...) +} + +func schemaAWSIntegrationDataSource() schema.Schema { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Description: schemaAWSIntegrationIDDesc, + Required: true, + }, + "name": schema.StringAttribute{ + Description: schemaAWSIntegrationNameDesc, + Computed: true, + }, + "memo": schema.StringAttribute{ + Description: schemaAWSIntegrationMemoDesc, + Computed: true, + }, + "key": schema.StringAttribute{ + Description: schemaAWSIntegrationKeyDesc, + Computed: true, + }, + "role_arn": schema.StringAttribute{ + Description: schemaAWSIntegrationRoleARNDesc, + Computed: true, + }, + "external_id": schema.StringAttribute{ + Description: schemaAWSIntegrationExternalIDDesc, + Computed: true, + }, + "region": schema.StringAttribute{ + Description: schemaAWSIntegrationRegionDesc, + Computed: true, + }, + "included_tags": schema.StringAttribute{ + Description: schemaAWSIntegrationIncludedTagsDesc, + Computed: true, + }, + "excluded_tags": schema.StringAttribute{ + Description: schemaAWSIntegrationExcludedTagsDesc, + Computed: true, + }, + } + + for name, spec := range awsIntegrationServices { + if spec.supportsAutoRetire { + attrs[name] = schema.ListAttribute{ + Computed: true, + ElementType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "enable": types.BoolType, + "role": types.StringType, + "excluded_metrics": types.ListType{ + ElemType: types.StringType, + }, + "retire_automatically": types.BoolType, + }, + }, + } + } else { + attrs[name] = schema.ListAttribute{ + Computed: true, + ElementType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "enable": types.BoolType, + "role": types.StringType, + "excluded_metrics": types.ListType{ + ElemType: types.StringType, + }, + }, + }, + } + } + } + + s := schema.Schema{ + Attributes: attrs, + } + return s +} diff --git a/internal/provider/data_source_mackerel_aws_integration_test.go b/internal/provider/data_source_mackerel_aws_integration_test.go new file mode 100644 index 0000000..ad423e0 --- /dev/null +++ b/internal/provider/data_source_mackerel_aws_integration_test.go @@ -0,0 +1,25 @@ +package provider_test + +import ( + "context" + "testing" + + fwdatasource "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/mackerelio-labs/terraform-provider-mackerel/internal/provider" +) + +func Test_MackerelAWSIntegrationDataSource_schema(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + req := fwdatasource.SchemaRequest{} + resp := fwdatasource.SchemaResponse{} + if provider.NewMackerelAWSIntegrationDataSource().Schema(ctx, req, &resp); resp.Diagnostics.HasError() { + t.Fatalf("schema diagnostics: %+v", resp.Diagnostics) + } + + if diags := resp.Schema.ValidateImplementation(ctx); diags.HasError() { + t.Fatalf("schema validation diagnostics: %+v", diags) + } +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 7f7cbb9..a921a4f 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -100,6 +100,7 @@ func (m *mackerelProvider) Resources(context.Context) []func() resource.Resource func (m *mackerelProvider) DataSources(context.Context) []func() datasource.DataSource { return []func() datasource.DataSource{ NewMackerelAlertGroupSettingDataSource, + NewMackerelAWSIntegrationDataSource, NewMackerelChannelDataSource, NewMackerelDashboardDataSource, NewMackerelDowntimeDataSource, diff --git a/mackerel/provider.go b/mackerel/provider.go index 3e8fe42..f02fde4 100644 --- a/mackerel/provider.go +++ b/mackerel/provider.go @@ -96,6 +96,7 @@ func protoV5ProviderServer(provider *schema.Provider) tfprotov5.ProviderServer { // Data Sources delete(provider.DataSourcesMap, "mackerel_alert_group_setting") + delete(provider.DataSourcesMap, "mackerel_aws_integration") delete(provider.DataSourcesMap, "mackerel_channel") delete(provider.DataSourcesMap, "mackerel_dashboard") delete(provider.DataSourcesMap, "mackerel_downtime") From 2faaac1953505348e7c49d42bb1756b5c3389d96 Mon Sep 17 00:00:00 2001 From: Keisuke Nitta Date: Thu, 31 Oct 2024 20:49:39 +0900 Subject: [PATCH 5/6] fix: each services has set type. --- internal/provider/data_source_mackerel_aws_integration.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/provider/data_source_mackerel_aws_integration.go b/internal/provider/data_source_mackerel_aws_integration.go index 35fa7f3..403fcb4 100644 --- a/internal/provider/data_source_mackerel_aws_integration.go +++ b/internal/provider/data_source_mackerel_aws_integration.go @@ -102,7 +102,7 @@ func schemaAWSIntegrationDataSource() schema.Schema { for name, spec := range awsIntegrationServices { if spec.supportsAutoRetire { - attrs[name] = schema.ListAttribute{ + attrs[name] = schema.SetAttribute{ Computed: true, ElementType: types.ObjectType{ AttrTypes: map[string]attr.Type{ @@ -116,7 +116,7 @@ func schemaAWSIntegrationDataSource() schema.Schema { }, } } else { - attrs[name] = schema.ListAttribute{ + attrs[name] = schema.SetAttribute{ Computed: true, ElementType: types.ObjectType{ AttrTypes: map[string]attr.Type{ From 68f021b859990a888d29319d239669ae5653d10b Mon Sep 17 00:00:00 2001 From: Keisuke Nitta Date: Tue, 5 Nov 2024 16:15:03 +0900 Subject: [PATCH 6/6] fix: add missing sensitive flag --- internal/provider/data_source_mackerel_aws_integration.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/provider/data_source_mackerel_aws_integration.go b/internal/provider/data_source_mackerel_aws_integration.go index 403fcb4..ed3a514 100644 --- a/internal/provider/data_source_mackerel_aws_integration.go +++ b/internal/provider/data_source_mackerel_aws_integration.go @@ -84,6 +84,7 @@ func schemaAWSIntegrationDataSource() schema.Schema { }, "external_id": schema.StringAttribute{ Description: schemaAWSIntegrationExternalIDDesc, + Sensitive: true, Computed: true, }, "region": schema.StringAttribute{