From ef534522c87870ca12a5e0e8efb0c4ade0bcd39f Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Tue, 4 Jan 2022 08:35:25 +0200 Subject: [PATCH 1/5] add codepipe validations --- internal/service/codepipeline/codepipeline.go | 61 +++++++++++++++---- 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/internal/service/codepipeline/codepipeline.go b/internal/service/codepipeline/codepipeline.go index d2de9462a8f..90e798abbe6 100644 --- a/internal/service/codepipeline/codepipeline.go +++ b/internal/service/codepipeline/codepipeline.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "log" + "regexp" "strings" "github.com/aws/aws-sdk-go/aws" @@ -50,11 +51,16 @@ func ResourceCodePipeline() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 100), + validation.StringMatch(regexp.MustCompile(`[A-Za-z0-9.@\-_]+`), ""), + ), }, "role_arn": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: verify.ValidARN, }, "artifact_store": { Type: schema.TypeSet, @@ -105,6 +111,10 @@ func ResourceCodePipeline() *schema.Resource { "name": { Type: schema.TypeString, Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 100), + validation.StringMatch(regexp.MustCompile(`[A-Za-z0-9.@\-_]+`), ""), + ), }, "action": { Type: schema.TypeList, @@ -112,8 +122,12 @@ func ResourceCodePipeline() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "configuration": { - Type: schema.TypeMap, - Optional: true, + Type: schema.TypeMap, + Optional: true, + ValidateDiagFunc: allDiagFunc( + validation.MapKeyLenBetween(1, 50), + validation.MapKeyLenBetween(1, 1000), + ), Elem: &schema.Schema{Type: schema.TypeString}, DiffSuppressFunc: suppressCodePipelineStageActionConfiguration, }, @@ -135,6 +149,10 @@ func ResourceCodePipeline() *schema.Resource { "version": { Type: schema.TypeString, Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 9), + validation.StringMatch(regexp.MustCompile(`[0-9A-Za-z_-]+`), ""), + ), }, "input_artifacts": { Type: schema.TypeList, @@ -149,15 +167,21 @@ func ResourceCodePipeline() *schema.Resource { "name": { Type: schema.TypeString, Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 100), + validation.StringMatch(regexp.MustCompile(`[A-Za-z0-9.@\-_]+`), ""), + ), }, "role_arn": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: verify.ValidARN, }, "run_order": { - Type: schema.TypeInt, - Optional: true, - Computed: true, + Type: schema.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.IntBetween(1, 999), }, "region": { Type: schema.TypeString, @@ -167,6 +191,10 @@ func ResourceCodePipeline() *schema.Resource { "namespace": { Type: schema.TypeString, Optional: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 100), + validation.StringMatch(regexp.MustCompile(`[A-Za-z0-9@\-_]+`), ""), + ), }, }, }, @@ -234,7 +262,7 @@ func expand(d *schema.ResourceData) (*codepipeline.PipelineDeclaration, error) { Stages: expandStages(d), } - pipelineArtifactStores, err := ExpandArtifactStores(d.Get("artifact_store").(*schema.Set).List()) + pipelineArtifactStores, err := expandArtifactStores(d.Get("artifact_store").(*schema.Set).List()) if err != nil { return nil, err } @@ -249,7 +277,7 @@ func expand(d *schema.ResourceData) (*codepipeline.PipelineDeclaration, error) { return &pipeline, nil } -func ExpandArtifactStores(configs []interface{}) (map[string]*codepipeline.ArtifactStore, error) { +func expandArtifactStores(configs []interface{}) (map[string]*codepipeline.ArtifactStore, error) { if len(configs) == 0 { return nil, nil } @@ -651,3 +679,14 @@ func hashCodePipelineGitHubToken(token string) string { sum := sha256.Sum256([]byte(token)) return codePipelineGitHubTokenHashPrefix + hex.EncodeToString(sum[:]) } + +// https://github.com/hashicorp/terraform-plugin-sdk/issues/780. +func allDiagFunc(validators ...schema.SchemaValidateDiagFunc) schema.SchemaValidateDiagFunc { + return func(i interface{}, k cty.Path) diag.Diagnostics { + var diags diag.Diagnostics + for _, validator := range validators { + diags = append(diags, validator(i, k)...) + } + return diags + } +} From 7581da7e1a5f23d6d81ecc7f9cfbc3ed9cc4b1ab Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Tue, 4 Jan 2022 08:36:51 +0200 Subject: [PATCH 2/5] update when no change to tags --- internal/service/codepipeline/codepipeline.go | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/internal/service/codepipeline/codepipeline.go b/internal/service/codepipeline/codepipeline.go index 90e798abbe6..206a92f26d7 100644 --- a/internal/service/codepipeline/codepipeline.go +++ b/internal/service/codepipeline/codepipeline.go @@ -593,17 +593,19 @@ func resourceCodePipelineRead(d *schema.ResourceData, meta interface{}) error { func resourceCodePipelineUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).CodePipelineConn - pipeline, err := expand(d) - if err != nil { - return err - } - params := &codepipeline.UpdatePipelineInput{ - Pipeline: pipeline, - } - _, err = conn.UpdatePipeline(params) + if d.HasChangesExcept("tags", "tags_all") { + pipeline, err := expand(d) + if err != nil { + return err + } + params := &codepipeline.UpdatePipelineInput{ + Pipeline: pipeline, + } + _, err = conn.UpdatePipeline(params) - if err != nil { - return fmt.Errorf("[ERROR] Error updating CodePipeline (%s): %w", d.Id(), err) + if err != nil { + return fmt.Errorf("[ERROR] Error updating CodePipeline (%s): %w", d.Id(), err) + } } arn := d.Get("arn").(string) From 5e167ce2629ae2036f93d5d0de23a73d784957a9 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Tue, 4 Jan 2022 22:09:53 +0200 Subject: [PATCH 3/5] validations --- internal/service/codepipeline/webhook.go | 285 ++++++++++-------- internal/service/codepipeline/webhook_test.go | 92 +++++- 2 files changed, 246 insertions(+), 131 deletions(-) diff --git a/internal/service/codepipeline/webhook.go b/internal/service/codepipeline/webhook.go index b91c07a5db3..bba4bd7a366 100644 --- a/internal/service/codepipeline/webhook.go +++ b/internal/service/codepipeline/webhook.go @@ -3,6 +3,7 @@ package codepipeline import ( "fmt" "log" + "regexp" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/codepipeline" @@ -26,15 +27,15 @@ func ResourceWebhook() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "authentication": { + "arn": { Type: schema.TypeString, - ForceNew: true, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - codepipeline.WebhookAuthenticationTypeGithubHmac, - codepipeline.WebhookAuthenticationTypeIp, - codepipeline.WebhookAuthenticationTypeUnauthenticated, - }, false), + Computed: true, + }, + "authentication": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + ValidateFunc: validation.StringInSlice(codepipeline.WebhookAuthenticationType_Values(), false), }, "authentication_configuration": { Type: schema.TypeList, @@ -45,10 +46,11 @@ func ResourceWebhook() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "secret_token": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Sensitive: true, + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Sensitive: true, + ValidateFunc: validation.StringLenBetween(1, 100), }, "allowed_ip_range": { Type: schema.TypeString, @@ -61,19 +63,21 @@ func ResourceWebhook() *schema.Resource { }, "filter": { Type: schema.TypeSet, - ForceNew: true, Required: true, MinItems: 1, + MaxItems: 5, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "json_path": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 150), }, "match_equals": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 150), }, }, }, @@ -82,13 +86,15 @@ func ResourceWebhook() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 100), + validation.StringMatch(regexp.MustCompile(`[A-Za-z0-9.@\-_]+`), ""), + ), }, - "url": { Type: schema.TypeString, Computed: true, }, - "target_action": { Type: schema.TypeString, ForceNew: true, @@ -107,34 +113,6 @@ func ResourceWebhook() *schema.Resource { } } -func extractCodePipelineWebhookRules(filters *schema.Set) []*codepipeline.WebhookFilterRule { - var rules []*codepipeline.WebhookFilterRule - - for _, f := range filters.List() { - r := f.(map[string]interface{}) - filter := codepipeline.WebhookFilterRule{ - JsonPath: aws.String(r["json_path"].(string)), - MatchEquals: aws.String(r["match_equals"].(string)), - } - - rules = append(rules, &filter) - } - - return rules -} - -func extractCodePipelineWebhookAuthConfig(authType string, authConfig map[string]interface{}) *codepipeline.WebhookAuthConfiguration { - var conf codepipeline.WebhookAuthConfiguration - switch authType { - case codepipeline.WebhookAuthenticationTypeIp: - conf.AllowedIPRange = aws.String(authConfig["allowed_ip_range"].(string)) - case codepipeline.WebhookAuthenticationTypeGithubHmac: - conf.SecretToken = aws.String(authConfig["secret_token"].(string)) - } - - return &conf -} - func resourceWebhookCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).CodePipelineConn defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig @@ -142,9 +120,8 @@ func resourceWebhookCreate(d *schema.ResourceData, meta interface{}) error { authType := d.Get("authentication").(string) var authConfig map[string]interface{} - if v, ok := d.GetOk("authentication_configuration"); ok { - l := v.([]interface{}) - authConfig = l[0].(map[string]interface{}) + if v, ok := d.GetOk("authentication_configuration"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + authConfig = v.([]interface{})[0].(map[string]interface{}) } request := &codepipeline.PutWebhookInput{ @@ -161,7 +138,7 @@ func resourceWebhookCreate(d *schema.ResourceData, meta interface{}) error { webhook, err := conn.PutWebhook(request) if err != nil { - return fmt.Errorf("Error creating webhook: %s", err) + return fmt.Errorf("Error creating webhook: %w", err) } d.SetId(aws.StringValue(webhook.Webhook.Arn)) @@ -169,6 +146,115 @@ func resourceWebhookCreate(d *schema.ResourceData, meta interface{}) error { return resourceWebhookRead(d, meta) } +func resourceWebhookRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).CodePipelineConn + defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig + ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig + + arn := d.Id() + webhook, err := GetWebhook(conn, arn) + + if tfresource.NotFound(err) { + log.Printf("[WARN] CodePipeline Webhook (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error getting CodePipeline Webhook (%s): %s", d.Id(), err) + } + + webhookDef := webhook.Definition + + name := aws.StringValue(webhookDef.Name) + if name == "" { + return fmt.Errorf("Webhook not found: %s", arn) + } + + d.Set("name", name) + d.Set("url", webhook.Url) + d.Set("target_action", webhookDef.TargetAction) + d.Set("target_pipeline", webhookDef.TargetPipeline) + d.Set("authentication", webhookDef.Authentication) + d.Set("arn", webhook.Arn) + + if err := d.Set("authentication_configuration", flattenCodePipelineWebhookAuthenticationConfiguration(webhookDef.AuthenticationConfiguration)); err != nil { + return fmt.Errorf("error setting authentication_configuration: %w", err) + } + + if err := d.Set("filter", flattenCodePipelineWebhookFilters(webhookDef.Filters)); err != nil { + return fmt.Errorf("error setting filter: %w", err) + } + + tags := KeyValueTags(webhook.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig) + + //lintignore:AWSR002 + 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 resourceWebhookUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).CodePipelineConn + + if d.HasChangesExcept("tags_all", "tags", "register_with_third_party") { + authType := d.Get("authentication").(string) + + var authConfig map[string]interface{} + if v, ok := d.GetOk("authentication_configuration"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + authConfig = v.([]interface{})[0].(map[string]interface{}) + } + + request := &codepipeline.PutWebhookInput{ + Webhook: &codepipeline.WebhookDefinition{ + Authentication: aws.String(authType), + Filters: extractCodePipelineWebhookRules(d.Get("filter").(*schema.Set)), + Name: aws.String(d.Get("name").(string)), + TargetAction: aws.String(d.Get("target_action").(string)), + TargetPipeline: aws.String(d.Get("target_pipeline").(string)), + AuthenticationConfiguration: extractCodePipelineWebhookAuthConfig(authType, authConfig), + }, + } + + _, err := conn.PutWebhook(request) + if err != nil { + return fmt.Errorf("Error updating webhook: %w", err) + } + } + + if d.HasChange("tags_all") { + o, n := d.GetChange("tags_all") + + if err := UpdateTags(conn, d.Id(), o, n); err != nil { + return fmt.Errorf("error updating CodePipeline Webhook (%s) tags: %w", d.Id(), err) + } + } + + return resourceWebhookRead(d, meta) +} + +func resourceWebhookDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).CodePipelineConn + name := d.Get("name").(string) + + input := codepipeline.DeleteWebhookInput{ + Name: &name, + } + _, err := conn.DeleteWebhook(&input) + + if err != nil { + return fmt.Errorf("Could not delete webhook: %w", err) + } + + return nil +} + func GetWebhook(conn *codepipeline.CodePipeline, arn string) (*codepipeline.ListWebhookItem, error) { var nextToken string @@ -234,92 +320,31 @@ func flattenCodePipelineWebhookAuthenticationConfiguration(authConfig *codepipel return results } -func resourceWebhookRead(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*conns.AWSClient).CodePipelineConn - defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig - ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - - arn := d.Id() - webhook, err := GetWebhook(conn, arn) - - if tfresource.NotFound(err) { - log.Printf("[WARN] CodePipeline Webhook (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - - if err != nil { - return fmt.Errorf("error getting CodePipeline Webhook (%s): %s", d.Id(), err) - } - - name := aws.StringValue(webhook.Definition.Name) - if name == "" { - return fmt.Errorf("Webhook not found: %s", arn) - } - - d.Set("name", name) - d.Set("url", webhook.Url) - - if err := d.Set("target_action", webhook.Definition.TargetAction); err != nil { - return err - } - - if err := d.Set("target_pipeline", webhook.Definition.TargetPipeline); err != nil { - return err - } - - if err := d.Set("authentication", webhook.Definition.Authentication); err != nil { - return err - } - - if err := d.Set("authentication_configuration", flattenCodePipelineWebhookAuthenticationConfiguration(webhook.Definition.AuthenticationConfiguration)); err != nil { - return fmt.Errorf("error setting authentication_configuration: %s", err) - } - - if err := d.Set("filter", flattenCodePipelineWebhookFilters(webhook.Definition.Filters)); err != nil { - return fmt.Errorf("error setting filter: %s", err) - } - - tags := KeyValueTags(webhook.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig) - - //lintignore:AWSR002 - 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 resourceWebhookUpdate(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*conns.AWSClient).CodePipelineConn - - if d.HasChange("tags_all") { - o, n := d.GetChange("tags_all") +func extractCodePipelineWebhookRules(filters *schema.Set) []*codepipeline.WebhookFilterRule { + var rules []*codepipeline.WebhookFilterRule - if err := UpdateTags(conn, d.Id(), o, n); err != nil { - return fmt.Errorf("error updating CodePipeline Webhook (%s) tags: %s", d.Id(), err) + for _, f := range filters.List() { + r := f.(map[string]interface{}) + filter := codepipeline.WebhookFilterRule{ + JsonPath: aws.String(r["json_path"].(string)), + MatchEquals: aws.String(r["match_equals"].(string)), } + + rules = append(rules, &filter) } - return resourceWebhookRead(d, meta) + return rules } -func resourceWebhookDelete(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*conns.AWSClient).CodePipelineConn - name := d.Get("name").(string) - - input := codepipeline.DeleteWebhookInput{ - Name: &name, - } - _, err := conn.DeleteWebhook(&input) +func extractCodePipelineWebhookAuthConfig(authType string, authConfig map[string]interface{}) *codepipeline.WebhookAuthConfiguration { + var conf codepipeline.WebhookAuthConfiguration - if err != nil { - return fmt.Errorf("Could not delete webhook: %s", err) + switch authType { + case codepipeline.WebhookAuthenticationTypeIp: + conf.AllowedIPRange = aws.String(authConfig["allowed_ip_range"].(string)) + case codepipeline.WebhookAuthenticationTypeGithubHmac: + conf.SecretToken = aws.String(authConfig["secret_token"].(string)) } - return nil + return &conf } diff --git a/internal/service/codepipeline/webhook_test.go b/internal/service/codepipeline/webhook_test.go index ce3e5fb6f7d..ebcd8ba1fce 100644 --- a/internal/service/codepipeline/webhook_test.go +++ b/internal/service/codepipeline/webhook_test.go @@ -2,6 +2,7 @@ package codepipeline_test import ( "fmt" + "regexp" "testing" "github.com/aws/aws-sdk-go/aws" @@ -36,10 +37,19 @@ func TestAccCodePipelineWebhook_basic(t *testing.T) { Config: testAccWebhookConfig_basic(rName, githubToken), Check: resource.ComposeTestCheckFunc( testAccCheckWebhookExists(resourceName, &v), - resource.TestCheckResourceAttrSet(resourceName, "id"), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "codepipeline", regexp.MustCompile(fmt.Sprintf("webhook:%s", rName))), + resource.TestCheckResourceAttr(resourceName, "authentication", "GITHUB_HMAC"), + resource.TestCheckResourceAttr(resourceName, "target_action", "Source"), + resource.TestCheckResourceAttrPair(resourceName, "target_pipeline", "aws_codepipeline.test", "name"), + resource.TestCheckResourceAttr(resourceName, "filter.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filter.*", map[string]string{ + "json_path": "$.ref", + "match_equals": "refs/head/{Branch}", + }), resource.TestCheckResourceAttrSet(resourceName, "url"), resource.TestCheckResourceAttr(resourceName, "authentication_configuration.#", "1"), resource.TestCheckResourceAttr(resourceName, "authentication_configuration.0.secret_token", "super-secret"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, { @@ -47,6 +57,32 @@ func TestAccCodePipelineWebhook_basic(t *testing.T) { ImportState: true, ImportStateVerify: true, }, + { + Config: testAccWebhookConfig_filters(rName, githubToken), + Check: resource.ComposeTestCheckFunc( + testAccCheckWebhookExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "filter.#", "2"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filter.*", map[string]string{ + "json_path": "$.ref", + "match_equals": "refs/head/{Branch}", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filter.*", map[string]string{ + "json_path": "$.head_commit.modified", + "match_equals": "^.*mypath.*$", + }), + ), + }, + { + Config: testAccWebhookConfig_basic(rName, githubToken), + Check: resource.ComposeTestCheckFunc( + testAccCheckWebhookExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "filter.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filter.*", map[string]string{ + "json_path": "$.ref", + "match_equals": "refs/head/{Branch}", + }), + ), + }, }, }) } @@ -183,6 +219,35 @@ func TestAccCodePipelineWebhook_tags(t *testing.T) { }) } +func TestAccCodePipelineWebhook_disappears(t *testing.T) { + githubToken := conns.SkipIfEnvVarEmpty(t, conns.EnvVarGithubToken, envVarGithubTokenUsageCodePipelineWebhook) + + var v codepipeline.ListWebhookItem + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_codepipeline_webhook.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(t) + testAccPreCheckSupported(t) + }, + ErrorCheck: acctest.ErrorCheck(t, codepipeline.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckDestroy, + Steps: []resource.TestStep{ + { + Config: testAccWebhookConfig_basic(rName, githubToken), + Check: resource.ComposeTestCheckFunc( + testAccCheckWebhookExists(resourceName, &v), + acctest.CheckResourceDisappears(acctest.Provider, tfcodepipeline.ResourceWebhook(), resourceName), + acctest.CheckResourceDisappears(acctest.Provider, tfcodepipeline.ResourceWebhook(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func TestAccCodePipelineWebhook_UpdateAuthentication_secretToken(t *testing.T) { githubToken := conns.SkipIfEnvVarEmpty(t, conns.EnvVarGithubToken, envVarGithubTokenUsageCodePipelineWebhook) @@ -274,6 +339,31 @@ resource "aws_codepipeline_webhook" "test" { `, rName) } +func testAccWebhookConfig_filters(rName, githubToken string) string { + return testAccWebhookConfig_codePipeline(rName, githubToken) + fmt.Sprintf(` +resource "aws_codepipeline_webhook" "test" { + name = %[1]q + authentication = "GITHUB_HMAC" + target_action = "Source" + target_pipeline = aws_codepipeline.test.name + + authentication_configuration { + secret_token = "super-secret" + } + + filter { + json_path = "$.ref" + match_equals = "refs/head/{Branch}" + } + + filter { + json_path = "$.head_commit.modified" + match_equals = "^.*mypath.*$" + } +} +`, rName) +} + func testAccWebhookConfig_ipAuth(rName, githubToken string) string { return testAccWebhookConfig_codePipeline(rName, githubToken) + fmt.Sprintf(` resource "aws_codepipeline_webhook" "test" { From 860af23d72c374fc9715ba759d3eca0f77fd88d2 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Tue, 4 Jan 2022 22:11:45 +0200 Subject: [PATCH 4/5] revert --- internal/service/codepipeline/codepipeline.go | 83 +++++-------------- 1 file changed, 21 insertions(+), 62 deletions(-) diff --git a/internal/service/codepipeline/codepipeline.go b/internal/service/codepipeline/codepipeline.go index 206a92f26d7..d2de9462a8f 100644 --- a/internal/service/codepipeline/codepipeline.go +++ b/internal/service/codepipeline/codepipeline.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "log" - "regexp" "strings" "github.com/aws/aws-sdk-go/aws" @@ -51,16 +50,11 @@ func ResourceCodePipeline() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: validation.All( - validation.StringLenBetween(1, 100), - validation.StringMatch(regexp.MustCompile(`[A-Za-z0-9.@\-_]+`), ""), - ), }, "role_arn": { - Type: schema.TypeString, - Required: true, - ValidateFunc: verify.ValidARN, + Type: schema.TypeString, + Required: true, }, "artifact_store": { Type: schema.TypeSet, @@ -111,10 +105,6 @@ func ResourceCodePipeline() *schema.Resource { "name": { Type: schema.TypeString, Required: true, - ValidateFunc: validation.All( - validation.StringLenBetween(1, 100), - validation.StringMatch(regexp.MustCompile(`[A-Za-z0-9.@\-_]+`), ""), - ), }, "action": { Type: schema.TypeList, @@ -122,12 +112,8 @@ func ResourceCodePipeline() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "configuration": { - Type: schema.TypeMap, - Optional: true, - ValidateDiagFunc: allDiagFunc( - validation.MapKeyLenBetween(1, 50), - validation.MapKeyLenBetween(1, 1000), - ), + Type: schema.TypeMap, + Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, DiffSuppressFunc: suppressCodePipelineStageActionConfiguration, }, @@ -149,10 +135,6 @@ func ResourceCodePipeline() *schema.Resource { "version": { Type: schema.TypeString, Required: true, - ValidateFunc: validation.All( - validation.StringLenBetween(1, 9), - validation.StringMatch(regexp.MustCompile(`[0-9A-Za-z_-]+`), ""), - ), }, "input_artifacts": { Type: schema.TypeList, @@ -167,21 +149,15 @@ func ResourceCodePipeline() *schema.Resource { "name": { Type: schema.TypeString, Required: true, - ValidateFunc: validation.All( - validation.StringLenBetween(1, 100), - validation.StringMatch(regexp.MustCompile(`[A-Za-z0-9.@\-_]+`), ""), - ), }, "role_arn": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: verify.ValidARN, + Type: schema.TypeString, + Optional: true, }, "run_order": { - Type: schema.TypeInt, - Optional: true, - Computed: true, - ValidateFunc: validation.IntBetween(1, 999), + Type: schema.TypeInt, + Optional: true, + Computed: true, }, "region": { Type: schema.TypeString, @@ -191,10 +167,6 @@ func ResourceCodePipeline() *schema.Resource { "namespace": { Type: schema.TypeString, Optional: true, - ValidateFunc: validation.All( - validation.StringLenBetween(1, 100), - validation.StringMatch(regexp.MustCompile(`[A-Za-z0-9@\-_]+`), ""), - ), }, }, }, @@ -262,7 +234,7 @@ func expand(d *schema.ResourceData) (*codepipeline.PipelineDeclaration, error) { Stages: expandStages(d), } - pipelineArtifactStores, err := expandArtifactStores(d.Get("artifact_store").(*schema.Set).List()) + pipelineArtifactStores, err := ExpandArtifactStores(d.Get("artifact_store").(*schema.Set).List()) if err != nil { return nil, err } @@ -277,7 +249,7 @@ func expand(d *schema.ResourceData) (*codepipeline.PipelineDeclaration, error) { return &pipeline, nil } -func expandArtifactStores(configs []interface{}) (map[string]*codepipeline.ArtifactStore, error) { +func ExpandArtifactStores(configs []interface{}) (map[string]*codepipeline.ArtifactStore, error) { if len(configs) == 0 { return nil, nil } @@ -593,19 +565,17 @@ func resourceCodePipelineRead(d *schema.ResourceData, meta interface{}) error { func resourceCodePipelineUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).CodePipelineConn - if d.HasChangesExcept("tags", "tags_all") { - pipeline, err := expand(d) - if err != nil { - return err - } - params := &codepipeline.UpdatePipelineInput{ - Pipeline: pipeline, - } - _, err = conn.UpdatePipeline(params) + pipeline, err := expand(d) + if err != nil { + return err + } + params := &codepipeline.UpdatePipelineInput{ + Pipeline: pipeline, + } + _, err = conn.UpdatePipeline(params) - if err != nil { - return fmt.Errorf("[ERROR] Error updating CodePipeline (%s): %w", d.Id(), err) - } + if err != nil { + return fmt.Errorf("[ERROR] Error updating CodePipeline (%s): %w", d.Id(), err) } arn := d.Get("arn").(string) @@ -681,14 +651,3 @@ func hashCodePipelineGitHubToken(token string) string { sum := sha256.Sum256([]byte(token)) return codePipelineGitHubTokenHashPrefix + hex.EncodeToString(sum[:]) } - -// https://github.com/hashicorp/terraform-plugin-sdk/issues/780. -func allDiagFunc(validators ...schema.SchemaValidateDiagFunc) schema.SchemaValidateDiagFunc { - return func(i interface{}, k cty.Path) diag.Diagnostics { - var diags diag.Diagnostics - for _, validator := range validators { - diags = append(diags, validator(i, k)...) - } - return diags - } -} From 9f95d7cc101365981066720b379bd1f22b73d61c Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Wed, 5 Jan 2022 10:36:49 +0200 Subject: [PATCH 5/5] docs + changelog --- .changelog/22406.txt | 11 +++++++++++ website/docs/r/codepipeline_webhook.markdown | 1 + 2 files changed, 12 insertions(+) create mode 100644 .changelog/22406.txt diff --git a/.changelog/22406.txt b/.changelog/22406.txt new file mode 100644 index 00000000000..1420969140b --- /dev/null +++ b/.changelog/22406.txt @@ -0,0 +1,11 @@ +```release-note:enhancement +resource/aws_codepipeline_webhook: Add plan time validation for `authentication_configuration.secret_token`, `filter.json_path`, `filter.match_equals`, `name`. +``` + +```release-note:enhancement +resource/aws_codepipeline_webhook: Add `arn` attribute. +``` + +```release-note:enhancement +resource/aws_codepipeline_webhook: Allow updating `filter` in place. +``` \ No newline at end of file diff --git a/website/docs/r/codepipeline_webhook.markdown b/website/docs/r/codepipeline_webhook.markdown index 05d43c0e49a..2cc37d89be8 100644 --- a/website/docs/r/codepipeline_webhook.markdown +++ b/website/docs/r/codepipeline_webhook.markdown @@ -131,6 +131,7 @@ A `filter` block supports the following arguments: In addition to all arguments above, the following attributes are exported: +* `arn` - The CodePipeline webhook's ARN. * `id` - The CodePipeline webhook's ARN. * `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block). * `url` - The CodePipeline webhook's URL. POST events to this endpoint to trigger the target.