diff --git a/.cane b/.cane index e69de29b..74553dac 100644 --- a/.cane +++ b/.cane @@ -0,0 +1 @@ +--style-measure 100 \ No newline at end of file diff --git a/.tailor b/.tailor index 5ec14193..00496c0b 100644 --- a/.tailor +++ b/.tailor @@ -90,7 +90,7 @@ Tailor.config do |config| style.indentation_spaces 2, level: :error style.max_code_lines_in_class 300, level: :error style.max_code_lines_in_method 30, level: :error - style.max_line_length 80, level: :error + style.max_line_length 100, level: :error style.spaces_after_comma 1, level: :off style.spaces_after_lbrace 1, level: :error style.spaces_after_lbracket 0, level: :error diff --git a/Gemfile b/Gemfile index 7a4a3347..c84570e7 100644 --- a/Gemfile +++ b/Gemfile @@ -5,4 +5,5 @@ gemspec group :test do gem 'rake' + gem 'pry' end diff --git a/README.md b/README.md index 4b424ac1..9dbd05e8 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,17 @@ The SSH port number to be used when communicating with the instance. The default is `22`. +### < name="interface"> interface + +The place from which to derive the hostname for communicating with the instance. May be `dns`, `public` or `private`. If this is unset, the driver will derive the hostname by failing back in the following order: + +1. DNS Name +2. Public IP Address +3. Private IP Address + +The default is unset. + + ### region **Required** The AWS [region][region_docs] to use. diff --git a/kitchen-ec2.gemspec b/kitchen-ec2.gemspec index a72659c4..479624d5 100644 --- a/kitchen-ec2.gemspec +++ b/kitchen-ec2.gemspec @@ -21,6 +21,7 @@ Gem::Specification.new do |gem| gem.add_dependency 'test-kitchen', '~> 1.0' gem.add_dependency 'fog' + gem.add_development_dependency 'rspec' gem.add_development_dependency 'cane' gem.add_development_dependency 'tailor' gem.add_development_dependency 'countloc' diff --git a/lib/kitchen/driver/ec2.rb b/lib/kitchen/driver/ec2.rb index 202daaf0..336c0837 100644 --- a/lib/kitchen/driver/ec2.rb +++ b/lib/kitchen/driver/ec2.rb @@ -19,7 +19,6 @@ require 'benchmark' require 'json' require 'fog' - require 'kitchen' module Kitchen @@ -59,6 +58,8 @@ class Ec2 < Kitchen::Driver::SSHBase "https://ec2.#{driver[:region]}.amazonaws.com/" end + default_config :interface, nil + required_config :aws_access_key_id required_config :aws_secret_access_key required_config :aws_ssh_key_id @@ -69,11 +70,11 @@ def create(state) state[:server_id] = server.id info("EC2 instance <#{state[:server_id]}> created.") - server.wait_for { print "."; ready? } - print "(server ready)" + server.wait_for { print '.'; ready? } + print '(server ready)' state[:hostname] = hostname(server) wait_for_sshd(state[:hostname], config[:username]) - print "(ssh ready)\n" + print '(ssh ready)\n' debug("ec2:create '#{state[:hostname]}'") rescue Fog::Errors::Error, Excon::Errors::Error => ex raise ActionFailed, ex.message @@ -90,12 +91,12 @@ def destroy(state) end def default_ami - region = amis["regions"][config[:region]] + region = amis['regions'][config[:region]] region && region[instance.platform.name] end def default_username - amis["usernames"][instance.platform.name] || "root" + amis['usernames'][instance.platform.name] || 'root' end private @@ -146,8 +147,23 @@ def amis end end + def interface_types + { + 'dns' => 'dns_name', + 'public' => 'public_ip_address', + 'private' => 'private_ip_address' + } + end + def hostname(server) - server.dns_name || server.public_ip_address || server.private_ip_address + if config[:interface] + method = interface_types.fetch(config[:interface]) do + raise Kitchen::UserError, 'Invalid interface' + end + server.send(method) + else + server.dns_name || server.public_ip_address || server.private_ip_address + end end end end diff --git a/lib/kitchen/driver/ec2_version.rb b/lib/kitchen/driver/ec2_version.rb index 24210cd9..91303a83 100644 --- a/lib/kitchen/driver/ec2_version.rb +++ b/lib/kitchen/driver/ec2_version.rb @@ -21,6 +21,6 @@ module Kitchen module Driver # Version string for EC2 Test Kitchen driver - EC2_VERSION = "0.7.1.dev" + EC2_VERSION = '0.7.1.dev' end end diff --git a/spec/create_spec.rb b/spec/create_spec.rb new file mode 100644 index 00000000..f4ed3e6a --- /dev/null +++ b/spec/create_spec.rb @@ -0,0 +1,106 @@ +require 'kitchen/driver/ec2' +require 'kitchen/provisioner/dummy' + +describe Kitchen::Driver::Ec2 do + + let(:config) do + {} + end + + let(:state) do + {} + end + + let(:server) do + double(:id => "123", + :wait_for => nil, + :dns_name => "server.example.com", + :private_ip_address => '172.13.16.11', + :public_ip_address => '213.225.123.134') + end + + let(:instance) do + Kitchen::Instance.new(:platform => double(:name => "centos-6.4"), + :suite => double(:name => "default"), + :driver => driver, + :provisioner => Kitchen::Provisioner::Dummy.new({}), + :busser => double("busser"), + :state_file => double("state_file")) + end + + let(:driver) do + Kitchen::Driver::Ec2.new(config) + end + + before do + instance + allow(driver).to receive(:create_server).and_return(server) + allow(driver).to receive(:wait_for_sshd) + end + + context 'Interface is set in config' do + + it 'derives hostname from DNS when specified in the .kitchen.yml' do + config[:interface] = 'dns' + driver.create(state) + expect(state[:hostname]).to eql('server.example.com') + end + + it 'derives hostname from public interface when specified in the .kitchen.yml' do + config[:interface] = 'public' + driver.create(state) + expect(state[:hostname]).to eql('213.225.123.134') + end + + it 'derives hostname from private interface when specified in the .kitchen.yml' do + config[:interface] = 'private' + driver.create(state) + expect(state[:hostname]).to eql('172.13.16.11') + end + + it 'throws a nice exception if the config is bogus' do + config[:interface] = 'I am an idiot' + expect { driver.create(state) }.to raise_error(Kitchen::UserError, 'Invalid interface') + end + + end + + context 'Interface is derived automatically' do + + let(:server) do + double(:id => "123", + :wait_for => nil, + :dns_name => nil, + :private_ip_address => nil, + :public_ip_address => nil) + end + + it 'sets hostname to DNS value if DNS value exists' do + allow(server).to receive(:dns_name).and_return('server.example.com') + driver.create(state) + expect(state[:hostname]).to eql('server.example.com') + end + + it 'sets hostname to public value if public value exists' do + allow(server).to receive(:public_ip_address).and_return('213.225.123.134') + driver.create(state) + + expect(state[:hostname]).to eql('213.225.123.134') + end + + it 'sets hostname to private value if private value exists' do + allow(server).to receive(:private_ip_address).and_return('172.13.16.11') + driver.create(state) + expect(state[:hostname]).to eql('172.13.16.11') + end + + it 'sets hostname to DNS if one or more Public/Private values are set' do + allow(server).to receive(:dns_name).and_return('server.example.com') + allow(server).to receive(:public_ip_address).and_return('213.225.123.134') + allow(server).to receive(:private_ip_address).and_return('172.13.16.11') + driver.create(state) + expect(state[:hostname]).to eql('server.example.com') + end + + end +end