Skip to content

Commit

Permalink
New Resource: aws_ses_template (#2003)
Browse files Browse the repository at this point in the history
* New Resource: aws_ses_template

* Address #2006 PR review comments

* remove extraneous id field in schema
* remove extraneous schema timeout definitions
* use isAWSErr instead of manual awserr check
  • Loading branch information
bflad authored and radeksimko committed Nov 7, 2017
1 parent 95f2651 commit 058ba61
Show file tree
Hide file tree
Showing 6 changed files with 412 additions and 0 deletions.
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ func Provider() terraform.ResourceProvider {
"aws_ses_receipt_rule_set": resourceAwsSesReceiptRuleSet(),
"aws_ses_configuration_set": resourceAwsSesConfigurationSet(),
"aws_ses_event_destination": resourceAwsSesEventDestination(),
"aws_ses_template": resourceAwsSesTemplate(),
"aws_s3_bucket": resourceAwsS3Bucket(),
"aws_s3_bucket_policy": resourceAwsS3BucketPolicy(),
"aws_s3_bucket_object": resourceAwsS3BucketObject(),
Expand Down
144 changes: 144 additions & 0 deletions aws/resource_aws_ses_template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package aws

import (
"fmt"
"log"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ses"
"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsSesTemplate() *schema.Resource {
return &schema.Resource{
Create: resourceAwsSesTemplateCreate,
Read: resourceAwsSesTemplateRead,
Update: resourceAwsSesTemplateUpdate,
Delete: resourceAwsSesTemplateDelete,

Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateSesTemplateName,
},
"html": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validateSesTemplateHtml,
},
"subject": {
Type: schema.TypeString,
Optional: true,
},
"text": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validateSesTemplateText,
},
},
}
}
func resourceAwsSesTemplateCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).sesConn

templateName := d.Get("name").(string)

template := ses.Template{
TemplateName: aws.String(templateName),
}

if v, ok := d.GetOk("html"); ok {
template.HtmlPart = aws.String(v.(string))
}

if v, ok := d.GetOk("subject"); ok {
template.SubjectPart = aws.String(v.(string))
}

if v, ok := d.GetOk("text"); ok {
template.TextPart = aws.String(v.(string))
}

input := ses.CreateTemplateInput{
Template: &template,
}

log.Printf("[DEBUG] Creating SES template: %#v", input)
_, err := conn.CreateTemplate(&input)
if err != nil {
return fmt.Errorf("Creating SES template failed: %s", err.Error())
}
d.SetId(templateName)

return resourceAwsSesTemplateRead(d, meta)
}

func resourceAwsSesTemplateRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).sesConn
input := ses.GetTemplateInput{
TemplateName: aws.String(d.Id()),
}

log.Printf("[DEBUG] Reading SES template: %#v", input)
gto, err := conn.GetTemplate(&input)
if err != nil {
if isAWSErr(err, "TemplateDoesNotExist", "") {
log.Printf("[WARN] SES template %q not found, removing from state", d.Id())
d.SetId("")
return nil
}
return fmt.Errorf("Reading SES template '%s' failed: %s", *input.TemplateName, err.Error())
}

d.Set("html", gto.Template.HtmlPart)
d.Set("name", gto.Template.TemplateName)
d.Set("subject", gto.Template.SubjectPart)
d.Set("text", gto.Template.TextPart)

return nil
}

func resourceAwsSesTemplateUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).sesConn

templateName := d.Id()

template := ses.Template{
HtmlPart: aws.String(d.Get("html").(string)),
TemplateName: aws.String(templateName),
SubjectPart: aws.String(d.Get("subject").(string)),
TextPart: aws.String(d.Get("text").(string)),
}

input := ses.UpdateTemplateInput{
Template: &template,
}

log.Printf("[DEBUG] Update SES template: %#v", input)
_, err := conn.UpdateTemplate(&input)
if err != nil {
return fmt.Errorf("Updating SES template '%s' failed: %s", templateName, err.Error())
}

return resourceAwsSesTemplateRead(d, meta)
}

func resourceAwsSesTemplateDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).sesConn
input := ses.DeleteTemplateInput{
TemplateName: aws.String(d.Id()),
}

log.Printf("[DEBUG] Delete SES template: %#v", input)
_, err := conn.DeleteTemplate(&input)
if err != nil {
return fmt.Errorf("Deleting SES template '%s' failed: %s", *input.TemplateName, err.Error())
}
return nil
}
193 changes: 193 additions & 0 deletions aws/resource_aws_ses_template_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package aws

import (
"fmt"
"testing"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/ses"

"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSSesTemplate_Basic(t *testing.T) {
name := acctest.RandString(5)
var template ses.Template
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckSesTemplateDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccCheckAwsSesTemplateResourceConfigBasic1(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckSesTemplate("aws_ses_template.test", &template),
resource.TestCheckResourceAttr("aws_ses_template.test", "name", name),
resource.TestCheckResourceAttr("aws_ses_template.test", "html", "html"),
resource.TestCheckResourceAttr("aws_ses_template.test", "subject", "subject"),
resource.TestCheckResourceAttr("aws_ses_template.test", "text", ""),
),
},
},
})
}

func TestAccAWSSesTemplate_Update(t *testing.T) {
t.Skipf("Skip due to SES.UpdateTemplate eventual consistency issues")
name := acctest.RandString(5)
var template ses.Template
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckSesTemplateDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccCheckAwsSesTemplateResourceConfigBasic1(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckSesTemplate("aws_ses_template.test", &template),
resource.TestCheckResourceAttr("aws_ses_template.test", "name", name),
resource.TestCheckResourceAttr("aws_ses_template.test", "html", "html"),
resource.TestCheckResourceAttr("aws_ses_template.test", "subject", "subject"),
resource.TestCheckResourceAttr("aws_ses_template.test", "text", ""),
),
},
resource.TestStep{
Config: testAccCheckAwsSesTemplateResourceConfigBasic2(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckSesTemplate("aws_ses_template.test", &template),
resource.TestCheckResourceAttr("aws_ses_template.test", "name", name),
resource.TestCheckResourceAttr("aws_ses_template.test", "html", "html"),
resource.TestCheckResourceAttr("aws_ses_template.test", "subject", "subject"),
resource.TestCheckResourceAttr("aws_ses_template.test", "text", "text"),
),
},
resource.TestStep{
Config: testAccCheckAwsSesTemplateResourceConfigBasic3(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckSesTemplate("aws_ses_template.test", &template),
resource.TestCheckResourceAttr("aws_ses_template.test", "name", name),
resource.TestCheckResourceAttr("aws_ses_template.test", "html", "html update"),
resource.TestCheckResourceAttr("aws_ses_template.test", "subject", "subject"),
resource.TestCheckResourceAttr("aws_ses_template.test", "text", ""),
),
},
},
})
}

func TestAccAWSSesTemplate_Import(t *testing.T) {
resourceName := "aws_ses_template.test"

name := acctest.RandString(5)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckSesTemplateDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccCheckAwsSesTemplateResourceConfigBasic1(name),
},

resource.TestStep{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testAccCheckSesTemplate(pr string, template *ses.Template) resource.TestCheckFunc {
return func(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).sesConn
rs, ok := s.RootModule().Resources[pr]
if !ok {
return fmt.Errorf("Not found: %s", pr)
}

if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set")
}

input := ses.GetTemplateInput{
TemplateName: aws.String(rs.Primary.ID),
}

_, err := conn.GetTemplate(&input)
if err != nil {
return err
}

return nil
}
}

func testAccCheckSesTemplateDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).sesConn

for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_ses_template" {
continue
}
err := resource.Retry(1*time.Minute, func() *resource.RetryError {
input := ses.GetTemplateInput{
TemplateName: aws.String(rs.Primary.ID),
}

gto, err := conn.GetTemplate(&input)
if err != nil {
if awsErr, ok := err.(awserr.Error); ok && (awsErr.Code() == "TemplateDoesNotExist") {
return nil
}
return resource.NonRetryableError(err)
}
if gto.Template != nil {
return resource.RetryableError(fmt.Errorf("Template exists: %v", gto.Template))
}

return nil
})

if err != nil {
return err
}
}

return nil
}

func testAccCheckAwsSesTemplateResourceConfigBasic1(name string) string {
return fmt.Sprintf(`
resource "aws_ses_template" "test" {
name = "%s"
subject = "subject"
html = "html"
}
`, name)
}

func testAccCheckAwsSesTemplateResourceConfigBasic2(name string) string {
return fmt.Sprintf(`
resource "aws_ses_template" "test" {
name = "%s"
subject = "subject"
html = "html"
text = "text"
}
`, name)
}

func testAccCheckAwsSesTemplateResourceConfigBasic3(name string) string {
return fmt.Sprintf(`
resource "aws_ses_template" "test" {
name = "%s"
subject = "subject"
html = "html update"
}
`, name)
}
25 changes: 25 additions & 0 deletions aws/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -1537,6 +1537,31 @@ func validateServiceCatalogPortfolioProviderName(v interface{}, k string) (ws []
return
}

func validateSesTemplateName(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if (len(value) > 64) || (len(value) == 0) {
errors = append(errors, fmt.Errorf("SES template name must be between 1 and 64 characters."))
}
return
}

func validateSesTemplateHtml(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if len(value) > 512000 {
errors = append(errors, fmt.Errorf("SES template must be less than 500KB in size, including both the text and HTML parts."))
}
return
}

func validateSesTemplateText(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if len(value) > 512000 {
errors = append(errors, fmt.Errorf("SES template must be less than 500KB in size, including both the text and HTML parts."))
}

return
}

func validateCognitoRoleMappingsAmbiguousRoleResolutionAgainstType(v map[string]interface{}) (errors []error) {
t := v["type"].(string)
isRequired := t == cognitoidentity.RoleMappingTypeToken || t == cognitoidentity.RoleMappingTypeRules
Expand Down
4 changes: 4 additions & 0 deletions website/aws.erb
Original file line number Diff line number Diff line change
Expand Up @@ -1360,6 +1360,10 @@
<a href="/docs/providers/aws/r/ses_event_destination.html">aws_ses_event_destination</a>
</li>

<li<%= sidebar_current("docs-aws-resource-ses-template") %>>
<a href="/docs/providers/aws/r/ses_template.html">aws_ses_template</a>
</li>

</ul>
</li>

Expand Down
Loading

0 comments on commit 058ba61

Please sign in to comment.