Skip to content

Commit

Permalink
Merge pull request #3597 from terraform-providers/b-aws_ssm_activatio…
Browse files Browse the repository at this point in the history
…n-expiration-date-crash

resource/aws_ssm_activation: Prevent crash with expiration_date
  • Loading branch information
bflad authored Mar 2, 2018
2 parents 6af299d + 7ca7f29 commit 0cd8e5a
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 6 deletions.
12 changes: 7 additions & 5 deletions aws/resource_aws_ssm_activation.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ func resourceAwsSsmActivation() *schema.Resource {
Computed: true,
},
"expiration_date": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validateRFC3339TimeString,
},
"iam_role": {
Type: schema.TypeString,
Expand Down Expand Up @@ -77,8 +78,9 @@ func resourceAwsSsmActivationCreate(d *schema.ResourceData, meta interface{}) er
activationInput.Description = aws.String(d.Get("description").(string))
}

if _, ok := d.GetOk("expiration_date"); ok {
activationInput.ExpirationDate = aws.Time(d.Get("expiration_date").(time.Time))
if v, ok := d.GetOk("expiration_date"); ok {
t, _ := time.Parse(time.RFC3339, v.(string))
activationInput.ExpirationDate = aws.Time(t)
}

if _, ok := d.GetOk("iam_role"); ok {
Expand Down
64 changes: 64 additions & 0 deletions aws/resource_aws_ssm_activation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package aws

import (
"fmt"
"regexp"
"testing"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ssm"
Expand All @@ -29,6 +31,32 @@ func TestAccAWSSSMActivation_basic(t *testing.T) {
})
}

func TestAccAWSSSMActivation_expirationDate(t *testing.T) {
rName := acctest.RandString(10)
expirationTime := time.Now().Add(48 * time.Hour)
expirationDateS := expirationTime.Format(time.RFC3339)
resourceName := "aws_ssm_activation.foo"

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSSSMActivationDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSSSMActivationConfig_expirationDate(rName, "2018-03-01"),
ExpectError: regexp.MustCompile(`cannot parse`),
},
{
Config: testAccAWSSSMActivationConfig_expirationDate(rName, expirationDateS),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSSSMActivationExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "expiration_date", expirationDateS),
),
},
},
})
}

func testAccCheckAWSSSMActivationExists(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
Expand Down Expand Up @@ -130,3 +158,39 @@ resource "aws_ssm_activation" "foo" {
}
`, rName, rName)
}

func testAccAWSSSMActivationConfig_expirationDate(rName, expirationDate string) string {
return fmt.Sprintf(`
resource "aws_iam_role" "test_role" {
name = "test_role-%[1]s"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ssm.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "test_attach" {
role = "${aws_iam_role.test_role.name}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM"
}
resource "aws_ssm_activation" "foo" {
name = "test_ssm_activation-%[1]s",
description = "Test"
expiration_date = "%[2]s"
iam_role = "${aws_iam_role.test_role.name}"
registration_limit = "5"
depends_on = ["aws_iam_role_policy_attachment.test_attach"]
}
`, rName, expirationDate)
}
9 changes: 9 additions & 0 deletions aws/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ import (
"github.com/hashicorp/terraform/helper/structure"
)

// When released, replace all usage with upstream validation function:
// https://github.com/hashicorp/terraform/pull/17484
func validateRFC3339TimeString(v interface{}, k string) (ws []string, errors []error) {
if _, err := time.Parse(time.RFC3339, v.(string)); err != nil {
errors = append(errors, fmt.Errorf("%q: %s", k, err))
}
return
}

func validateInstanceUserDataSize(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
length := len(value)
Expand Down
73 changes: 73 additions & 0 deletions aws/validators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,86 @@ package aws

import (
"fmt"
"regexp"
"strings"
"testing"

"github.com/aws/aws-sdk-go/service/cognitoidentity"
"github.com/aws/aws-sdk-go/service/s3"
)

func TestValidateRFC3339TimeString(t *testing.T) {
testCases := []struct {
val interface{}
expectedErr *regexp.Regexp
}{
{
val: "2018-03-01T00:00:00Z",
},
{
val: "2018-03-01T00:00:00-05:00",
},
{
val: "2018-03-01T00:00:00+05:00",
},
{
val: "03/01/2018",
expectedErr: regexp.MustCompile(regexp.QuoteMeta(`cannot parse "1/2018" as "2006"`)),
},
{
val: "03-01-2018",
expectedErr: regexp.MustCompile(regexp.QuoteMeta(`cannot parse "1-2018" as "2006"`)),
},
{
val: "2018-03-01",
expectedErr: regexp.MustCompile(regexp.QuoteMeta(`cannot parse "" as "T"`)),
},
{
val: "2018-03-01T",
expectedErr: regexp.MustCompile(regexp.QuoteMeta(`cannot parse "" as "15"`)),
},
{
val: "2018-03-01T00:00:00",
expectedErr: regexp.MustCompile(regexp.QuoteMeta(`cannot parse "" as "Z07:00"`)),
},
{
val: "2018-03-01T00:00:00Z05:00",
expectedErr: regexp.MustCompile(regexp.QuoteMeta(`extra text: 05:00`)),
},
{
val: "2018-03-01T00:00:00Z-05:00",
expectedErr: regexp.MustCompile(regexp.QuoteMeta(`extra text: -05:00`)),
},
}

matchErr := func(errs []error, r *regexp.Regexp) bool {
// err must match one provided
for _, err := range errs {
if r.MatchString(err.Error()) {
return true
}
}

return false
}

for i, tc := range testCases {
_, errs := validateRFC3339TimeString(tc.val, "test_property")

if len(errs) == 0 && tc.expectedErr == nil {
continue
}

if len(errs) != 0 && tc.expectedErr == nil {
t.Fatalf("expected test case %d to produce no errors, got %v", i, errs)
}

if !matchErr(errs, tc.expectedErr) {
t.Fatalf("expected test case %d to produce error matching \"%s\", got %v", i, tc.expectedErr, errs)
}
}
}

func TestValidateInstanceUserDataSize(t *testing.T) {
validValues := []string{
"#!/bin/bash",
Expand Down
2 changes: 1 addition & 1 deletion website/docs/r/ssm_activation.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ The following arguments are supported:

* `name` - (Optional) The default name of the registerd managed instance.
* `description` - (Optional) The description of the resource that you want to register.
* `expiration_date` - (Optional) The date by which this activation request should expire. The default value is 24 hours.
* `expiration_date` - (Optional) A timestamp in [RFC3339 format](https://tools.ietf.org/html/rfc3339#section-5.8) by which this activation request should expire. The default value is 24 hours from resource creation time.
* `iam_role` - (Required) The IAM Role to attach to the managed instance.
* `registration_limit` - (Optional) The maximum number of managed instances you want to register. The default value is 1 instance.

Expand Down

0 comments on commit 0cd8e5a

Please sign in to comment.