lib/ami_spec.rb in ami_spec-1.2.0 vs lib/ami_spec.rb in ami_spec-1.4.0

- old
+ new

@@ -1,12 +1,15 @@ require 'ami_spec/aws_instance' require 'ami_spec/aws_instance_options' +require 'ami_spec/aws_key_pair' +require 'ami_spec/aws_security_group' require 'ami_spec/server_spec' require 'ami_spec/server_spec_options' require 'ami_spec/wait_for_ssh' require 'ami_spec/wait_for_rc' require 'optimist' +require 'logger' module AmiSpec class InstanceConnectionTimeout < StandardError; end # == Parameters: # amis:: @@ -17,19 +20,21 @@ # A string of the directory to find ServerSpecs. # There should be a folder in this directory for each role found in ::amis # subnet_id:: # The subnet_id to start instances in. # key_name:: - # The SSH key name to assign to instances. This key name must exist on the executing host for passwordless login. + # The SSH key name to assign to instances. If not provided a temporary key pair will be generated in AWS # key_file:: # The SSH key file to use to connect to the host. # aws_region:: # AWS region to connect to # Defaults to AWS_DEFAULT_REGION # aws_security_group_ids:: # AWS Security groups to assign to the instances - # Defaults to the default security group for the VPC + # If not provided a temporary security group will be generated in AWS + # allow_any_temporary_security_group:: + # The temporary security group will allow SSH connections from any IP address (0.0.0.0/0) # aws_instance_type:: # AWS ec2 instance type # aws_public_ip:: # Should the instances get a public IP address # ssh_user:: @@ -43,10 +48,30 @@ # buildkite:: # Output section separators for buildkite # == Returns: # Boolean - The result of all the server specs. def self.run(options) + logger = Logger.new(STDOUT, formatter: proc { |_sev, _time, _name, message| "#{message}\n" }) + + ec2 = Aws::EC2::Resource.new(options[:aws_region] ? {region: options[:aws_region]} : {}) + + unless options[:key_name] + key_pair = AwsKeyPair.create(ec2: ec2, logger: logger) + options[:key_name] = key_pair.key_name + options[:key_file] = key_pair.key_file + end + + if options[:aws_security_groups].nil? || options[:aws_security_groups].empty? + temporary_security_group = AwsSecurityGroup.create( + ec2: ec2, + subnet_id: options[:subnet_id], + allow_any_ip: options[:allow_any_temporary_security_group], + logger: logger + ) + options[:aws_security_groups] = [temporary_security_group.group_id] + end + instances = [] options[:amis].each_pair do |role, ami| aws_instance_options = AwsInstanceOptions.new(options.merge(role: role, ami: ami)) instances << AwsInstance.start(aws_instance_options) end @@ -62,10 +87,12 @@ end results.all? ensure stop_instances(instances, options[:debug]) + key_pair.delete if key_pair + temporary_security_group.delete if temporary_security_group end def self.stop_instances(instances, debug) instances.each do |instance| begin @@ -88,17 +115,19 @@ opt :ami, "The ami ID to run tests against", type: :string opt :role_ami_file, "A file containing comma separated roles and amis. i.e.\nweb_server,ami-id.", type: :string opt :specs, "The directory to find ServerSpecs", type: :string, required: true opt :subnet_id, "The subnet to start the instance in", type: :string, required: true - opt :key_name, "The SSH key name to assign to instances", type: :string, required: true - opt :key_file, "The SSH private key file associated to the key_name", type: :string, required: true + opt :key_name, "The SSH key name to assign to instances. If not provided a temporary key pair will be generated in AWS", + type: :string + opt :key_file, "The SSH private key file associated to the key_name", type: :string opt :ssh_user, "The user to ssh to the instance as", type: :string, required: true opt :aws_region, "The AWS region, defaults to AWS_DEFAULT_REGION environment variable", type: :string opt :aws_instance_type, "The ec2 instance type, defaults to t2.micro", type: :string, default: 't2.micro' - opt :aws_security_groups, "Security groups to associate to the launched instances. May be specified multiple times", + opt :aws_security_groups, "Security groups to associate to the launched instances. May be specified multiple times. If not provided a temporary security group will be generated in AWS", type: :string, default: nil, multi: true + opt :allow_any_temporary_security_group, "The temporary security group will allow SSH connections from any IP address (0.0.0.0/0)" opt :aws_public_ip, "Launch instances with a public IP" opt :ssh_retries, "The number of times we should try sshing to the ec2 instance before giving up. Defaults to 30", type: :int, default: 30 opt :tags, "Additional tags to add to launched instances in the form of comma separated key=value pairs. i.e. Name=AmiSpec", type: :string, default: "" opt :debug, "Don't terminate instances on exit" @@ -119,18 +148,22 @@ options.delete(:role_ami_file) else fail "You must specify either role and ami or role_ami_file" end - unless File.exist? options[:key_file] - fail "Key file #{options[:key_file]} not found" - end + fail "Key file #{options[:key_file]} not found" if options[:key_name] && !File.exist?(options.fetch(:key_file)) if options[:user_data_file] and !File.exist? options[:user_data_file] fail "User Data file #{options[:user_data_file]} not found" end options[:tags] = parse_tags(options[:tags]) + + options[:amis].each_pair do |role, _| + unless Dir.exist?("#{options[:specs]}/#{role}") + fail "Role directory #{options[:specs]}/#{role} does not exist. If you'd like to skip the role '#{role}', create the directory but leave it empty (except for a .gitignore file)." + end + end exit run(options) end def self.parse_tags(tags)