diff --git a/.changelog/19100.txt b/.changelog/19100.txt new file mode 100644 index 00000000000..471cee52e86 --- /dev/null +++ b/.changelog/19100.txt @@ -0,0 +1,11 @@ +```release-note:new-resource +aws_schemas_discoverer +``` + +```release-note:new-resource +aws_schemas_registry +``` + +```release-note:new-resource +aws_schemas_schema +``` \ No newline at end of file diff --git a/.changelog/19404.txt b/.changelog/19404.txt index e1ea47bec81..d48647b4860 100644 --- a/.changelog/19404.txt +++ b/.changelog/19404.txt @@ -3,5 +3,9 @@ data-source/aws_msk_cluster: Add `bootstrap_brokers_sasl_iam` attribute ``` ```release-note:enhancement -resource/aws_msk_cluster: Add `bootstrap_brokers_sasl_iam` argument +resource/aws_msk_cluster: Add `bootstrap_brokers_sasl_iam` attribute +``` + +```release-note:enhancement +resource/aws_msk_cluster: Add `iam` argument to `client_authentication.sasl` configuration block ``` \ No newline at end of file diff --git a/.github/labeler-pr-triage.yml b/.github/labeler-pr-triage.yml index 2458add8c9c..a70cf67696a 100644 --- a/.github/labeler-pr-triage.yml +++ b/.github/labeler-pr-triage.yml @@ -686,6 +686,10 @@ service/sagemaker: - 'aws/internal/service/sagemaker/**/*' - '**/*_sagemaker_*' - '**/sagemaker_*' +service/schemas: + - 'aws/internal/service/schemas/**/*' + - '**/*_schemas_*' + - '**/schemas_*' service/secretsmanager: - 'aws/internal/service/secretsmanager/**/*' - '**/*_secretsmanager_*' diff --git a/.hashibot.hcl b/.hashibot.hcl index 62351a747fc..73046cefac0 100644 --- a/.hashibot.hcl +++ b/.hashibot.hcl @@ -471,6 +471,9 @@ behavior "regexp_issue_labeler_v2" "service_labels" { "service/sagemaker" = [ "aws_sagemaker_", ], + "service/schemas" = [ + "aws_schemas_", + ], "service/secretsmanager" = [ "aws_secretsmanager_", ], diff --git a/aws/config.go b/aws/config.go index 4b47c42ced4..8eef7bed53c 100644 --- a/aws/config.go +++ b/aws/config.go @@ -140,6 +140,7 @@ import ( "github.com/aws/aws-sdk-go/service/s3control" "github.com/aws/aws-sdk-go/service/s3outposts" "github.com/aws/aws-sdk-go/service/sagemaker" + "github.com/aws/aws-sdk-go/service/schemas" "github.com/aws/aws-sdk-go/service/secretsmanager" "github.com/aws/aws-sdk-go/service/securityhub" "github.com/aws/aws-sdk-go/service/serverlessapplicationrepository" @@ -354,6 +355,7 @@ type AWSClient struct { s3outpostsconn *s3outposts.S3Outposts sagemakerconn *sagemaker.SageMaker scconn *servicecatalog.ServiceCatalog + schemasconn *schemas.Schemas sdconn *servicediscovery.ServiceDiscovery secretsmanagerconn *secretsmanager.SecretsManager securityhubconn *securityhub.SecurityHub @@ -598,6 +600,7 @@ func (c *Config) Client() (interface{}, error) { s3outpostsconn: s3outposts.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["s3outposts"])})), sagemakerconn: sagemaker.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["sagemaker"])})), scconn: servicecatalog.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["servicecatalog"])})), + schemasconn: schemas.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["schemas"])})), sdconn: servicediscovery.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["servicediscovery"])})), secretsmanagerconn: secretsmanager.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["secretsmanager"])})), securityhubconn: securityhub.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["securityhub"])})), diff --git a/aws/internal/keyvaluetags/generators/listtags/main.go b/aws/internal/keyvaluetags/generators/listtags/main.go index f7e35a5cd60..393ba549ef2 100644 --- a/aws/internal/keyvaluetags/generators/listtags/main.go +++ b/aws/internal/keyvaluetags/generators/listtags/main.go @@ -108,6 +108,7 @@ var serviceNames = []string{ "sagemaker", "securityhub", "servicediscovery", + "schemas", "sfn", "shield", "signer", diff --git a/aws/internal/keyvaluetags/generators/servicetags/main.go b/aws/internal/keyvaluetags/generators/servicetags/main.go index 39c30ad7baa..8775ca80700 100644 --- a/aws/internal/keyvaluetags/generators/servicetags/main.go +++ b/aws/internal/keyvaluetags/generators/servicetags/main.go @@ -144,6 +144,7 @@ var mapServiceNames = []string{ "pinpoint", "resourcegroups", "securityhub", + "schemas", "signer", "sqs", "synthetics", diff --git a/aws/internal/keyvaluetags/generators/updatetags/main.go b/aws/internal/keyvaluetags/generators/updatetags/main.go index d330cbdf48e..c302a3b7da1 100644 --- a/aws/internal/keyvaluetags/generators/updatetags/main.go +++ b/aws/internal/keyvaluetags/generators/updatetags/main.go @@ -115,6 +115,7 @@ var serviceNames = []string{ "secretsmanager", "securityhub", "servicediscovery", + "schemas", "sfn", "shield", "signer", diff --git a/aws/internal/keyvaluetags/list_tags_gen.go b/aws/internal/keyvaluetags/list_tags_gen.go index 4b2688e7c3f..0b527c833fa 100644 --- a/aws/internal/keyvaluetags/list_tags_gen.go +++ b/aws/internal/keyvaluetags/list_tags_gen.go @@ -93,6 +93,7 @@ import ( "github.com/aws/aws-sdk-go/service/route53" "github.com/aws/aws-sdk-go/service/route53resolver" "github.com/aws/aws-sdk-go/service/sagemaker" + "github.com/aws/aws-sdk-go/service/schemas" "github.com/aws/aws-sdk-go/service/securityhub" "github.com/aws/aws-sdk-go/service/servicediscovery" "github.com/aws/aws-sdk-go/service/sfn" @@ -1638,6 +1639,23 @@ func SagemakerListTags(conn *sagemaker.SageMaker, identifier string) (KeyValueTa return SagemakerKeyValueTags(output.Tags), nil } +// SchemasListTags lists schemas service tags. +// The identifier is typically the Amazon Resource Name (ARN), although +// it may also be a different identifier depending on the service. +func SchemasListTags(conn *schemas.Schemas, identifier string) (KeyValueTags, error) { + input := &schemas.ListTagsForResourceInput{ + ResourceArn: aws.String(identifier), + } + + output, err := conn.ListTagsForResource(input) + + if err != nil { + return New(nil), err + } + + return SchemasKeyValueTags(output.Tags), nil +} + // SecurityhubListTags lists securityhub service tags. // The identifier is typically the Amazon Resource Name (ARN), although // it may also be a different identifier depending on the service. diff --git a/aws/internal/keyvaluetags/service_generation_customizations.go b/aws/internal/keyvaluetags/service_generation_customizations.go index 34819c2b764..f1f03359a5a 100644 --- a/aws/internal/keyvaluetags/service_generation_customizations.go +++ b/aws/internal/keyvaluetags/service_generation_customizations.go @@ -103,6 +103,7 @@ import ( "github.com/aws/aws-sdk-go/service/route53" "github.com/aws/aws-sdk-go/service/route53resolver" "github.com/aws/aws-sdk-go/service/sagemaker" + "github.com/aws/aws-sdk-go/service/schemas" "github.com/aws/aws-sdk-go/service/secretsmanager" "github.com/aws/aws-sdk-go/service/securityhub" "github.com/aws/aws-sdk-go/service/servicediscovery" @@ -333,6 +334,8 @@ func ServiceClientType(serviceName string) string { funcType = reflect.TypeOf(securityhub.New) case "servicediscovery": funcType = reflect.TypeOf(servicediscovery.New) + case "schemas": + funcType = reflect.TypeOf(schemas.New) case "sfn": funcType = reflect.TypeOf(sfn.New) case "shield": diff --git a/aws/internal/keyvaluetags/service_tags_gen.go b/aws/internal/keyvaluetags/service_tags_gen.go index 2abcf13632a..7a2dad640d0 100644 --- a/aws/internal/keyvaluetags/service_tags_gen.go +++ b/aws/internal/keyvaluetags/service_tags_gen.go @@ -447,6 +447,16 @@ func ResourcegroupsKeyValueTags(tags map[string]*string) KeyValueTags { return New(tags) } +// SchemasTags returns schemas service tags. +func (tags KeyValueTags) SchemasTags() map[string]*string { + return aws.StringMap(tags.Map()) +} + +// SchemasKeyValueTags creates KeyValueTags from schemas service tags. +func SchemasKeyValueTags(tags map[string]*string) KeyValueTags { + return New(tags) +} + // SecurityhubTags returns securityhub service tags. func (tags KeyValueTags) SecurityhubTags() map[string]*string { return aws.StringMap(tags.Map()) diff --git a/aws/internal/keyvaluetags/update_tags_gen.go b/aws/internal/keyvaluetags/update_tags_gen.go index 19f2c314012..6a2a9d37f19 100644 --- a/aws/internal/keyvaluetags/update_tags_gen.go +++ b/aws/internal/keyvaluetags/update_tags_gen.go @@ -101,6 +101,7 @@ import ( "github.com/aws/aws-sdk-go/service/route53" "github.com/aws/aws-sdk-go/service/route53resolver" "github.com/aws/aws-sdk-go/service/sagemaker" + "github.com/aws/aws-sdk-go/service/schemas" "github.com/aws/aws-sdk-go/service/secretsmanager" "github.com/aws/aws-sdk-go/service/securityhub" "github.com/aws/aws-sdk-go/service/servicediscovery" @@ -3545,6 +3546,42 @@ func SagemakerUpdateTags(conn *sagemaker.SageMaker, identifier string, oldTagsMa return nil } +// SchemasUpdateTags updates schemas service tags. +// The identifier is typically the Amazon Resource Name (ARN), although +// it may also be a different identifier depending on the service. +func SchemasUpdateTags(conn *schemas.Schemas, identifier string, oldTagsMap interface{}, newTagsMap interface{}) error { + oldTags := New(oldTagsMap) + newTags := New(newTagsMap) + + if removedTags := oldTags.Removed(newTags); len(removedTags) > 0 { + input := &schemas.UntagResourceInput{ + ResourceArn: aws.String(identifier), + TagKeys: aws.StringSlice(removedTags.IgnoreAws().Keys()), + } + + _, err := conn.UntagResource(input) + + if err != nil { + return fmt.Errorf("error untagging resource (%s): %w", identifier, err) + } + } + + if updatedTags := oldTags.Updated(newTags); len(updatedTags) > 0 { + input := &schemas.TagResourceInput{ + ResourceArn: aws.String(identifier), + Tags: updatedTags.IgnoreAws().SchemasTags(), + } + + _, err := conn.TagResource(input) + + if err != nil { + return fmt.Errorf("error tagging resource (%s): %w", identifier, err) + } + } + + return nil +} + // SecretsmanagerUpdateTags updates secretsmanager service tags. // The identifier is typically the Amazon Resource Name (ARN), although // it may also be a different identifier depending on the service. diff --git a/aws/internal/service/schemas/finder/finder.go b/aws/internal/service/schemas/finder/finder.go new file mode 100644 index 00000000000..c2511a294e7 --- /dev/null +++ b/aws/internal/service/schemas/finder/finder.go @@ -0,0 +1,93 @@ +package finder + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/schemas" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func DiscovererByID(conn *schemas.Schemas, id string) (*schemas.DescribeDiscovererOutput, error) { + input := &schemas.DescribeDiscovererInput{ + DiscovererId: aws.String(id), + } + + output, err := conn.DescribeDiscoverer(input) + + if tfawserr.ErrCodeEquals(err, schemas.ErrCodeNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, &resource.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + return output, nil +} + +func RegistryByName(conn *schemas.Schemas, name string) (*schemas.DescribeRegistryOutput, error) { + input := &schemas.DescribeRegistryInput{ + RegistryName: aws.String(name), + } + + output, err := conn.DescribeRegistry(input) + + if tfawserr.ErrCodeEquals(err, schemas.ErrCodeNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, &resource.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + return output, nil +} + +func SchemaByNameAndRegistryName(conn *schemas.Schemas, name, registryName string) (*schemas.DescribeSchemaOutput, error) { + input := &schemas.DescribeSchemaInput{ + RegistryName: aws.String(registryName), + SchemaName: aws.String(name), + } + + output, err := conn.DescribeSchema(input) + + if tfawserr.ErrCodeEquals(err, schemas.ErrCodeNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, &resource.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + return output, nil +} diff --git a/aws/internal/service/schemas/id.go b/aws/internal/service/schemas/id.go new file mode 100644 index 00000000000..71d0435c119 --- /dev/null +++ b/aws/internal/service/schemas/id.go @@ -0,0 +1,25 @@ +package transfer + +import ( + "fmt" + "strings" +) + +const schemaResourceIDSeparator = "/" + +func SchemaCreateResourceID(schemaName, registryName string) string { + parts := []string{schemaName, registryName} + id := strings.Join(parts, schemaResourceIDSeparator) + + return id +} + +func SchemaParseResourceID(id string) (string, string, error) { + parts := strings.Split(id, schemaResourceIDSeparator) + + if len(parts) == 2 && parts[0] != "" && parts[1] != "" { + return parts[0], parts[1], nil + } + + return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected SCHEMA_NAME%[2]sREGISTRY_NAME", id, schemaResourceIDSeparator) +} diff --git a/aws/provider.go b/aws/provider.go index 53fcd8be5e8..66c4a761428 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -981,6 +981,9 @@ func Provider() *schema.Provider { "aws_sagemaker_notebook_instance_lifecycle_configuration": resourceAwsSagemakerNotebookInstanceLifeCycleConfiguration(), "aws_sagemaker_notebook_instance": resourceAwsSagemakerNotebookInstance(), "aws_sagemaker_user_profile": resourceAwsSagemakerUserProfile(), + "aws_schemas_discoverer": resourceAwsSchemasDiscoverer(), + "aws_schemas_registry": resourceAwsSchemasRegistry(), + "aws_schemas_schema": resourceAwsSchemasSchema(), "aws_secretsmanager_secret": resourceAwsSecretsManagerSecret(), "aws_secretsmanager_secret_policy": resourceAwsSecretsManagerSecretPolicy(), "aws_secretsmanager_secret_version": resourceAwsSecretsManagerSecretVersion(), @@ -1387,6 +1390,7 @@ func init() { "s3control", "s3outposts", "sagemaker", + "schemas", "sdb", "secretsmanager", "securityhub", diff --git a/aws/resource_aws_cloudwatch_event_bus_test.go b/aws/resource_aws_cloudwatch_event_bus_test.go index a3731bfe7d2..6a64adca9ea 100644 --- a/aws/resource_aws_cloudwatch_event_bus_test.go +++ b/aws/resource_aws_cloudwatch_event_bus_test.go @@ -8,11 +8,13 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/cloudwatchevents" events "github.com/aws/aws-sdk-go/service/cloudwatchevents" + "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + tfevents "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/cloudwatchevents" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/cloudwatchevents/lister" ) func init() { @@ -22,6 +24,7 @@ func init() { Dependencies: []string{ "aws_cloudwatch_event_rule", "aws_cloudwatch_event_target", + "aws_schemas_discoverer", }, }) } @@ -32,46 +35,45 @@ func testSweepCloudWatchEventBuses(region string) error { return fmt.Errorf("Error getting client: %w", err) } conn := client.(*AWSClient).cloudwatcheventsconn - input := &events.ListEventBusesInput{} + var sweeperErrs *multierror.Error - for { - output, err := conn.ListEventBuses(input) - if err != nil { - if testSweepSkipSweepError(err) { - log.Printf("[WARN] Skipping CloudWatch Events event bus sweep for %s: %s", region, err) - return nil - } - return fmt.Errorf("Error retrieving CloudWatch Events event bus: %w", err) + err = lister.ListEventBusesPages(conn, input, func(page *events.ListEventBusesOutput, lastPage bool) bool { + if page == nil { + return !lastPage } - if len(output.EventBuses) == 0 { - log.Print("[DEBUG] No CloudWatch Events event buses to sweep") - return nil - } - - for _, eventBus := range output.EventBuses { + for _, eventBus := range page.EventBuses { name := aws.StringValue(eventBus.Name) - if name == "default" { + if name == tfevents.DefaultEventBusName { continue } - log.Printf("[INFO] Deleting CloudWatch Events event bus (%s)", name) - _, err := conn.DeleteEventBus(&events.DeleteEventBusInput{ - Name: aws.String(name), - }) + r := resourceAwsCloudWatchEventBus() + d := r.Data(nil) + d.SetId(name) + err = r.Delete(d, client) + if err != nil { - return fmt.Errorf("Error deleting CloudWatch Events event bus (%s): %w", name, err) + log.Printf("[ERROR] %s", err) + sweeperErrs = multierror.Append(sweeperErrs, err) + continue } } - if output.NextToken == nil { - break - } - input.NextToken = output.NextToken + return !lastPage + }) + + if testSweepSkipSweepError(err) { + log.Printf("[WARN] Skipping CloudWatch Events event bus sweep for %s: %s", region, err) + return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors } - return nil + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing CloudWatch Events event buses: %w", err)) + } + + return sweeperErrs.ErrorOrNil() } func TestAccAWSCloudWatchEventBus_basic(t *testing.T) { @@ -83,7 +85,7 @@ func TestAccAWSCloudWatchEventBus_basic(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, - ErrorCheck: testAccErrorCheck(t, cloudwatchevents.EndpointsID), + ErrorCheck: testAccErrorCheck(t, events.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAWSCloudWatchEventBusDestroy, Steps: []resource.TestStep{ @@ -134,7 +136,7 @@ func TestAccAWSCloudWatchEventBus_tags(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, - ErrorCheck: testAccErrorCheck(t, cloudwatchevents.EndpointsID), + ErrorCheck: testAccErrorCheck(t, events.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAWSCloudWatchEventBusDestroy, Steps: []resource.TestStep{ @@ -185,7 +187,7 @@ func TestAccAWSCloudWatchEventBus_tags(t *testing.T) { func TestAccAWSCloudWatchEventBus_default(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, - ErrorCheck: testAccErrorCheck(t, cloudwatchevents.EndpointsID), + ErrorCheck: testAccErrorCheck(t, events.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAWSCloudWatchEventBusDestroy, Steps: []resource.TestStep{ @@ -205,7 +207,7 @@ func TestAccAWSCloudWatchEventBus_disappears(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, - ErrorCheck: testAccErrorCheck(t, cloudwatchevents.EndpointsID), + ErrorCheck: testAccErrorCheck(t, events.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAWSCloudWatchEventBusDestroy, Steps: []resource.TestStep{ @@ -233,7 +235,7 @@ func TestAccAWSCloudWatchEventBus_PartnerEventSource(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, - ErrorCheck: testAccErrorCheck(t, cloudwatchevents.EndpointsID), + ErrorCheck: testAccErrorCheck(t, events.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAWSCloudWatchEventBusDestroy, Steps: []resource.TestStep{ diff --git a/aws/resource_aws_schemas_discoverer.go b/aws/resource_aws_schemas_discoverer.go new file mode 100644 index 00000000000..dfb2f786127 --- /dev/null +++ b/aws/resource_aws_schemas_discoverer.go @@ -0,0 +1,168 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/schemas" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/schemas/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" +) + +func resourceAwsSchemasDiscoverer() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsSchemasDiscovererCreate, + Read: resourceAwsSchemasDiscovererRead, + Update: resourceAwsSchemasDiscovererUpdate, + Delete: resourceAwsSchemasDiscovererDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + + "description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(0, 256), + }, + + "source_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArn, + }, + + "tags": tagsSchema(), + "tags_all": tagsSchemaComputed(), + }, + + CustomizeDiff: SetTagsDiff, + } +} + +func resourceAwsSchemasDiscovererCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).schemasconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + tags := defaultTagsConfig.MergeTags(keyvaluetags.New(d.Get("tags").(map[string]interface{}))) + + sourceARN := d.Get("source_arn").(string) + input := &schemas.CreateDiscovererInput{ + SourceArn: aws.String(sourceARN), + } + + if v, ok := d.GetOk("description"); ok { + input.Description = aws.String(v.(string)) + } + + if len(tags) > 0 { + input.Tags = tags.IgnoreAws().SchemasTags() + } + + log.Printf("[DEBUG] Creating EventBridge Schemas Discoverer: %s", input) + output, err := conn.CreateDiscoverer(input) + + if err != nil { + return fmt.Errorf("error creating EventBridge Schemas Discoverer (%s): %w", sourceARN, err) + } + + d.SetId(aws.StringValue(output.DiscovererId)) + + return resourceAwsSchemasDiscovererRead(d, meta) +} + +func resourceAwsSchemasDiscovererRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).schemasconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + output, err := finder.DiscovererByID(conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] EventBridge Schemas Discoverer (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading EventBridge Schemas Discoverer (%s): %w", d.Id(), err) + } + + d.Set("arn", output.DiscovererArn) + d.Set("description", output.Description) + d.Set("source_arn", output.SourceArn) + + tags, err := keyvaluetags.SchemasListTags(conn, d.Get("arn").(string)) + + if err != nil { + return fmt.Errorf("error listing tags for EventBridge Schemas Discoverer (%s): %w", d.Id(), err) + } + + tags = tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig) + + if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %w", err) + } + + if err := d.Set("tags_all", tags.Map()); err != nil { + return fmt.Errorf("error setting tags_all: %w", err) + } + + return nil +} + +func resourceAwsSchemasDiscovererUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).schemasconn + + if d.HasChange("description") { + input := &schemas.UpdateDiscovererInput{ + DiscovererId: aws.String(d.Id()), + Description: aws.String(d.Get("description").(string)), + } + + log.Printf("[DEBUG] Updating EventBridge Schemas Discoverer: %s", input) + _, err := conn.UpdateDiscoverer(input) + + if err != nil { + return fmt.Errorf("error updating EventBridge Schemas Discoverer (%s): %w", d.Id(), err) + } + } + + if d.HasChange("tags_all") { + o, n := d.GetChange("tags_all") + if err := keyvaluetags.SchemasUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating tags: %w", err) + } + } + + return resourceAwsSchemasDiscovererRead(d, meta) +} + +func resourceAwsSchemasDiscovererDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).schemasconn + + log.Printf("[INFO] Deleting EventBridge Schemas Discoverer (%s)", d.Id()) + _, err := conn.DeleteDiscoverer(&schemas.DeleteDiscovererInput{ + DiscovererId: aws.String(d.Id()), + }) + + if tfawserr.ErrCodeEquals(err, schemas.ErrCodeNotFoundException) { + return nil + } + + if err != nil { + return fmt.Errorf("error deleting EventBridge Schemas Discoverer (%s): %w", d.Id(), err) + } + + return nil +} diff --git a/aws/resource_aws_schemas_discoverer_test.go b/aws/resource_aws_schemas_discoverer_test.go new file mode 100644 index 00000000000..82d271d6b02 --- /dev/null +++ b/aws/resource_aws_schemas_discoverer_test.go @@ -0,0 +1,311 @@ +package aws + +import ( + "fmt" + "log" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/schemas" + "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/schemas/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" +) + +func init() { + resource.AddTestSweepers("aws_schemas_discoverer", &resource.Sweeper{ + Name: "aws_schemas_discoverer", + F: testSweepSchemasDiscoverers, + }) +} + +func testSweepSchemasDiscoverers(region string) error { + client, err := sharedClientForRegion(region) + if err != nil { + return fmt.Errorf("Error getting client: %w", err) + } + conn := client.(*AWSClient).schemasconn + input := &schemas.ListDiscoverersInput{} + var sweeperErrs *multierror.Error + + err = conn.ListDiscoverersPages(input, func(page *schemas.ListDiscoverersOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, discoverer := range page.Discoverers { + r := resourceAwsSchemasDiscoverer() + d := r.Data(nil) + d.SetId(aws.StringValue(discoverer.DiscovererId)) + err = r.Delete(d, client) + + if err != nil { + log.Printf("[ERROR] %s", err) + sweeperErrs = multierror.Append(sweeperErrs, err) + continue + } + } + + return !lastPage + }) + + if testSweepSkipSweepError(err) { + log.Printf("[WARN] Skipping EventBridge Schemas Discoverer sweep for %s: %s", region, err) + return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors + } + + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing EventBridge Schemas Discoverers: %w", err)) + } + + return sweeperErrs.ErrorOrNil() +} + +func TestAccAWSSchemasDiscoverer_basic(t *testing.T) { + var v schemas.DescribeDiscovererOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_schemas_discoverer.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(schemas.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, schemas.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSchemasDiscovererDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSchemasDiscovererConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasDiscovererExists(resourceName, &v), + testAccCheckResourceAttrRegionalARN(resourceName, "arn", "schemas", fmt.Sprintf("discoverer/events-event-bus-%s", rName)), + resource.TestCheckResourceAttr(resourceName, "description", ""), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSSchemasDiscoverer_disappears(t *testing.T) { + var v schemas.DescribeDiscovererOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_schemas_discoverer.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(schemas.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, schemas.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSchemasDiscovererDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSchemasDiscovererConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasDiscovererExists(resourceName, &v), + testAccCheckResourceDisappears(testAccProvider, resourceAwsSchemasDiscoverer(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSSchemasDiscoverer_Description(t *testing.T) { + var v schemas.DescribeDiscovererOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_schemas_discoverer.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(schemas.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, schemas.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSchemasDiscovererDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSchemasDiscovererConfigDescription(rName, "description1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasDiscovererExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "description", "description1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSSchemasDiscovererConfigDescription(rName, "description2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasDiscovererExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "description", "description2"), + ), + }, + { + Config: testAccAWSSchemasDiscovererConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasDiscovererExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "description", ""), + ), + }, + }, + }) +} + +func TestAccAWSSchemasDiscoverer_Tags(t *testing.T) { + var v schemas.DescribeDiscovererOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_schemas_discoverer.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(schemas.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, schemas.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSchemasDiscovererDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSchemasDiscovererConfigTags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasDiscovererExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSSchemasDiscovererConfigTags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasDiscovererExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAWSSchemasDiscovererConfigTags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasDiscovererExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +func testAccCheckAWSSchemasDiscovererDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).schemasconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_schemas_discoverer" { + continue + } + + _, err := finder.DiscovererByID(conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err + } + + return fmt.Errorf("EventBridge Schemas Discoverer %s still exists", rs.Primary.ID) + } + + return nil +} + +func testAccCheckSchemasDiscovererExists(n string, v *schemas.DescribeDiscovererOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No EventBridge Schemas Discoverer ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).schemasconn + + output, err := finder.DiscovererByID(conn, rs.Primary.ID) + + if err != nil { + return err + } + + *v = *output + + return nil + } +} + +func testAccAWSSchemasDiscovererConfig(rName string) string { + return fmt.Sprintf(` +resource "aws_cloudwatch_event_bus" "test" { + name = %[1]q +} + +resource "aws_schemas_discoverer" "test" { + source_arn = aws_cloudwatch_event_bus.test.arn +} +`, rName) +} + +func testAccAWSSchemasDiscovererConfigDescription(rName, description string) string { + return fmt.Sprintf(` +resource "aws_cloudwatch_event_bus" "test" { + name = %[1]q +} + +resource "aws_schemas_discoverer" "test" { + source_arn = aws_cloudwatch_event_bus.test.arn + + description = %[2]q +} +`, rName, description) +} + +func testAccAWSSchemasDiscovererConfigTags1(rName, tagKey1, tagValue1 string) string { + return fmt.Sprintf(` +resource "aws_cloudwatch_event_bus" "test" { + name = %[1]q +} + +resource "aws_schemas_discoverer" "test" { + source_arn = aws_cloudwatch_event_bus.test.arn + + tags = { + %[2]q = %[3]q + } +} +`, rName, tagKey1, tagValue1) +} + +func testAccAWSSchemasDiscovererConfigTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return fmt.Sprintf(` +resource "aws_cloudwatch_event_bus" "test" { + name = %[1]q +} + +resource "aws_schemas_discoverer" "test" { + source_arn = aws_cloudwatch_event_bus.test.arn + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +} diff --git a/aws/resource_aws_schemas_registry.go b/aws/resource_aws_schemas_registry.go new file mode 100644 index 00000000000..c14839e3854 --- /dev/null +++ b/aws/resource_aws_schemas_registry.go @@ -0,0 +1,172 @@ +package aws + +import ( + "fmt" + "log" + "regexp" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/schemas" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/schemas/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" +) + +func resourceAwsSchemasRegistry() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsSchemasRegistryCreate, + Read: resourceAwsSchemasRegistryRead, + Update: resourceAwsSchemasRegistryUpdate, + Delete: resourceAwsSchemasRegistryDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + + "description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(0, 256), + }, + + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 64), + validation.StringMatch(regexp.MustCompile(`^[\.\-_A-Za-z0-9]+`), ""), + ), + }, + + "tags": tagsSchema(), + "tags_all": tagsSchemaComputed(), + }, + + CustomizeDiff: SetTagsDiff, + } +} + +func resourceAwsSchemasRegistryCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).schemasconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + tags := defaultTagsConfig.MergeTags(keyvaluetags.New(d.Get("tags").(map[string]interface{}))) + + name := d.Get("name").(string) + input := &schemas.CreateRegistryInput{ + RegistryName: aws.String(name), + } + + if v, ok := d.GetOk("description"); ok { + input.Description = aws.String(v.(string)) + } + + if len(tags) > 0 { + input.Tags = tags.IgnoreAws().SchemasTags() + } + + log.Printf("[DEBUG] Creating EventBridge Schemas Registry: %s", input) + _, err := conn.CreateRegistry(input) + + if err != nil { + return fmt.Errorf("error creating EventBridge Schemas Registry (%s): %w", name, err) + } + + d.SetId(aws.StringValue(input.RegistryName)) + + return resourceAwsSchemasRegistryRead(d, meta) +} + +func resourceAwsSchemasRegistryRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).schemasconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + output, err := finder.RegistryByName(conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] EventBridge Schemas Registry (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading EventBridge Schemas Registry (%s): %w", d.Id(), err) + } + + d.Set("arn", output.RegistryArn) + d.Set("description", output.Description) + d.Set("name", output.RegistryName) + + tags, err := keyvaluetags.SchemasListTags(conn, d.Get("arn").(string)) + + tags = tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig) + + if err != nil { + return fmt.Errorf("error listing tags for EventBridge Schemas Registry (%s): %w", d.Id(), err) + } + + if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %w", err) + } + + if err := d.Set("tags_all", tags.Map()); err != nil { + return fmt.Errorf("error setting tags_all: %w", err) + } + + return nil +} + +func resourceAwsSchemasRegistryUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).schemasconn + + if d.HasChanges("description") { + input := &schemas.UpdateRegistryInput{ + Description: aws.String(d.Get("description").(string)), + RegistryName: aws.String(d.Id()), + } + + log.Printf("[DEBUG] Updating EventBridge Schemas Registry: %s", input) + _, err := conn.UpdateRegistry(input) + + if err != nil { + return fmt.Errorf("error updating EventBridge Schemas Registry (%s): %w", d.Id(), err) + } + } + + if d.HasChange("tags_all") { + o, n := d.GetChange("tags_all") + if err := keyvaluetags.SchemasUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating tags: %w", err) + } + } + + return resourceAwsSchemasRegistryRead(d, meta) +} + +func resourceAwsSchemasRegistryDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).schemasconn + + log.Printf("[INFO] Deleting EventBridge Schemas Registry (%s)", d.Id()) + _, err := conn.DeleteRegistry(&schemas.DeleteRegistryInput{ + RegistryName: aws.String(d.Id()), + }) + + if tfawserr.ErrCodeEquals(err, schemas.ErrCodeNotFoundException) { + return nil + } + + if err != nil { + return fmt.Errorf("error deleting EventBridge Schemas Registry (%s): %w", d.Id(), err) + } + + return nil +} diff --git a/aws/resource_aws_schemas_registry_test.go b/aws/resource_aws_schemas_registry_test.go new file mode 100644 index 00000000000..f53d09a2885 --- /dev/null +++ b/aws/resource_aws_schemas_registry_test.go @@ -0,0 +1,337 @@ +package aws + +import ( + "fmt" + "log" + "strings" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/schemas" + "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + tfschemas "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/schemas" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/schemas/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" +) + +func init() { + resource.AddTestSweepers("aws_schemas_registry", &resource.Sweeper{ + Name: "aws_schemas_registry", + F: testSweepSchemasRegistries, + }) +} + +func testSweepSchemasRegistries(region string) error { + client, err := sharedClientForRegion(region) + if err != nil { + return fmt.Errorf("Error getting client: %w", err) + } + conn := client.(*AWSClient).schemasconn + input := &schemas.ListRegistriesInput{} + var sweeperErrs *multierror.Error + + err = conn.ListRegistriesPages(input, func(page *schemas.ListRegistriesOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, registry := range page.Registries { + registryName := aws.StringValue(registry.RegistryName) + + input := &schemas.ListSchemasInput{ + RegistryName: aws.String(registryName), + } + + err = conn.ListSchemasPages(input, func(page *schemas.ListSchemasOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, schema := range page.Schemas { + schemaName := aws.StringValue(schema.SchemaName) + if strings.HasPrefix(schemaName, "aws.") { + continue + } + + r := resourceAwsSchemasSchema() + d := r.Data(nil) + d.SetId(tfschemas.SchemaCreateResourceID(schemaName, registryName)) + err = r.Delete(d, client) + + if err != nil { + log.Printf("[ERROR] %s", err) + sweeperErrs = multierror.Append(sweeperErrs, err) + continue + } + } + + return !lastPage + }) + + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing EventBridge Schemas Schemas: %w", err)) + } + + if strings.HasPrefix(registryName, "aws.") { + continue + } + + r := resourceAwsSchemasRegistry() + d := r.Data(nil) + d.SetId(registryName) + err = r.Delete(d, client) + + if err != nil { + log.Printf("[ERROR] %s", err) + sweeperErrs = multierror.Append(sweeperErrs, err) + continue + } + } + + return !lastPage + }) + + if testSweepSkipSweepError(err) { + log.Printf("[WARN] Skipping EventBridge Schemas Registry sweep for %s: %s", region, err) + return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors + } + + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing EventBridge Schemas Registries: %w", err)) + } + + return sweeperErrs.ErrorOrNil() +} + +func TestAccAWSSchemasRegistry_basic(t *testing.T) { + var v schemas.DescribeRegistryOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_schemas_registry.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(schemas.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, schemas.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSchemasRegistryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSchemasRegistryConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasRegistryExists(resourceName, &v), + testAccCheckResourceAttrRegionalARN(resourceName, "arn", "schemas", fmt.Sprintf("registry/%s", rName)), + resource.TestCheckResourceAttr(resourceName, "description", ""), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSSchemasRegistry_disappears(t *testing.T) { + var v schemas.DescribeRegistryOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_schemas_registry.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(schemas.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, schemas.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSchemasRegistryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSchemasRegistryConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasRegistryExists(resourceName, &v), + testAccCheckResourceDisappears(testAccProvider, resourceAwsSchemasRegistry(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSSchemasRegistry_Description(t *testing.T) { + var v schemas.DescribeRegistryOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_schemas_registry.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(schemas.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, schemas.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSchemasRegistryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSchemasRegistryConfigDescription(rName, "description1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasRegistryExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "description", "description1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSSchemasRegistryConfigDescription(rName, "description2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasRegistryExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "description", "description2"), + ), + }, + { + Config: testAccAWSSchemasRegistryConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasRegistryExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "description", ""), + ), + }, + }, + }) +} + +func TestAccAWSSchemasRegistry_Tags(t *testing.T) { + var v schemas.DescribeRegistryOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_schemas_registry.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(schemas.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, schemas.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSchemasRegistryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSchemasRegistryConfigTags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasRegistryExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSSchemasRegistryConfigTags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasRegistryExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAWSSchemasRegistryConfigTags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasRegistryExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +func testAccCheckAWSSchemasRegistryDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).schemasconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_schemas_registry" { + continue + } + + _, err := finder.RegistryByName(conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err + } + + return fmt.Errorf("EventBridge Schemas Registry %s still exists", rs.Primary.ID) + } + + return nil +} + +func testAccCheckSchemasRegistryExists(n string, v *schemas.DescribeRegistryOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No EventBridge Schemas Registry ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).schemasconn + + output, err := finder.RegistryByName(conn, rs.Primary.ID) + + if err != nil { + return err + } + + *v = *output + + return nil + } +} + +func testAccAWSSchemasRegistryConfig(rName string) string { + return fmt.Sprintf(` +resource "aws_schemas_registry" "test" { + name = %[1]q +} +`, rName) +} + +func testAccAWSSchemasRegistryConfigDescription(rName, description string) string { + return fmt.Sprintf(` +resource "aws_schemas_registry" "test" { + name = %[1]q + description = %[2]q +} +`, rName, description) +} + +func testAccAWSSchemasRegistryConfigTags1(rName, tagKey1, tagValue1 string) string { + return fmt.Sprintf(` +resource "aws_schemas_registry" "test" { + name = %[1]q + + tags = { + %[2]q = %[3]q + } +} +`, rName, tagKey1, tagValue1) +} + +func testAccAWSSchemasRegistryConfigTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return fmt.Sprintf(` +resource "aws_schemas_registry" "test" { + name = %[1]q + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +} diff --git a/aws/resource_aws_schemas_schema.go b/aws/resource_aws_schemas_schema.go new file mode 100644 index 00000000000..ac1b657dbf8 --- /dev/null +++ b/aws/resource_aws_schemas_schema.go @@ -0,0 +1,255 @@ +package aws + +import ( + "fmt" + "log" + "regexp" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/schemas" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" + tfschemas "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/schemas" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/schemas/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" +) + +func resourceAwsSchemasSchema() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsSchemasSchemaCreate, + Read: resourceAwsSchemasSchemaRead, + Update: resourceAwsSchemasSchemaUpdate, + Delete: resourceAwsSchemasSchemaDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + + "content": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: suppressEquivalentJsonDiffs, + }, + + "description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(0, 256), + }, + + "last_modified": { + Type: schema.TypeString, + Computed: true, + }, + + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 385), + validation.StringMatch(regexp.MustCompile(`^[\.\-_A-Za-z@]+`), ""), + ), + }, + + "registry_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(schemas.Type_Values(), true), + }, + + "version": { + Type: schema.TypeString, + Computed: true, + }, + + "version_created_date": { + Type: schema.TypeString, + Computed: true, + }, + + "tags": tagsSchema(), + "tags_all": tagsSchemaComputed(), + }, + + CustomizeDiff: SetTagsDiff, + } +} + +func resourceAwsSchemasSchemaCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).schemasconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + tags := defaultTagsConfig.MergeTags(keyvaluetags.New(d.Get("tags").(map[string]interface{}))) + + name := d.Get("name").(string) + registryName := d.Get("registry_name").(string) + input := &schemas.CreateSchemaInput{ + Content: aws.String(d.Get("content").(string)), + RegistryName: aws.String(registryName), + SchemaName: aws.String(name), + Type: aws.String(d.Get("type").(string)), + } + + if v, ok := d.GetOk("description"); ok { + input.Description = aws.String(v.(string)) + } + + if len(tags) > 0 { + input.Tags = tags.IgnoreAws().SchemasTags() + } + + id := tfschemas.SchemaCreateResourceID(name, registryName) + + log.Printf("[DEBUG] Creating EventBridge Schemas Schema: %s", input) + _, err := conn.CreateSchema(input) + + if err != nil { + return fmt.Errorf("error creating EventBridge Schemas Schema (%s): %w", id, err) + } + + d.SetId(id) + + return resourceAwsSchemasSchemaRead(d, meta) +} + +func resourceAwsSchemasSchemaRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).schemasconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + name, registryName, err := tfschemas.SchemaParseResourceID(d.Id()) + + if err != nil { + return fmt.Errorf("error parsing EventBridge Schemas Schema ID: %w", err) + } + + output, err := finder.SchemaByNameAndRegistryName(conn, name, registryName) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] EventBridge Schemas Schema (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading EventBridge Schemas Schema (%s): %w", d.Id(), err) + } + + d.Set("arn", output.SchemaArn) + d.Set("content", output.Content) + d.Set("description", output.Description) + if output.LastModified != nil { + d.Set("last_modified", aws.TimeValue(output.LastModified).Format(time.RFC3339)) + } else { + d.Set("last_modified", nil) + } + d.Set("name", output.SchemaName) + d.Set("registry_name", registryName) + d.Set("type", output.Type) + d.Set("version", output.SchemaVersion) + if output.VersionCreatedDate != nil { + d.Set("version_created_date", aws.TimeValue(output.VersionCreatedDate).Format(time.RFC3339)) + } else { + d.Set("version_created_date", nil) + } + + tags, err := keyvaluetags.SchemasListTags(conn, d.Get("arn").(string)) + + if err != nil { + return fmt.Errorf("error listing tags for EventBridge Schemas Schema (%s): %w", d.Id(), err) + } + + tags = tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig) + + if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %w", err) + } + + if err := d.Set("tags_all", tags.Map()); err != nil { + return fmt.Errorf("error setting tags_all: %w", err) + } + + return nil +} + +func resourceAwsSchemasSchemaUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).schemasconn + + if d.HasChanges("content", "description", "type") { + name, registryName, err := tfschemas.SchemaParseResourceID(d.Id()) + + if err != nil { + return fmt.Errorf("error parsing EventBridge Schemas Schema ID: %w", err) + } + + input := &schemas.UpdateSchemaInput{ + RegistryName: aws.String(registryName), + SchemaName: aws.String(name), + } + + if d.HasChanges("content", "type") { + input.Content = aws.String(d.Get("content").(string)) + input.Type = aws.String(d.Get("type").(string)) + } + + if d.HasChange("description") { + input.Description = aws.String(d.Get("description").(string)) + } + + log.Printf("[DEBUG] Updating EventBridge Schemas Schema: %s", input) + _, err = conn.UpdateSchema(input) + + if err != nil { + return fmt.Errorf("error updating EventBridge Schemas Schema (%s): %w", d.Id(), err) + } + } + + if d.HasChange("tags_all") { + o, n := d.GetChange("tags_all") + if err := keyvaluetags.SchemasUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating tags: %w", err) + } + } + + return resourceAwsSchemasSchemaRead(d, meta) +} + +func resourceAwsSchemasSchemaDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).schemasconn + + name, registryName, err := tfschemas.SchemaParseResourceID(d.Id()) + + if err != nil { + return fmt.Errorf("error parsing EventBridge Schemas Schema ID: %w", err) + } + + log.Printf("[INFO] Deleting EventBridge Schemas Schema (%s)", d.Id()) + _, err = conn.DeleteSchema(&schemas.DeleteSchemaInput{ + RegistryName: aws.String(registryName), + SchemaName: aws.String(name), + }) + + if tfawserr.ErrCodeEquals(err, schemas.ErrCodeNotFoundException) { + return nil + } + + if err != nil { + return fmt.Errorf("error deleting EventBridge Schemas Schema (%s): %w", d.Id(), err) + } + + return nil +} diff --git a/aws/resource_aws_schemas_schema_test.go b/aws/resource_aws_schemas_schema_test.go new file mode 100644 index 00000000000..21d5155c993 --- /dev/null +++ b/aws/resource_aws_schemas_schema_test.go @@ -0,0 +1,347 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/service/schemas" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + tfschemas "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/schemas" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/schemas/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" +) + +const ( + testAccAWSSchemasSchemaContent = ` +{ + "openapi": "3.0.0", + "info": { + "version": "1.0.0", + "title": "Event" + }, + "paths": {}, + "components": { + "schemas": { + "Event": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + } + } + } +} +` + + testAccAWSSchemasSchemaContentUpdated = ` +{ + "openapi": "3.0.0", + "info": { + "version": "2.0.0", + "title": "Event" + }, + "paths": {}, + "components": { + "schemas": { + "Event": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "created_at": { + "type": "string", + "format": "date-time" + } + } + } + } + } +} +` +) + +func TestAccAWSSchemasSchema_basic(t *testing.T) { + var v schemas.DescribeSchemaOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_schemas_schema.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(schemas.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, schemas.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSchemasSchemaDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSchemasSchemaConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasSchemaExists(resourceName, &v), + testAccCheckResourceAttrRegionalARN(resourceName, "arn", "schemas", fmt.Sprintf("schema/%s/%s", rName, rName)), + resource.TestCheckResourceAttrSet(resourceName, "content"), + resource.TestCheckResourceAttr(resourceName, "description", ""), + resource.TestCheckResourceAttrSet(resourceName, "last_modified"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "registry_name", rName), + resource.TestCheckResourceAttr(resourceName, "type", "OpenApi3"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "version", "1"), + resource.TestCheckResourceAttrSet(resourceName, "version_created_date"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSSchemasSchema_disappears(t *testing.T) { + var v schemas.DescribeSchemaOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_schemas_schema.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(schemas.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, schemas.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSchemasSchemaDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSchemasSchemaConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasSchemaExists(resourceName, &v), + testAccCheckResourceDisappears(testAccProvider, resourceAwsSchemasSchema(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSSchemasSchema_ContentDescription(t *testing.T) { + var v schemas.DescribeSchemaOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_schemas_schema.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(schemas.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, schemas.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSchemasSchemaDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSchemasSchemaConfigContentDescription(rName, testAccAWSSchemasSchemaContent, "description1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasSchemaExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "content", testAccAWSSchemasSchemaContent), + resource.TestCheckResourceAttr(resourceName, "description", "description1"), + resource.TestCheckResourceAttr(resourceName, "version", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSSchemasSchemaConfigContentDescription(rName, testAccAWSSchemasSchemaContentUpdated, "description2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasSchemaExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "content", testAccAWSSchemasSchemaContentUpdated), + resource.TestCheckResourceAttr(resourceName, "description", "description2"), + resource.TestCheckResourceAttr(resourceName, "version", "2"), + ), + }, + { + Config: testAccAWSSchemasSchemaConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasSchemaExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "description", ""), + resource.TestCheckResourceAttr(resourceName, "version", "3"), + ), + }, + }, + }) +} + +func TestAccAWSSchemasSchema_Tags(t *testing.T) { + var v schemas.DescribeSchemaOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_schemas_schema.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(schemas.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, schemas.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSchemasSchemaDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSchemasSchemaConfigTags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasSchemaExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSSchemasSchemaConfigTags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasSchemaExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAWSSchemasSchemaConfigTags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckSchemasSchemaExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +func testAccCheckAWSSchemasSchemaDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).schemasconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_schemas_schema" { + continue + } + + name, registryName, err := tfschemas.SchemaParseResourceID(rs.Primary.ID) + + if err != nil { + return err + } + + _, err = finder.SchemaByNameAndRegistryName(conn, name, registryName) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err + } + + return fmt.Errorf("EventBridge Schemas Schema %s still exists", rs.Primary.ID) + } + + return nil +} + +func testAccCheckSchemasSchemaExists(n string, v *schemas.DescribeSchemaOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No EventBridge Schemas Schema ID is set") + } + + name, registryName, err := tfschemas.SchemaParseResourceID(rs.Primary.ID) + + if err != nil { + return err + } + + conn := testAccProvider.Meta().(*AWSClient).schemasconn + + output, err := finder.SchemaByNameAndRegistryName(conn, name, registryName) + + if err != nil { + return err + } + + *v = *output + + return nil + } +} + +func testAccAWSSchemasSchemaConfig(rName string) string { + return fmt.Sprintf(` +resource "aws_schemas_registry" "test" { + name = %[1]q +} + +resource "aws_schemas_schema" "test" { + name = %[1]q + registry_name = aws_schemas_registry.test.name + type = "OpenApi3" + content = %[2]q +} +`, rName, testAccAWSSchemasSchemaContent) +} + +func testAccAWSSchemasSchemaConfigContentDescription(rName, content, description string) string { + return fmt.Sprintf(` +resource "aws_schemas_registry" "test" { + name = %[1]q +} + +resource "aws_schemas_schema" "test" { + name = %[1]q + registry_name = aws_schemas_registry.test.name + type = "OpenApi3" + content = %[2]q + description = %[3]q +} +`, rName, content, description) +} + +func testAccAWSSchemasSchemaConfigTags1(rName, tagKey1, tagValue1 string) string { + return fmt.Sprintf(` +resource "aws_schemas_registry" "test" { + name = %[1]q +} + +resource "aws_schemas_schema" "test" { + name = %[1]q + registry_name = aws_schemas_registry.test.name + type = "OpenApi3" + content = %[2]q + + tags = { + %[3]q = %[4]q + } +} +`, rName, testAccAWSSchemasSchemaContent, tagKey1, tagValue1) +} + +func testAccAWSSchemasSchemaConfigTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return fmt.Sprintf(` +resource "aws_schemas_registry" "test" { + name = %[1]q +} + +resource "aws_schemas_schema" "test" { + name = %[1]q + registry_name = aws_schemas_registry.test.name + type = "OpenApi3" + content = %[2]q + + tags = { + %[3]q = %[4]q + %[5]q = %[6]q + } +} +`, rName, testAccAWSSchemasSchemaContent, tagKey1, tagValue1, tagKey2, tagValue2) +} diff --git a/infrastructure/repository/labels-service.tf b/infrastructure/repository/labels-service.tf index 753b05e32cc..4f27b2b8ff1 100644 --- a/infrastructure/repository/labels-service.tf +++ b/infrastructure/repository/labels-service.tf @@ -172,6 +172,7 @@ variable "service_labels" { "s3outposts", "sagemaker", "savingsplans", + "schemas", "secretsmanager", "securityhub", "serverlessapplicationrepository", diff --git a/website/allowed-subcategories.txt b/website/allowed-subcategories.txt index c1584e01a03..79d5df82be0 100644 --- a/website/allowed-subcategories.txt +++ b/website/allowed-subcategories.txt @@ -62,6 +62,7 @@ Elastic Map Reduce Containers Elastic Transcoder ElasticSearch EventBridge (CloudWatch Events) +EventBridge Schemas File System (FSx) Firewall Manager (FMS) Gamelift diff --git a/website/docs/guides/custom-service-endpoints.html.md b/website/docs/guides/custom-service-endpoints.html.md index e23f99ffa08..edad3b4262e 100644 --- a/website/docs/guides/custom-service-endpoints.html.md +++ b/website/docs/guides/custom-service-endpoints.html.md @@ -184,6 +184,7 @@ The Terraform AWS Provider allows the following endpoints to be customized:
  • s3control
  • s3outposts
  • sagemaker
  • +
  • schemas
  • sdb
  • secretsmanager
  • securityhub
  • diff --git a/website/docs/r/schemas_discoverer.html.markdown b/website/docs/r/schemas_discoverer.html.markdown new file mode 100644 index 00000000000..2889b34ca60 --- /dev/null +++ b/website/docs/r/schemas_discoverer.html.markdown @@ -0,0 +1,51 @@ +--- +subcategory: "EventBridge Schemas" +layout: "aws" +page_title: "AWS: aws_schemas_discoverer" +description: |- + Provides an EventBridge Schema Discoverer resource. +--- + +# Resource: aws_schemas_discoverer + +Provides an EventBridge Schema Discoverer resource. + +~> **Note:** EventBridge was formerly known as CloudWatch Events. The functionality is identical. + + +## Example Usage + +```terraform +resource "aws_cloudwatch_event_bus" "messenger" { + name = "chat-messages" +} + +resource "aws_schemas_discoverer" "test" { + source_arn = aws_cloudwatch_event_bus.messenger.arn + description = "Auto discover event schemas" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `source_arn` - (Required) The ARN of the event bus to discover event schemas on. +* `description` - (Optional) The description of the discoverer. Maximum of 256 characters. +* `tags` - (Optional) A map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](https://www.terraform.io/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `arn` - The Amazon Resource Name (ARN) of the discoverer. +* `id` - The ID of the discoverer. +* `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://www.terraform.io/docs/providers/aws/index.html#default_tags-configuration-block). + +## Import + +EventBridge discoverers can be imported using the `id`, e.g. + +```console +$ terraform import aws_schemas_discoverer.test 123 +``` diff --git a/website/docs/r/schemas_registry.html.markdown b/website/docs/r/schemas_registry.html.markdown new file mode 100644 index 00000000000..d554f9b9b7c --- /dev/null +++ b/website/docs/r/schemas_registry.html.markdown @@ -0,0 +1,45 @@ +--- +subcategory: "EventBridge Schemas" +layout: "aws" +page_title: "AWS: aws_schemas_registry" +description: |- + Provides an EventBridge Custom Schema Registry resource. +--- + +# Resource: aws_schemas_registry + +Provides an EventBridge Custom Schema Registry resource. + +~> **Note:** EventBridge was formerly known as CloudWatch Events. The functionality is identical. + +## Example Usage + +```terraform +resource "aws_schemas_registry" "test" { + name = "my_own_registry" + description = "A custom schema registry" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the custom event schema registry. Maximum of 64 characters consisting of lower case letters, upper case letters, 0-9, ., -, _. +* `description` - (Optional) The description of the discoverer. Maximum of 256 characters. +* `tags` - (Optional) A map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](https://www.terraform.io/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `arn` - The Amazon Resource Name (ARN) of the discoverer. +* `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://www.terraform.io/docs/providers/aws/index.html#default_tags-configuration-block). + +## Import + +EventBridge schema registries can be imported using the `name`, e.g. + +```console +$ terraform import aws_schemas_registry.test my_own_registry +``` diff --git a/website/docs/r/schemas_schema.html.markdown b/website/docs/r/schemas_schema.html.markdown new file mode 100644 index 00000000000..96ed3eb9b8f --- /dev/null +++ b/website/docs/r/schemas_schema.html.markdown @@ -0,0 +1,78 @@ +--- +subcategory: "EventBridge Schemas" +layout: "aws" +page_title: "AWS: aws_schemas_schema" +description: |- + Provides an EventBridge Schema resource. +--- + +# Resource: aws_schemas_schema + +Provides an EventBridge Schema resource. + +~> **Note:** EventBridge was formerly known as CloudWatch Events. The functionality is identical. + +## Example Usage + +```terraform +resource "aws_schemas_registry" "test" { + name = "my_own_registry" +} + +resource "aws_schemas_schema" "test" { + name = "my_schema" + registry_name = aws_schemas_registry.test.name + type = "OpenApi3" + description = "The schema definition for my event" + + content = jsonencode({ + "openapi" : "3.0.0", + "info" : { + "version" : "1.0.0", + "title" : "Event" + }, + "paths" : {}, + "components" : { + "schemas" : { + "Event" : { + "type" : "object", + "properties" : { + "name" : { + "type" : "string" + } + } + } + } + } + }) +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the schema. Maximum of 385 characters consisting of lower case letters, upper case letters, ., -, _, @. +* `content` - (Required) The schema specification. Must be a valid Open API 3.0 spec. +* `registry_name` - (Required) The name of the registry in which this schema belongs. +* `type` - (Required) The type of the schema. Valid values: `OpenApi3`. +* `description` - (Optional) The description of the schema. Maximum of 256 characters. +* `tags` - (Optional) A map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](https://www.terraform.io/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `arn` - The Amazon Resource Name (ARN) of the discoverer. +* `last_modified` - The last modified date of the schema. +* `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://www.terraform.io/docs/providers/aws/index.html#default_tags-configuration-block). +* `version` - The version of the schema. +* `version_created_date` - The created date of the version of the schema. + +## Import + +EventBridge schema can be imported using the `name` and `registry_name`, e.g. + +```console +$ terraform import aws_schemas_schema.test name/registry +```