lib/kitchen/driver/ec2.rb in kitchen-ec2-1.2.0 vs lib/kitchen/driver/ec2.rb in kitchen-ec2-1.3.0

- old
+ new

@@ -79,11 +79,13 @@ default_config :username, nil default_config :associate_public_ip, nil default_config :interface, nil default_config :http_proxy, ENV["HTTPS_PROXY"] || ENV["HTTP_PROXY"] default_config :retry_limit, 3 + default_config :tenancy, "default" default_config :instance_initiated_shutdown_behavior, nil + default_config :ssl_verify_peer, true def initialize(*args, &block) super # AWS Ruby SDK loading isn't thread safe, so as soon as we know we're # going to use EC2, autoload it. Seems to have been fixed in Ruby 2.3+ @@ -192,20 +194,24 @@ # See https://github.com/aws/aws-sdk-ruby/issues/859 # Tagging can fail with a NotFound error even though we waited until the server exists # Waiting can also fail, so we have to also retry on that. If it means we re-tag the # instance, so be it. + # Tagging an instance is possible before volumes are attached. Tagging the volumes after + # instance creation is consistent. Retryable.retryable( :tries => 10, :sleep => lambda { |n| [2**n, 30].min }, :on => ::Aws::EC2::Errors::InvalidInstanceIDNotFound ) do |r, _| info("Attempting to tag the instance, #{r} retries") tag_server(server) state[:server_id] = server.id info("EC2 instance <#{state[:server_id]}> created.") + wait_until_volumes_ready(server, state) + tag_volumes(server) wait_until_ready(server, state) end if windows_os? && instance.transport[:username] =~ /administrator/i && @@ -294,13 +300,18 @@ search_platform.find_image(image_search) end end def update_username(state) - # TODO: if the user explicitly specified the transport's default username, - # do NOT overwrite it! - if instance.transport[:username] == instance.transport.class.defaults[:username] + # BUG: With the following equality condition on username, if the user specifies 'root' + # as the transport's username then we will overwrite that value with one from the standard + # platform definitions. This seems difficult to handle here as the default username is + # provided by the underlying transport classes, and is often non-nil (eg; 'root'), leaving + # us no way to distinguish a user-set value from the transport's default. + # See https://github.com/test-kitchen/kitchen-ec2/pull/273 + if actual_platform && + instance.transport[:username] == instance.transport.class.defaults[:username] debug("No SSH username specified: using default username #{actual_platform.username} " \ " for image #{config[:image_id]}, which we detected as #{actual_platform}.") state[:username] = actual_platform.username end end @@ -311,11 +322,12 @@ config[:shared_credentials_profile], config[:aws_access_key_id], config[:aws_secret_access_key], config[:aws_session_token], config[:http_proxy], - config[:retry_limit] + config[:retry_limit], + config[:ssl_verify_peer] ) end def instance_generator @instance_generator ||= Aws::InstanceGenerator.new(config, ec2, instance.logger) @@ -354,13 +366,15 @@ end ec2.get_instance_from_spot_request(spot_request_id) end def create_spot_request + request_duration = config[:retryable_tries] * config[:retryable_sleep] request_data = { :spot_price => config[:spot_price].to_s, - :launch_specification => instance_generator.ec2_instance_data + :launch_specification => instance_generator.ec2_instance_data, + :valid_until => Time.now + request_duration } if config[:block_duration_minutes] request_data[:block_duration_minutes] = config[:block_duration_minutes] end @@ -376,10 +390,38 @@ end server.create_tags(:tags => tags) end end + def tag_volumes(server) + tags = [] + config[:tags].each do |k, v| + tags << { :key => k, :value => v } + end + server.volumes.each do |volume| + volume.create_tags(:tags => tags) + end + end + + # Compares the requested volume count vs what has actually been set to be + # attached to the instance. The information requested through + # ec2.client.described_volumes is updated before the instance volume + # information. + def wait_until_volumes_ready(server, state) + wait_with_destroy(server, state, "volumes to be ready") do |aws_instance| + described_volume_count = 0 + ready_volume_count = 0 + if aws_instance.exists? + described_volume_count = ec2.client.describe_volumes(:filters => [ + { :name => "attachment.instance-id", :values => ["#{state[:server_id]}"] }] + ).volumes.length + aws_instance.volumes.each { ready_volume_count += 1 } + end + (described_volume_count > 0) && (described_volume_count == ready_volume_count) + end + end + # Normally we could use `server.wait_until_running` but we actually need # to check more than just the instance state def wait_until_ready(server, state) wait_with_destroy(server, state, "to become ready") do |aws_instance| hostname = hostname(aws_instance, config[:interface]) @@ -511,13 +553,18 @@ "Adding $username to Administrators" >> $logfile & net.exe localgroup Administrators /add $username >> $logfile EOH end + if actual_platform.version =~ /2016/ + logfile_name = 'C:\\ProgramData\\Amazon\\EC2-Windows\\Launch\\Log\\kitchen-ec2.log' + else + logfile_name = 'C:\\Program Files\\Amazon\\Ec2ConfigService\\Logs\\kitchen-ec2.log' + end # Returning the fully constructed PowerShell script to user_data Kitchen::Util.outdent!(<<-EOH) <powershell> - $logfile="C:\\Program Files\\Amazon\\Ec2ConfigService\\Logs\\kitchen-ec2.log" + $logfile=#{logfile_name} # Allow script execution Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force #PS Remoting and & winrm.cmd basic config Enable-PSRemoting -Force -SkipNetworkProfileCheck & winrm.cmd set winrm/config '@{MaxTimeoutms="1800000"}' >> $logfile