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)