diff --git a/aws/resource_aws_glue_workflow.go b/aws/resource_aws_glue_workflow.go index 2f9caa78bc5..89c390c158d 100644 --- a/aws/resource_aws_glue_workflow.go +++ b/aws/resource_aws_glue_workflow.go @@ -2,11 +2,14 @@ package aws import ( "fmt" + "log" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/glue" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - "log" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" ) func resourceAwsGlueWorkflow() *schema.Resource { @@ -20,6 +23,10 @@ func resourceAwsGlueWorkflow() *schema.Resource { }, Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, "default_run_properties": { Type: schema.TypeMap, Optional: true, @@ -29,12 +36,17 @@ func resourceAwsGlueWorkflow() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "max_concurrent_runs": { + Type: schema.TypeInt, + Optional: true, + }, "name": { Type: schema.TypeString, Optional: true, ForceNew: true, - ValidateFunc: validation.NoZeroValues, + ValidateFunc: validation.StringLenBetween(1, 255), }, + "tags": tagsSchema(), }, } } @@ -45,24 +57,25 @@ func resourceAwsGlueWorkflowCreate(d *schema.ResourceData, meta interface{}) err input := &glue.CreateWorkflowInput{ Name: aws.String(name), + Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().GlueTags(), } if kv, ok := d.GetOk("default_run_properties"); ok { - defaultRunPropertiesMap := make(map[string]string) - for k, v := range kv.(map[string]interface{}) { - defaultRunPropertiesMap[k] = v.(string) - } - input.DefaultRunProperties = aws.StringMap(defaultRunPropertiesMap) + input.DefaultRunProperties = stringMapToPointers(kv.(map[string]interface{})) } if v, ok := d.GetOk("description"); ok { input.Description = aws.String(v.(string)) } + if v, ok := d.GetOk("max_concurrent_runs"); ok { + input.MaxConcurrentRuns = aws.Int64(int64(v.(int))) + } + log.Printf("[DEBUG] Creating Glue Workflow: %s", input) _, err := conn.CreateWorkflow(input) if err != nil { - return fmt.Errorf("error creating Glue Trigger (%s): %s", name, err) + return fmt.Errorf("error creating Glue Trigger (%s): %w", name, err) } d.SetId(name) @@ -71,12 +84,13 @@ func resourceAwsGlueWorkflowCreate(d *schema.ResourceData, meta interface{}) err func resourceAwsGlueWorkflowRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).glueconn + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig input := &glue.GetWorkflowInput{ Name: aws.String(d.Id()), } - log.Printf("[DEBUG] Reading Glue Workflow: %s", input) + log.Printf("[DEBUG] Reading Glue Workflow: %#v", input) output, err := conn.GetWorkflow(input) if err != nil { if isAWSErr(err, glue.ErrCodeEntityNotFoundException, "") { @@ -84,7 +98,7 @@ func resourceAwsGlueWorkflowRead(d *schema.ResourceData, meta interface{}) error d.SetId("") return nil } - return fmt.Errorf("error reading Glue Workflow (%s): %s", d.Id(), err) + return fmt.Errorf("error reading Glue Workflow (%s): %w", d.Id(), err) } workflow := output.Workflow @@ -94,38 +108,66 @@ func resourceAwsGlueWorkflowRead(d *schema.ResourceData, meta interface{}) error return nil } + workFlowArn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Service: "glue", + Region: meta.(*AWSClient).region, + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("workflow/%s", d.Id()), + }.String() + d.Set("arn", workFlowArn) + if err := d.Set("default_run_properties", aws.StringValueMap(workflow.DefaultRunProperties)); err != nil { - return fmt.Errorf("error setting default_run_properties: %s", err) + return fmt.Errorf("error setting default_run_properties: %w", err) } d.Set("description", workflow.Description) d.Set("name", workflow.Name) + tags, err := keyvaluetags.GlueListTags(conn, workFlowArn) + + if err != nil { + return fmt.Errorf("error listing tags for Glue Workflow (%s): %w", workFlowArn, err) + } + + if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %w", err) + } + return nil } func resourceAwsGlueWorkflowUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).glueconn - input := &glue.UpdateWorkflowInput{ - Name: aws.String(d.Get("name").(string)), - } + if d.HasChanges("default_run_properties", "description", "max_concurrent_runs") { + input := &glue.UpdateWorkflowInput{ + Name: aws.String(d.Get("name").(string)), + } - if kv, ok := d.GetOk("default_run_properties"); ok { - defaultRunPropertiesMap := make(map[string]string) - for k, v := range kv.(map[string]interface{}) { - defaultRunPropertiesMap[k] = v.(string) + if kv, ok := d.GetOk("default_run_properties"); ok { + input.DefaultRunProperties = stringMapToPointers(kv.(map[string]interface{})) } - input.DefaultRunProperties = aws.StringMap(defaultRunPropertiesMap) - } - if v, ok := d.GetOk("description"); ok { - input.Description = aws.String(v.(string)) + if v, ok := d.GetOk("description"); ok { + input.Description = aws.String(v.(string)) + } + + if v, ok := d.GetOk("max_concurrent_runs"); ok { + input.MaxConcurrentRuns = aws.Int64(int64(v.(int))) + } + + log.Printf("[DEBUG] Updating Glue Workflow: %#v", input) + _, err := conn.UpdateWorkflow(input) + if err != nil { + return fmt.Errorf("error updating Glue Workflow (%s): %w", d.Id(), err) + } } - log.Printf("[DEBUG] Updating Glue Workflow: %s", input) - _, err := conn.UpdateWorkflow(input) - if err != nil { - return fmt.Errorf("error updating Glue Workflow (%s): %s", d.Id(), err) + if d.HasChange("tags") { + o, n := d.GetChange("tags") + if err := keyvaluetags.GlueUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating tags: %s", err) + } } return resourceAwsGlueWorkflowRead(d, meta) @@ -137,7 +179,7 @@ func resourceAwsGlueWorkflowDelete(d *schema.ResourceData, meta interface{}) err log.Printf("[DEBUG] Deleting Glue Workflow: %s", d.Id()) err := deleteWorkflow(conn, d.Id()) if err != nil { - return fmt.Errorf("error deleting Glue Workflow (%s): %s", d.Id(), err) + return fmt.Errorf("error deleting Glue Workflow (%s): %w", d.Id(), err) } return nil diff --git a/aws/resource_aws_glue_workflow_test.go b/aws/resource_aws_glue_workflow_test.go index 49d784e219e..12bdd1fcfd6 100644 --- a/aws/resource_aws_glue_workflow_test.go +++ b/aws/resource_aws_glue_workflow_test.go @@ -47,7 +47,7 @@ func testSweepGlueWorkflow(region string) error { func TestAccAWSGlueWorkflow_basic(t *testing.T) { var workflow glue.Workflow - rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_glue_workflow.test" resource.ParallelTest(t, resource.TestCase{ @@ -59,7 +59,9 @@ func TestAccAWSGlueWorkflow_basic(t *testing.T) { Config: testAccAWSGlueWorkflowConfig_Required(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSGlueWorkflowExists(resourceName, &workflow), + testAccCheckResourceAttrRegionalARN(resourceName, "arn", "glue", fmt.Sprintf("workflow/%s", rName)), resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, { @@ -71,10 +73,51 @@ func TestAccAWSGlueWorkflow_basic(t *testing.T) { }) } +func TestAccAWSGlueWorkflow_maxConcurrentRuns(t *testing.T) { + var workflow glue.Workflow + + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_glue_workflow.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSGlueWorkflow(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSGlueWorkflowDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSGlueWorkflowConfigMaxConcurrentRuns(rName, 1), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSGlueWorkflowExists(resourceName, &workflow), + resource.TestCheckResourceAttr(resourceName, "max_concurrent_runs", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSGlueWorkflowConfigMaxConcurrentRuns(rName, 2), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSGlueWorkflowExists(resourceName, &workflow), + resource.TestCheckResourceAttr(resourceName, "max_concurrent_runs", "2"), + ), + }, + { + Config: testAccAWSGlueWorkflowConfigMaxConcurrentRuns(rName, 1), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSGlueWorkflowExists(resourceName, &workflow), + resource.TestCheckResourceAttr(resourceName, "max_concurrent_runs", "1"), + ), + }, + }, + }) +} + func TestAccAWSGlueWorkflow_DefaultRunProperties(t *testing.T) { var workflow glue.Workflow - rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_glue_workflow.test" resource.ParallelTest(t, resource.TestCase{ @@ -103,7 +146,7 @@ func TestAccAWSGlueWorkflow_DefaultRunProperties(t *testing.T) { func TestAccAWSGlueWorkflow_Description(t *testing.T) { var workflow glue.Workflow - rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_glue_workflow.test" resource.ParallelTest(t, resource.TestCase{ @@ -134,6 +177,73 @@ func TestAccAWSGlueWorkflow_Description(t *testing.T) { }) } +func TestAccAWSGlueWorkflow_Tags(t *testing.T) { + var workflow glue.Workflow + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_glue_workflow.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSGlueWorkflow(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSGlueWorkflowDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSGlueWorkflowConfigTags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSGlueWorkflowExists(resourceName, &workflow), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSGlueWorkflowConfigTags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSGlueWorkflowExists(resourceName, &workflow), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAWSGlueWorkflowConfigTags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSGlueWorkflowExists(resourceName, &workflow), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +func TestAccAWSGlueWorkflow_disappears(t *testing.T) { + var workflow glue.Workflow + + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_glue_workflow.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSGlueWorkflow(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSGlueWorkflowDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSGlueWorkflowConfig_Required(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSGlueWorkflowExists(resourceName, &workflow), + testAccCheckResourceDisappears(testAccProvider, resourceAwsGlueWorkflow(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func testAccPreCheckAWSGlueWorkflow(t *testing.T) { conn := testAccProvider.Meta().(*AWSClient).glueconn @@ -241,3 +351,37 @@ resource "aws_glue_workflow" "test" { } `, rName) } + +func testAccAWSGlueWorkflowConfigMaxConcurrentRuns(rName string, runs int) string { + return fmt.Sprintf(` +resource "aws_glue_workflow" "test" { + name = %[1]q + max_concurrent_runs = %[2]d +} +`, rName, runs) +} + +func testAccAWSGlueWorkflowConfigTags1(rName, tagKey1, tagValue1 string) string { + return fmt.Sprintf(` +resource "aws_glue_workflow" "test" { + name = %[1]q + + tags = { + %[2]q = %[3]q + } +} +`, rName, tagKey1, tagValue1) +} + +func testAccAWSGlueWorkflowConfigTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return fmt.Sprintf(` +resource "aws_glue_workflow" "test" { + name = %[1]q + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +} diff --git a/website/docs/index.html.markdown b/website/docs/index.html.markdown index 878e1f1ab26..d00867f1b48 100644 --- a/website/docs/index.html.markdown +++ b/website/docs/index.html.markdown @@ -291,6 +291,7 @@ for more information about connecting to alternate AWS endpoints or AWS compatib - [`aws_glue_job` resource](/docs/providers/aws/r/glue_job.html) - [`aws_glue_trigger` resource](/docs/providers/aws/r/glue_trigger.html) - [`aws_glue_user_defined_function` resource](/docs/providers/aws/r/glue_user_defined_function.html) + - [`aws_glue_workflow` resource](/docs/providers/aws/r/glue_workflow.html) - [`aws_guardduty_detector` resource](/docs/providers/aws/r/guardduty_detector.html) - [`aws_guardduty_ipset` resource](/docs/providers/aws/r/guardduty_ipset.html) - [`aws_guardduty_threatintelset` resource](/docs/providers/aws/r/guardduty_threatintelset.html) diff --git a/website/docs/r/glue_workflow.html.markdown b/website/docs/r/glue_workflow.html.markdown index 30a139e5f9a..9be2664fb67 100644 --- a/website/docs/r/glue_workflow.html.markdown +++ b/website/docs/r/glue_workflow.html.markdown @@ -3,7 +3,7 @@ subcategory: "Glue" layout: "aws" page_title: "AWS: aws_glue_workflow" description: |- - Provides an Glue Workflow resource. + Provides a Glue Workflow resource. --- # Resource: aws_glue_workflow @@ -51,14 +51,17 @@ resource "aws_glue_trigger" "example-inner" { The following arguments are supported: +* `name` – (Required) The name you assign to this workflow. * `default_run_properties` – (Optional) A map of default run properties for this workflow. These properties are passed to all jobs associated to the workflow. * `description` – (Optional) Description of the workflow. -* `name` – (Required) The name you assign to this workflow. +* `max_concurrent_runs` - (Optional) Prevents exceeding the maximum number of concurrent runs of any of the component jobs. If you leave this parameter blank, there is no limit to the number of concurrent workflow runs. +* `tags` - (Optional) Key-value map of resource tags ## Attributes Reference In addition to all arguments above, the following attributes are exported: +* `arn` - Amazon Resource Name (ARN) of Glue Workflow * `id` - Workflow name ## Import