lib/rubber/recipes/rubber/instances.rb in axtro-rubber-1.0.2.8 vs lib/rubber/recipes/rubber/instances.rb in axtro-rubber-1.2.0

- old
+ new

@@ -3,11 +3,17 @@ desc <<-DESC Create a new EC2 instance with the given ALIAS and ROLES DESC required_task :create do instance_alias = get_env('ALIAS', "Instance alias (e.g. web01)", true) - r = get_env('ROLES', "Instance roles (e.g. web,app,db:primary=true)", true) + + env = rubber_cfg.environment.bind(nil, instance_alias) + default_roles = env.instance_roles + r = get_env("ROLES", "Instance roles (e.g. web,app,db:primary=true)", true, default_roles) + + create_spot_instance = ENV.delete("SPOT_INSTANCE") || env.cloud_providers[env.cloud_provider].spot_instance + if r == '*' instance_roles = rubber_cfg.environment.known_roles instance_roles = instance_roles.collect {|role| role == "db" ? "db:primary=true" : role } else instance_roles = r.split(",") @@ -27,11 +33,11 @@ end # Add in roles that the given set of roles depends on ir = Rubber::Configuration::RoleItem.expand_role_dependencies(ir, get_role_dependencies) - create_instance(instance_alias, ir) + create_instance(instance_alias, ir, create_spot_instance) end desc <<-DESC Refresh the host data for a EC2 instance with the given ALIAS. This is useful to run when rubber:create fails after instance creation @@ -81,10 +87,14 @@ instance = rubber_instances[instance_alias] fatal "Instance does not exist: #{instance_alias}" unless instance instance.roles = (instance.roles + ir).uniq rubber_instances.save() + logger.info "Roles for #{instance_alias} are now:" + logger.info instance.role_names.sort.join("\n") + logger.info '' + logger.info "Run 'cap rubber:bootstrap' if done adding roles" end desc <<-DESC Removes the given ROLES from the instance named ALIAS DESC @@ -103,10 +113,12 @@ instance = rubber_instances[instance_alias] fatal "Instance does not exist: #{instance_alias}" unless instance instance.roles = (instance.roles - ir).uniq rubber_instances.save() + logger.info "Roles for #{instance_alias} are now:" + logger.info instance.role_names.sort.join("\n") end desc <<-DESC List all your EC2 instances DESC @@ -142,11 +154,11 @@ set :print_ip_command, "ifconfig eth0 | awk 'NR==2 {print $2}' | awk -F: '{print $2}'" # Creates a new ec2 instance with the given alias and roles # Configures aliases (/etc/hosts) on local and remote machines - def create_instance(instance_alias, instance_roles) + def create_instance(instance_alias, instance_roles, create_spot_instance=false) fatal "Instance already exists: #{instance_alias}" if rubber_instances[instance_alias] role_names = instance_roles.collect{|x| x.name} env = rubber_cfg.environment.bind(role_names, instance_alias) @@ -155,16 +167,49 @@ security_groups = get_assigned_security_groups(instance_alias, role_names) ami = env.cloud_providers[env.cloud_provider].image_id ami_type = env.cloud_providers[env.cloud_provider].image_type availability_zone = env.availability_zone - logger.info "Creating instance #{ami}/#{ami_type}/#{security_groups.join(',') rescue 'Default'}/#{availability_zone || 'Default'}" - instance_id = cloud.create_instance(ami, ami_type, security_groups, availability_zone) + if create_spot_instance + spot_price = env.cloud_providers[env.cloud_provider].spot_price.to_s + + logger.info "Creating spot instance request for instance #{ami}/#{ami_type}/#{security_groups.join(',') rescue 'Default'}/#{availability_zone || 'Default'}" + request_id = cloud.create_spot_instance_request(spot_price, ami, ami_type, security_groups, availability_zone) + + print "Waiting for spot instance request to be fulfilled" + max_wait_time = env.cloud_providers[env.cloud_provider].spot_instance_request_timeout || (1.0 / 0) # Use the specified timeout value or default to infinite. + instance_id = nil + while instance_id.nil? do + print "." + sleep 2 + max_wait_time -= 2 + + request = cloud.describe_spot_instance_requests(request_id).first + instance_id = request[:instance_id] + + if max_wait_time < 0 && instance_id.nil? + cloud.destroy_spot_instance_request(request[:id]) + + print "\n" + print "Failed to fulfill spot instance in the time specified. Falling back to on-demand instance creation." + break + end + end + + print "\n" + end + + if !create_spot_instance || (create_spot_instance && max_wait_time < 0) + logger.info "Creating instance #{ami}/#{ami_type}/#{security_groups.join(',') rescue 'Default'}/#{availability_zone || 'Default'}" + instance_id = cloud.create_instance(ami, ami_type, security_groups, availability_zone) + end + logger.info "Instance #{instance_id} created" instance_item = Rubber::Configuration::InstanceItem.new(instance_alias, env.domain, instance_roles, instance_id, security_groups) + instance_item.spot_instance_request_id = request_id if create_spot_instance rubber_instances.add(instance_item) rubber_instances.save() print "Waiting for instance to start" @@ -178,10 +223,11 @@ logger.info "Instance running, fetching hostname/ip data" instance_item.external_host = instance[:external_host] instance_item.external_ip = instance[:external_ip] instance_item.internal_host = instance[:internal_host] instance_item.zone = instance[:zone] + instance_item.platform = instance[:platform] rubber_instances.save() # setup amazon elastic ips if configured to do so setup_static_ips @@ -194,10 +240,17 @@ # Connect to newly created instance and grab its internal ip # so that we can update all aliases task :_get_ip, :hosts => instance_item.external_ip do - instance_item.internal_ip = capture(print_ip_command).strip + # There's no good way to get the internal IP for a Windows host, so just set it to the external + # and let the router handle mapping to the internal network. + if instance_item.windows? + instance_item.internal_ip = instance_item.external_ip + else + instance_item.internal_ip = capture(print_ip_command).strip + end + rubber_instances.save() end # even though instance is running, sometimes ssh hasn't started yet, # so retry on connect failure