Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a aws_codepipeline_webhook resource for managing CodePipeline webhooks #5875

Merged
merged 26 commits into from
Oct 18, 2018

Conversation

joestump
Copy link
Contributor

@joestump joestump commented Sep 13, 2018

What does this add?

Introduces the aws_codepipeline_webhook resource to manage AWS CodePipeline webhooks.

Example HCL

# A shared secret between GitHub and AWS that allows AWS
# CodePipeline to authenticate the request came from GitHub.
# Would probably be better to pull this from the environment
# or something like SSM Parameter Store.
locals {
  webhook_secret = "super-secret"
}

resource "aws_codepipeline_webhook" "bar" {
  name            = "test-webhook-github-bar" 
  authentication  = "GITHUB_HMAC" 
  target_action   = "Source"
  target_pipeline = "${aws_codepipeline.bar.name}"

  authentication_configuration {
    secret_token = "${local.webhook_secret}"
  }

  filter {
    json_path    = "$.ref"
    match_equals = "refs/heads/{Branch}"
  }
}

# Wire the CodePipeline webhook into a GitHub repository.
resource "github_repository_webhook" "bar" {
  repository = "${github_repository.repo.name}"

  name = "web"

  configuration {
    url          = "${aws_codepipeline_webhook.bar.url}"
    content_type = "form"
    insecure_ssl = true 
    secret       = "${local.webhook_secret}"
  }
  events = ["push"]
}

Test output

make testacc TESTARGS='-run=TestAccAWSCodePipelineWebhook'
TF_ACC=1 go test ./... -v -run=TestAccAWSCodePipelineWebhook -timeout 120m
=== RUN   TestAccAWSCodePipelineWebhook_basic
--- PASS: TestAccAWSCodePipelineWebhook_basic (31.78s)
=== RUN   TestAccAWSCodePipelineWebhook_ipAuth
--- PASS: TestAccAWSCodePipelineWebhook_ipAuth (33.43s)
=== RUN   TestAccAWSCodePipelineWebhook_unauthenticated
--- PASS: TestAccAWSCodePipelineWebhook_unauthenticated (29.54s)
PASS
ok  	github.com/terraform-providers/terraform-provider-aws/aws	95.993s

TODO

  • Create an acceptance test
  • Write documentation
  • Clean up PR with HCL example

@ghost ghost added the size/L Managed by automation to categorize the size of a PR. label Sep 13, 2018
@bflad bflad added new-resource Introduces a new resource. service/codepipeline Issues and PRs that pertain to the codepipeline service. labels Sep 14, 2018
@ghost ghost added size/XL Managed by automation to categorize the size of a PR. documentation Introduces or discusses updates to documentation. tests PRs: expanded test coverage. Issues: expanded coverage, enhancements to test infrastructure. and removed size/L Managed by automation to categorize the size of a PR. labels Oct 14, 2018
@joestump joestump changed the title WIP: Add a aws_codepipeline_webhook resource for managing CodePipeline webhooks Add a aws_codepipeline_webhook resource for managing CodePipeline webhooks Oct 14, 2018
@joestump
Copy link
Contributor Author

Here's a complete working example. Push a buildspec.yml to kick off a pipeline run/build.

https://gist.github.com/joestump/cac3abb94050186fcba1c57c8a880a71

@ghost ghost added the service/codebuild Issues and PRs that pertain to the codebuild service. label Oct 14, 2018
@joestump
Copy link
Contributor Author

@bflad I took another pass at this one after your comments on #6117. Would love to get 👀 on this one as well. 😄

Copy link
Contributor

@bflad bflad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @joestump 👋 Thanks for submitting this! Initial feedback below. Please reach out with any questions or if you do not have time to implement these items.

Read: resourceAwsCodePipelineWebhookRead,
Update: nil,
Delete: resourceAwsCodePipelineWebhookDelete,
SchemaVersion: 1,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SchemaVersion should be omitted for new resources (it defaults to 0)

aws/resource_aws_codepipeline_webhook.go Show resolved Hide resolved
}
}

func extractCodePipelineWebhookAttr(d *schema.ResourceData, attr string) (map[string]interface{}, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We prefer to allow the schema and any API errors to dictate this behavior rather than implementing additional error handling in the resource functions. authentication_configuration is marked as Required: true and we can allow the API (or the handling in extractCodePipelineWebhookAuthConfig) to return errors if both Optional nested arguments are omitted.

break
case codepipeline.WebhookAuthenticationTypeUnauthenticated:
break
default:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The schema handling for the auth_type argument prevents this case from ever getting hit.

case codepipeline.WebhookAuthenticationTypeIp:
ipRange := authConfig["allowed_ip_range"].(string)
if ipRange == "" {
return nil, fmt.Errorf("An IP range must be set when using IP-based auth")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the API returns a sufficient error message, we should remove this 👍

return webhooks, nil
}

func setCodePipelineWebhookFilters(webhook codepipeline.WebhookDefinition, d *schema.ResourceData) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: In this project we tend to have "flatten" functions which perform the first half of this function, then leave the ResourceData handling in the Read function. e.g.

func flattenCodePipelineWebhookFilters(filters []*codepipeline.WebhookFilterRule) []interface{} {
// ...
}

// in Read
if err := d.Set("filter", flattenCodePipelineWebhookFilters(webhook.Filters)); err != nil {
  return fmt.Errorf("error setting filter: %s", err)
}


d.Set("name", name)

targetAction := *found.TargetAction
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other than the recommendation to use the AWS Go SDK helper functions for these, its also important to note that d.Set() can also directly handle pointers to simplify things and not require dereferencing (if the types match), e.g.

if err := d.Set("target_action", found.TargetAction); err != nil {
  return fmt.Errorf("error setting target_action: %s", err)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to know. For clarification, which is preferred?

d.Set("target_action", found.TargetAction)

or...

d.Set("target_action", aws.String(found.TargetAction))

return fmt.Errorf("Could not delete webhook: %s", err)
}

d.SetId("")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling d.SetId("") during the Delete function is extraneous. 👍


resource "aws_codepipeline_webhook" "bar" {
name = "test-webhook-%s"
authentication = "GITHUB_HMAC"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add testing for the other authentication types as well. 👍 You may find its easiest to just make the "base" configuration above its own function so it doesn't need to be duplicated each test configuration.

Type: schema.TypeList,
MaxItems: 1,
MinItems: 1,
Required: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this required for authentication = "UNAUTHENTICATED"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not. I'll mark it as Optional: true instead.

@joestump
Copy link
Contributor Author

@bflad I believe all comments have been addressed. There's a test for each authentication_configuration iteration now. 👍

bflad added a commit that referenced this pull request Oct 18, 2018
* Remove extraneous error returns from extractCodePipelineWebhookRules and extractCodePipelineWebhookAuthConfig
* Use resource.NotFoundError in getCodePipelineWebhook
Copy link
Contributor

@bflad bflad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, @joestump! LGTM with two little things I'm fixing on merge so we can release this today. 🚀

(after mentioned changes)

--- PASS: TestAccAWSCodePipelineWebhook_basic (15.72s)
--- PASS: TestAccAWSCodePipelineWebhook_ipAuth (24.27s)
--- PASS: TestAccAWSCodePipelineWebhook_unauthenticated (25.03s)

}
}

func extractCodePipelineWebhookRules(filters *schema.Set) ([]*codepipeline.WebhookFilterRule, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Since we never return any errors we can just leave off that return (and its downstream handling) 😄


arn := d.Id()
webhook, err := getCodePipelineWebhook(conn, arn)
if err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We only want Terraform to recreate the resource if the error indicates the resource was not found, otherwise we need to return the actual error message to the operator (e.g. AuthorizationDenied, InternalServiceError messages). Utilizing resource.NotFoundError should be enough to do the trick so we can do the following here:

// in getCodePipelineWebhook()
	return nil, &resource.NotFoundError{
		Message: fmt.Sprintf("No webhook with ARN %s found", arn),
	}

// in resourceAwsCodePipelineWebhookRead()
if isResourceNotFoundError(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)
}

@bflad bflad merged commit b8185ce into hashicorp:master Oct 18, 2018
bflad added a commit that referenced this pull request Oct 18, 2018
@bflad bflad added this to the v1.41.0 milestone Oct 18, 2018
@joestump joestump deleted the jstump-codepipeline-webhook branch October 18, 2018 18:46
@bflad
Copy link
Contributor

bflad commented Oct 18, 2018

This has been released in version 1.41.0 of the AWS provider. Please see the Terraform documentation on provider versioning or reach out if you need any assistance upgrading.

@ghost
Copy link

ghost commented Apr 2, 2020

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. Thanks!

@ghost ghost locked and limited conversation to collaborators Apr 2, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
documentation Introduces or discusses updates to documentation. new-resource Introduces a new resource. service/codebuild Issues and PRs that pertain to the codebuild service. service/codepipeline Issues and PRs that pertain to the codepipeline service. size/XL Managed by automation to categorize the size of a PR. tests PRs: expanded test coverage. Issues: expanded coverage, enhancements to test infrastructure.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants