mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
provider/aws: Add failing test for OpsWorks endpoints (#13024)
Fix an issue when upgrading from Terraform < 0.9 to 0.9+, when we added support for the regional endpoints in OpsWorks Stacks. OpsWorks Stacks can only be managed via the endpoint with which they were created, not where the stack resides.
This commit is contained in:
parent
b1d6b2e554
commit
dd25334b46
@ -3,14 +3,17 @@ package aws
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/opsworks"
|
||||
)
|
||||
|
||||
@ -183,6 +186,11 @@ func resourceAwsOpsworksStack() *schema.Resource {
|
||||
Computed: true,
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
"stack_endpoint": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -254,6 +262,13 @@ func resourceAwsOpsworksSetStackCustomCookbooksSource(d *schema.ResourceData, v
|
||||
|
||||
func resourceAwsOpsworksStackRead(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*AWSClient).opsworksconn
|
||||
var conErr error
|
||||
if v := d.Get("stack_endpoint").(string); v != "" {
|
||||
client, conErr = opsworksConnForRegion(v, meta)
|
||||
if conErr != nil {
|
||||
return conErr
|
||||
}
|
||||
}
|
||||
|
||||
req := &opsworks.DescribeStacksInput{
|
||||
StackIds: []*string{
|
||||
@ -263,16 +278,53 @@ func resourceAwsOpsworksStackRead(d *schema.ResourceData, meta interface{}) erro
|
||||
|
||||
log.Printf("[DEBUG] Reading OpsWorks stack: %s", d.Id())
|
||||
|
||||
resp, err := client.DescribeStacks(req)
|
||||
if err != nil {
|
||||
if awserr, ok := err.(awserr.Error); ok {
|
||||
if awserr.Code() == "ResourceNotFoundException" {
|
||||
log.Printf("[DEBUG] OpsWorks stack (%s) not found", d.Id())
|
||||
d.SetId("")
|
||||
return nil
|
||||
// notFound represents the number of times we've called DescribeStacks looking
|
||||
// for this Stack. If it's not found in the the default region we're in, we
|
||||
// check us-east-1 in the event this stack was created with Terraform before
|
||||
// version 0.9
|
||||
// See https://github.com/hashicorp/terraform/issues/12842
|
||||
var notFound int
|
||||
var resp *opsworks.DescribeStacksOutput
|
||||
var dErr error
|
||||
|
||||
for {
|
||||
resp, dErr = client.DescribeStacks(req)
|
||||
if dErr != nil {
|
||||
if awserr, ok := dErr.(awserr.Error); ok {
|
||||
if awserr.Code() == "ResourceNotFoundException" {
|
||||
if notFound < 1 {
|
||||
// If we haven't already, try us-east-1, legacy connection
|
||||
notFound++
|
||||
var connErr error
|
||||
client, connErr = opsworksConnForRegion("us-east-1", meta)
|
||||
if connErr != nil {
|
||||
return connErr
|
||||
}
|
||||
// start again from the top of the FOR loop, but with a client
|
||||
// configured to talk to us-east-1
|
||||
continue
|
||||
}
|
||||
|
||||
// We've tried both the original and us-east-1 endpoint, and the stack
|
||||
// is still not found
|
||||
log.Printf("[DEBUG] OpsWorks stack (%s) not found", d.Id())
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
// not ResoureNotFoundException, fall through to returning error
|
||||
}
|
||||
return dErr
|
||||
}
|
||||
// If the stack was found, set the stack_endpoint
|
||||
if client.Config.Region != nil && *client.Config.Region != "" {
|
||||
log.Printf("[DEBUG] Setting stack_endpoint for (%s) to (%s)", d.Id(), *client.Config.Region)
|
||||
if err := d.Set("stack_endpoint", *client.Config.Region); err != nil {
|
||||
log.Printf("[WARN] Error setting stack_endpoint: %s", err)
|
||||
}
|
||||
}
|
||||
return err
|
||||
log.Printf("[DEBUG] Breaking stack endpoint search, found stack for (%s)", d.Id())
|
||||
// Break the FOR loop
|
||||
break
|
||||
}
|
||||
|
||||
stack := resp.Stacks[0]
|
||||
@ -309,6 +361,40 @@ func resourceAwsOpsworksStackRead(d *schema.ResourceData, meta interface{}) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
// opsworksConn will return a connection for the stack_endpoint in the
|
||||
// configuration. Stacks can only be accessed or managed within the endpoint
|
||||
// in which they are created, so we allow users to specify an original endpoint
|
||||
// for Stacks created before multiple endpoints were offered (Terraform v0.9.0).
|
||||
// See:
|
||||
// - https://github.com/hashicorp/terraform/pull/12688
|
||||
// - https://github.com/hashicorp/terraform/issues/12842
|
||||
func opsworksConnForRegion(region string, meta interface{}) (*opsworks.OpsWorks, error) {
|
||||
originalConn := meta.(*AWSClient).opsworksconn
|
||||
|
||||
// Regions are the same, no need to reconfigure
|
||||
if originalConn.Config.Region != nil && *originalConn.Config.Region == region {
|
||||
return originalConn, nil
|
||||
}
|
||||
|
||||
// Set up base session
|
||||
sess, err := session.NewSession(&originalConn.Config)
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error creating AWS session: {{err}}", err)
|
||||
}
|
||||
|
||||
sess.Handlers.Build.PushBackNamed(addTerraformVersionToUserAgent)
|
||||
|
||||
if extraDebug := os.Getenv("TERRAFORM_AWS_AUTHFAILURE_DEBUG"); extraDebug != "" {
|
||||
sess.Handlers.UnmarshalError.PushFrontNamed(debugAuthFailure)
|
||||
}
|
||||
|
||||
newSession := sess.Copy(&aws.Config{Region: aws.String(region)})
|
||||
newOpsworksconn := opsworks.New(newSession)
|
||||
|
||||
log.Printf("[DEBUG] Returning new OpsWorks client")
|
||||
return newOpsworksconn, nil
|
||||
}
|
||||
|
||||
func resourceAwsOpsworksStackCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*AWSClient).opsworksconn
|
||||
|
||||
@ -396,6 +482,13 @@ func resourceAwsOpsworksStackCreate(d *schema.ResourceData, meta interface{}) er
|
||||
|
||||
func resourceAwsOpsworksStackUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*AWSClient).opsworksconn
|
||||
var conErr error
|
||||
if v := d.Get("stack_endpoint").(string); v != "" {
|
||||
client, conErr = opsworksConnForRegion(v, meta)
|
||||
if conErr != nil {
|
||||
return conErr
|
||||
}
|
||||
}
|
||||
|
||||
err := resourceAwsOpsworksStackValidate(d)
|
||||
if err != nil {
|
||||
@ -456,6 +549,13 @@ func resourceAwsOpsworksStackUpdate(d *schema.ResourceData, meta interface{}) er
|
||||
|
||||
func resourceAwsOpsworksStackDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*AWSClient).opsworksconn
|
||||
var conErr error
|
||||
if v := d.Get("stack_endpoint").(string); v != "" {
|
||||
client, conErr = opsworksConnForRegion(v, meta)
|
||||
if conErr != nil {
|
||||
return conErr
|
||||
}
|
||||
}
|
||||
|
||||
req := &opsworks.DeleteStackInput{
|
||||
StackId: aws.String(d.Id()),
|
||||
|
@ -74,6 +74,205 @@ func TestAccAWSOpsworksStackVpc(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// Tests the addition of regional endpoints and supporting the classic link used
|
||||
// to create Stack's prior to v0.9.0.
|
||||
// See https://github.com/hashicorp/terraform/issues/12842
|
||||
func TestAccAWSOpsWorksStack_classic_endpoints(t *testing.T) {
|
||||
stackName := fmt.Sprintf("tf-opsworks-acc-%d", acctest.RandInt())
|
||||
rInt := acctest.RandInt()
|
||||
var opsstack opsworks.Stack
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAwsOpsworksStackDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAwsOpsWorksStack_classic_endpoint(stackName, rInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSOpsworksStackExists(
|
||||
"aws_opsworks_stack.main", false, &opsstack),
|
||||
),
|
||||
},
|
||||
// Ensure that changing to us-west-2 region results in no plan
|
||||
resource.TestStep{
|
||||
Config: testAccAwsOpsWorksStack_regional_endpoint(stackName, rInt),
|
||||
PlanOnly: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func testAccAwsOpsWorksStack_classic_endpoint(rName string, rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
provider "aws" {
|
||||
region = "us-east-1"
|
||||
}
|
||||
|
||||
resource "aws_opsworks_stack" "main" {
|
||||
name = "%s"
|
||||
region = "us-west-2"
|
||||
service_role_arn = "${aws_iam_role.opsworks_service.arn}"
|
||||
default_instance_profile_arn = "${aws_iam_instance_profile.opsworks_instance.arn}"
|
||||
|
||||
configuration_manager_version = "12"
|
||||
default_availability_zone = "us-west-2b"
|
||||
}
|
||||
|
||||
resource "aws_iam_role" "opsworks_service" {
|
||||
name = "tf_opsworks_service_%d"
|
||||
|
||||
assume_role_policy = <<EOT
|
||||
{
|
||||
"Version": "2008-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "",
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Service": "opsworks.amazonaws.com"
|
||||
},
|
||||
"Action": "sts:AssumeRole"
|
||||
}
|
||||
]
|
||||
}
|
||||
EOT
|
||||
}
|
||||
|
||||
resource "aws_iam_role_policy" "opsworks_service" {
|
||||
name = "tf_opsworks_service_%d"
|
||||
role = "${aws_iam_role.opsworks_service.id}"
|
||||
|
||||
policy = <<EOT
|
||||
{
|
||||
"Statement": [
|
||||
{
|
||||
"Action": [
|
||||
"ec2:*",
|
||||
"iam:PassRole",
|
||||
"cloudwatch:GetMetricStatistics",
|
||||
"elasticloadbalancing:*",
|
||||
"rds:*"
|
||||
],
|
||||
"Effect": "Allow",
|
||||
"Resource": ["*"]
|
||||
}
|
||||
]
|
||||
}
|
||||
EOT
|
||||
}
|
||||
|
||||
resource "aws_iam_role" "opsworks_instance" {
|
||||
name = "tf_opsworks_instance_%d"
|
||||
|
||||
assume_role_policy = <<EOT
|
||||
{
|
||||
"Version": "2008-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "",
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Service": "ec2.amazonaws.com"
|
||||
},
|
||||
"Action": "sts:AssumeRole"
|
||||
}
|
||||
]
|
||||
}
|
||||
EOT
|
||||
}
|
||||
|
||||
resource "aws_iam_instance_profile" "opsworks_instance" {
|
||||
name = "%s_profile"
|
||||
roles = ["${aws_iam_role.opsworks_instance.name}"]
|
||||
}`, rName, rInt, rInt, rInt, rName)
|
||||
}
|
||||
|
||||
func testAccAwsOpsWorksStack_regional_endpoint(rName string, rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
provider "aws" {
|
||||
region = "us-west-2"
|
||||
}
|
||||
|
||||
resource "aws_opsworks_stack" "main" {
|
||||
name = "%s"
|
||||
region = "us-west-2"
|
||||
service_role_arn = "${aws_iam_role.opsworks_service.arn}"
|
||||
default_instance_profile_arn = "${aws_iam_instance_profile.opsworks_instance.arn}"
|
||||
|
||||
configuration_manager_version = "12"
|
||||
default_availability_zone = "us-west-2b"
|
||||
}
|
||||
|
||||
resource "aws_iam_role" "opsworks_service" {
|
||||
name = "tf_opsworks_service_%d"
|
||||
|
||||
assume_role_policy = <<EOT
|
||||
{
|
||||
"Version": "2008-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "",
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Service": "opsworks.amazonaws.com"
|
||||
},
|
||||
"Action": "sts:AssumeRole"
|
||||
}
|
||||
]
|
||||
}
|
||||
EOT
|
||||
}
|
||||
|
||||
resource "aws_iam_role_policy" "opsworks_service" {
|
||||
name = "tf_opsworks_service_%d"
|
||||
role = "${aws_iam_role.opsworks_service.id}"
|
||||
|
||||
policy = <<EOT
|
||||
{
|
||||
"Statement": [
|
||||
{
|
||||
"Action": [
|
||||
"ec2:*",
|
||||
"iam:PassRole",
|
||||
"cloudwatch:GetMetricStatistics",
|
||||
"elasticloadbalancing:*",
|
||||
"rds:*"
|
||||
],
|
||||
"Effect": "Allow",
|
||||
"Resource": ["*"]
|
||||
}
|
||||
]
|
||||
}
|
||||
EOT
|
||||
}
|
||||
|
||||
resource "aws_iam_role" "opsworks_instance" {
|
||||
name = "tf_opsworks_instance_%d"
|
||||
|
||||
assume_role_policy = <<EOT
|
||||
{
|
||||
"Version": "2008-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "",
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Service": "ec2.amazonaws.com"
|
||||
},
|
||||
"Action": "sts:AssumeRole"
|
||||
}
|
||||
]
|
||||
}
|
||||
EOT
|
||||
}
|
||||
|
||||
resource "aws_iam_instance_profile" "opsworks_instance" {
|
||||
name = "%s_profile"
|
||||
roles = ["${aws_iam_role.opsworks_instance.name}"]
|
||||
}`, rName, rInt, rInt, rInt, rName)
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
//// Checkers and Utilities
|
||||
////////////////////////////
|
||||
|
Loading…
Reference in New Issue
Block a user