-
Notifications
You must be signed in to change notification settings - Fork 9.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #16678 from hashicorp/f-aws_autoscaling_group-inst…
…ance_refresh resource/aws_autoscaling_group: Add Instance Refresh configuration
- Loading branch information
Showing
10 changed files
with
1,186 additions
and
111 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package nullable | ||
|
||
import ( | ||
"fmt" | ||
"strconv" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
) | ||
|
||
const ( | ||
TypeNullableInt = schema.TypeString | ||
) | ||
|
||
type Int string | ||
|
||
func (i Int) IsNull() bool { | ||
return i == "" | ||
} | ||
|
||
func (i Int) Value() (int64, bool, error) { | ||
if i.IsNull() { | ||
return 0, true, nil | ||
} | ||
|
||
value, err := strconv.ParseInt(string(i), 10, 64) | ||
if err != nil { | ||
return 0, false, err | ||
} | ||
return value, false, nil | ||
} | ||
|
||
// ValidateTypeStringNullableInt provides custom error messaging for TypeString ints | ||
// Some arguments require an int value or unspecified, empty field. | ||
func ValidateTypeStringNullableInt(v interface{}, k string) (ws []string, es []error) { | ||
value, ok := v.(string) | ||
if !ok { | ||
es = append(es, fmt.Errorf("expected type of %s to be string", k)) | ||
return | ||
} | ||
|
||
if value == "" { | ||
return | ||
} | ||
|
||
if _, err := strconv.ParseInt(value, 10, 64); err != nil { | ||
es = append(es, fmt.Errorf("%s: cannot parse '%s' as int: %w", k, value, err)) | ||
} | ||
|
||
return | ||
} | ||
|
||
// ValidateTypeStringNullableIntAtLeast provides custom error messaging for TypeString ints | ||
// Some arguments require an int value or unspecified, empty field. | ||
func ValidateTypeStringNullableIntAtLeast(min int) schema.SchemaValidateFunc { | ||
return func(i interface{}, k string) (ws []string, es []error) { | ||
value, ok := i.(string) | ||
if !ok { | ||
es = append(es, fmt.Errorf("expected type of %s to be string", k)) | ||
return | ||
} | ||
|
||
if value == "" { | ||
return | ||
} | ||
|
||
v, err := strconv.ParseInt(value, 10, 64) | ||
if err != nil { | ||
es = append(es, fmt.Errorf("%s: cannot parse '%s' as int: %w", k, value, err)) | ||
return | ||
} | ||
|
||
if v < int64(min) { | ||
es = append(es, fmt.Errorf("expected %s to be at least (%d), got %d", k, min, v)) | ||
} | ||
|
||
return | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package nullable | ||
|
||
import ( | ||
"errors" | ||
"regexp" | ||
"strconv" | ||
"testing" | ||
) | ||
|
||
func TestNullableInt(t *testing.T) { | ||
cases := []struct { | ||
val string | ||
expectedNull bool | ||
expectedValue int64 | ||
expectedErr error | ||
}{ | ||
{ | ||
val: "1", | ||
expectedNull: false, | ||
expectedValue: 1, | ||
}, | ||
{ | ||
val: "", | ||
expectedNull: true, | ||
expectedValue: 0, | ||
}, | ||
{ | ||
val: "A", | ||
expectedNull: false, | ||
expectedValue: 0, | ||
expectedErr: strconv.ErrSyntax, | ||
}, | ||
} | ||
|
||
for i, tc := range cases { | ||
v := Int(tc.val) | ||
|
||
if null := v.IsNull(); null != tc.expectedNull { | ||
t.Fatalf("expected test case %d IsNull to return %t, got %t", i, null, tc.expectedNull) | ||
} | ||
|
||
value, null, err := v.Value() | ||
if value != tc.expectedValue { | ||
t.Fatalf("expected test case %d Value to be %d, got %d", i, tc.expectedValue, value) | ||
} | ||
if null != tc.expectedNull { | ||
t.Fatalf("expected test case %d Value null flag to be %t, got %t", i, tc.expectedNull, null) | ||
} | ||
if tc.expectedErr == nil && err != nil { | ||
t.Fatalf("expected test case %d to succeed, got error %s", i, err) | ||
} | ||
if tc.expectedErr != nil { | ||
if !errors.Is(err, tc.expectedErr) { | ||
t.Fatalf("expected test case %d to have error matching \"%s\", got %s", i, tc.expectedErr, err) | ||
} | ||
} | ||
} | ||
} | ||
|
||
func TestValidationInt(t *testing.T) { | ||
runTestCases(t, []testCase{ | ||
{ | ||
val: "1", | ||
f: ValidateTypeStringNullableInt, | ||
}, | ||
{ | ||
val: "A", | ||
f: ValidateTypeStringNullableInt, | ||
expectedErr: regexp.MustCompile(`[\w]+: cannot parse 'A' as int: .*`), | ||
}, | ||
{ | ||
val: 1, | ||
f: ValidateTypeStringNullableInt, | ||
expectedErr: regexp.MustCompile(`expected type of [\w]+ to be string`), | ||
}, | ||
}) | ||
} | ||
|
||
func TestValidationIntAtLeast(t *testing.T) { | ||
runTestCases(t, []testCase{ | ||
{ | ||
val: "1", | ||
f: ValidateTypeStringNullableIntAtLeast(1), | ||
}, | ||
{ | ||
val: "1", | ||
f: ValidateTypeStringNullableIntAtLeast(0), | ||
}, | ||
{ | ||
val: "1", | ||
f: ValidateTypeStringNullableIntAtLeast(2), | ||
expectedErr: regexp.MustCompile(`expected [\w]+ to be at least \(2\), got 1`), | ||
}, | ||
{ | ||
val: 1, | ||
f: ValidateTypeStringNullableIntAtLeast(2), | ||
expectedErr: regexp.MustCompile(`expected type of [\w]+ to be string`), | ||
}, | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package nullable | ||
|
||
import ( | ||
"regexp" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
testing "github.com/mitchellh/go-testing-interface" | ||
) | ||
|
||
type testCase struct { | ||
val interface{} | ||
f schema.SchemaValidateFunc | ||
expectedErr *regexp.Regexp | ||
} | ||
|
||
func runTestCases(t testing.T, cases []testCase) { | ||
t.Helper() | ||
|
||
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 cases { | ||
_, errs := tc.f(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) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package waiter | ||
|
||
import ( | ||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/service/autoscaling" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" | ||
) | ||
|
||
func InstanceRefreshStatus(conn *autoscaling.AutoScaling, asgName, instanceRefreshId string) resource.StateRefreshFunc { | ||
return func() (interface{}, string, error) { | ||
input := autoscaling.DescribeInstanceRefreshesInput{ | ||
AutoScalingGroupName: aws.String(asgName), | ||
InstanceRefreshIds: []*string{aws.String(instanceRefreshId)}, | ||
} | ||
output, err := conn.DescribeInstanceRefreshes(&input) | ||
if err != nil { | ||
return nil, "", err | ||
} | ||
|
||
if output == nil || len(output.InstanceRefreshes) == 0 || output.InstanceRefreshes[0] == nil { | ||
return nil, "", nil | ||
} | ||
|
||
instanceRefresh := output.InstanceRefreshes[0] | ||
|
||
return instanceRefresh, aws.StringValue(instanceRefresh.Status), nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package waiter | ||
|
||
import ( | ||
"time" | ||
|
||
"github.com/aws/aws-sdk-go/service/autoscaling" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" | ||
) | ||
|
||
const ( | ||
// Maximum amount of time to wait for an InstanceRefresh to be started | ||
// Must be at least as long as InstanceRefreshCancelledTimeout, since we try to cancel any | ||
// existing Instance Refreshes when starting. | ||
InstanceRefreshStartedTimeout = InstanceRefreshCancelledTimeout | ||
|
||
// Maximum amount of time to wait for an Instance Refresh to be Cancelled | ||
InstanceRefreshCancelledTimeout = 15 * time.Minute | ||
) | ||
|
||
func InstanceRefreshCancelled(conn *autoscaling.AutoScaling, asgName, instanceRefreshId string) (*autoscaling.InstanceRefresh, error) { | ||
stateConf := &resource.StateChangeConf{ | ||
Pending: []string{ | ||
autoscaling.InstanceRefreshStatusPending, | ||
autoscaling.InstanceRefreshStatusInProgress, | ||
autoscaling.InstanceRefreshStatusCancelling, | ||
}, | ||
Target: []string{ | ||
autoscaling.InstanceRefreshStatusCancelled, | ||
// Failed and Successful are also acceptable end-states | ||
autoscaling.InstanceRefreshStatusFailed, | ||
autoscaling.InstanceRefreshStatusSuccessful, | ||
}, | ||
Refresh: InstanceRefreshStatus(conn, asgName, instanceRefreshId), | ||
Timeout: InstanceRefreshCancelledTimeout, | ||
} | ||
|
||
outputRaw, err := stateConf.WaitForState() | ||
|
||
if v, ok := outputRaw.(*autoscaling.InstanceRefresh); ok { | ||
return v, err | ||
} | ||
|
||
return nil, err | ||
} |
Oops, something went wrong.