diff --git a/README.md b/README.md index ea65c918..d3a53da8 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ token) through config. See the `aws_access_key_id` and `aws_secret_access_key` config sections below to see how to specify these in your .kitchen.yml or through environment variables. If you would like to specify your session token use the environment variable `AWS_SESSION_TOKEN`. -1. The shared credentials ini file at `~/.aws/credentials`. You can specify +1. The shared credentials ini file at `~/.aws/credentials`. You can specify multiple profiles in this file and select one with the `AWS_PROFILE` environment variable or the `shared_credentials_profile` driver config. Read [this][credentials_docs] for more information. @@ -89,7 +89,7 @@ environment variable or the `shared_credentials_profile` driver config. Read metadata service to discover the local instance's IAM instance profile. This precedence order is taken from http://docs.aws.amazon.com/sdkforruby/api/index.html#Configuration - + The first method attempted that works will be used. IE, if you want to auth using the instance profile, you must not set any of the access key configs or environment variables, and you must not specify a `~/.aws/credentials` @@ -97,7 +97,7 @@ file. Because the Test Kitchen test should be checked into source control and ran through CI we no longer recommend storing the AWS credentials in the -`.kitchen.yml` file. Instead, specify them as environment variables or in the +`.kitchen.yml` file. Instead, specify them as environment variables or in the `~/.aws/credentials` file. ## Windows Configuration @@ -186,6 +186,18 @@ The default will be determined by the `aws_region` chosen and the Platform name, if a default exists (see [amis.json][ami_json]). If a default cannot be computed, then the default is `nil`. +### image\_search + +Searches the EC2 API for the latest AMI ID that matches the given [filters](http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeImages.html). + +For example, a search for the latest AMI that matches a given image name looks +like: + +```yaml +image_search: + name: Windows_Server-2012-R2_RTM-English-64Bit-Base-* +``` + ### region **Required** The AWS [region][region_docs] to use. diff --git a/lib/kitchen/driver/ec2.rb b/lib/kitchen/driver/ec2.rb index e795bb82..de40af2b 100644 --- a/lib/kitchen/driver/ec2.rb +++ b/lib/kitchen/driver/ec2.rb @@ -237,6 +237,17 @@ def destroy(state) state.delete(:hostname) end + def lookup_ami(filters_hash) + filters = [] + filters_hash.each do |key, value| + filters.push(:name => key.to_s, :values => Array(value)) + end + images = ec2.resource.images(:filters => filters).sort do |ami1, ami2| + Time.parse(ami1.creation_date) <=> Time.parse(ami2.creation_date) + end + images.last && images.last.id + end + def ubuntu_ami(region, platform_name) release = amis["ubuntu_releases"][platform_name] Ubuntu.release(release).amis.find do |ami| @@ -251,6 +262,8 @@ def default_ami if instance.platform.name.start_with?("ubuntu") ami = ubuntu_ami(config[:region], instance.platform.name) ami && ami.name + elsif !config[:image_search].nil? + lookup_ami(config[:image_search]) else region = amis["regions"][config[:region]] region && region[instance.platform.name] diff --git a/spec/kitchen/driver/ec2_spec.rb b/spec/kitchen/driver/ec2_spec.rb index 4edb813e..a0a1e05c 100644 --- a/spec/kitchen/driver/ec2_spec.rb +++ b/spec/kitchen/driver/ec2_spec.rb @@ -424,6 +424,50 @@ end end + describe "#lookup_ami" do + let(:resource) { double("actual resource") } + + let(:filters) { { :name => "ami_name" } } + let(:aws_filters) do + { :filters => [{ :name => "name", :values => ["ami_name"] }] } + end + + before do + allow(client).to receive(:resource).and_return(resource) + end + + context "when one ami found" do + let(:ami_id) { "ami-a1b2c3d4" } + let(:date) { "2015-06-10T03:47:20.000Z" } + let(:ami_list) { [double("ami", :id => ami_id, :creation_date => date)] } + + it "returns the ami id" do + expect(resource).to receive(:images).with(aws_filters). \ + and_return(ami_list) + expect(driver.lookup_ami(filters)).to eq(ami_id) + end + end + + context "when more than one ami found" do + let(:date_older) { "2015-06-10T03:47:20.000Z" } + let(:date_newer) { "2015-06-11T03:47:20.000Z" } + let(:ami_id_older) { "ami-a1b2c3d4" } + let(:ami_id_newer) { "ami-e5f6g7h8" } + let(:ami_list) do + [ + double("ami", :id => ami_id_older, :creation_date => date_older), + double("ami", :id => ami_id_newer, :creation_date => date_newer) + ] + end + + it "returns most recently created ami id" do + expect(resource).to receive(:images).with(aws_filters). \ + and_return(ami_list) + expect(driver.lookup_ami(filters)).to eq(ami_id_newer) + end + end + end + describe "#default_ami" do context "when platform is ubuntu" do let(:config) { { :aws_ssh_key_id => "key" } } @@ -436,6 +480,17 @@ expect(driver.default_ami).to eq(ami_data[0]) end end + + context "when ami_search is provided" do + let(:config) { { :image_search => {} } } + let(:ami_id) { "ami-xxxxxxxx" } + + it "searches for an image id" do + expect(driver).to receive(:lookup_ami).with(config[:image_search]). \ + and_return(ami_id) + expect(driver.default_ami).to eq(ami_id) + end + end end end