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

Allow Data Source for AWS instances to get instances that are in a state other than running #4950

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion aws/data_source_aws_instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
)

func dataSourceAwsInstances() *schema.Resource {
Expand All @@ -17,6 +18,21 @@ func dataSourceAwsInstances() *schema.Resource {
Schema: map[string]*schema.Schema{
"filter": dataSourceFiltersSchema(),
"instance_tags": tagsSchemaComputed(),
"instance_state_names": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice([]string{
ec2.InstanceStateNamePending,
ec2.InstanceStateNameRunning,
ec2.InstanceStateNameShuttingDown,
ec2.InstanceStateNameStopped,
ec2.InstanceStateNameStopping,
ec2.InstanceStateNameTerminated,
}, false),
},
},

"ids": {
Type: schema.TypeList,
Expand Down Expand Up @@ -47,14 +63,19 @@ func dataSourceAwsInstancesRead(d *schema.ResourceData, meta interface{}) error
return fmt.Errorf("One of filters or instance_tags must be assigned")
}

instanceStateNames := []*string{aws.String(ec2.InstanceStateNameRunning)}
if v, ok := d.GetOk("instance_state_names"); ok && len(v.(*schema.Set).List()) > 0 {
instanceStateNames = expandStringSet(v.(*schema.Set))
}
params := &ec2.DescribeInstancesInput{
Filters: []*ec2.Filter{
&ec2.Filter{
Name: aws.String("instance-state-name"),
Values: []*string{aws.String("running")},
Copy link
Contributor

Choose a reason for hiding this comment

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

The enhancement here needs to allow the instance-state to either be configurable, while leaving the existing default behavior to not break backwards compatibility. This can be accomplished two ways:

Less preferably as a string attribute, but provided as an example, e.g.

"instance_state_name": {
  Type: schema.TypeString,
  Optional: true,
  Default: ec2.InstanceStateNameRunning,
  ValidateFunc: validation.StringInSlice([]string{
    ec2.InstanceStateNamePending,
    ec2.InstanceStateNameRunning,
    ec2.InstanceStateNameShuttingDown,
    ec2.InstanceStateNameStopped,
    ec2.InstanceStateNameStopping,
    ec2.InstanceStateNameTerminated,
  }, false),
},

Where it is referenced in the code with:

	params := &ec2.DescribeInstancesInput{
		Filters: []*ec2.Filter{
			&ec2.Filter{
				Name: aws.String("instance-state-name"),
				Values: []*string{aws.String(d.Get("instance_state_name").(string))},
			},
		},
	}

Or, more preferably, as a set so you can configure one or more instance states:

"instance_state_names": {
  Type: schema.TypeSet,
  Optional: true,
  Elem: schema.Schema{
    Type: schema.TypeString,
    ValidateFunc: validation.StringInSlice([]string{
      ec2.InstanceStateNamePending,
      ec2.InstanceStateNameRunning,
      ec2.InstanceStateNameShuttingDown,
      ec2.InstanceStateNameStopped,
      ec2.InstanceStateNameStopping,
      ec2.InstanceStateNameTerminated,
    }, false),
  },
},

Where it is referenced in the code with:

	instanceStateNames := []*string{aws.String(ec2.InstanceStateNameRunning)}
	if v, ok := d.GetOk("instance_state_names"); ok && len(v.(*schema.Set).List()) > 0 {
		instanceStateNames = expandStringSet(v.(*schema.Set))
	}
	params := &ec2.DescribeInstancesInput{
		Filters: []*ec2.Filter{
			&ec2.Filter{
				Name: aws.String("instance-state-name"),
				Values: instanceStateNames,
			},
		},
	}

Either case should implement a new acceptance test that creates two instances and immediately calls the data source that accepts the pending (and probably running as well) instance states.

Values: instanceStateNames,
},
},
}

if filtersOk {
params.Filters = append(params.Filters,
buildAwsDataSourceFilters(filters.(*schema.Set))...)
Expand Down
54 changes: 54 additions & 0 deletions aws/data_source_aws_instances_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,22 @@ func TestAccAWSInstancesDataSource_tags(t *testing.T) {
})
}

func TestAccAWSInstancesDataSource_instance_state_names(t *testing.T) {
rInt := acctest.RandInt()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccInstancesDataSourceConfig_instance_state_names(rInt),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.aws_instances.test", "ids.#", "2"),
),
},
},
})
}

const testAccInstancesDataSourceConfig_ids = `
data "aws_ami" "ubuntu" {
most_recent = true
Expand Down Expand Up @@ -113,3 +129,41 @@ data "aws_instances" "test" {
}
`, rInt)
}

func testAccInstancesDataSourceConfig_instance_state_names(rInt int) string {
return fmt.Sprintf(`
data "aws_ami" "ubuntu" {
most_recent = true

filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-*"]
}

filter {
name = "virtualization-type"
values = ["hvm"]
}

owners = ["099720109477"] # Canonical
}

resource "aws_instance" "test" {
count = 2
ami = "${data.aws_ami.ubuntu.id}"
instance_type = "t2.micro"
tags {
Name = "TfAccTest-HelloWorld"
TestSeed = "%[1]d"
}
}

data "aws_instances" "test" {
instance_tags {
Name = "*"
Copy link
Contributor

Choose a reason for hiding this comment

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

To properly setup the resource and data source ordering, you can reference the aws_instance resources created above 😄

Name = "${aws_instance.test.0.tags["Name"]}"

This should be enough to delay the data source to catch the two new instances 👍 Let me know if this still is causing an issue and we can dive in deeper. Thanks for your work here!

}

instance_state_names = [ "pending", "running" ]
}
`, rInt)
}
4 changes: 4 additions & 0 deletions website/docs/d/instances.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ data "aws_instances" "test" {
name = "instance.group-id"
values = ["sg-12345678"]
}

instance_state_names = [ "running", "stopped" ]
}

resource "aws_eip" "test" {
Expand All @@ -45,6 +47,8 @@ resource "aws_eip" "test" {
* `instance_tags` - (Optional) A mapping of tags, each pair of which must
exactly match a pair on desired instances.

* `instance_state_names` - (Optional) A list of instance states that should be applicable to the desired instances. The permitted values are: `pending, running, shutting-down, stopped, stopping, terminated`
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 list the default behavior of returning only running here 👍


* `filter` - (Optional) One or more name/value pairs to use as filters. There are
several valid keys, for a full reference, check out
[describe-instances in the AWS CLI reference][1].
Expand Down