lib/cloud_providers/ec2/ec2.rb in poolparty-1.6.8 vs lib/cloud_providers/ec2/ec2.rb in poolparty-1.6.9

- old
+ new

@@ -8,49 +8,51 @@ puts <<-EOM There was an error requiring AWS EOM end +require 'pp' + module CloudProviders class Ec2 < CloudProvider # Set the aws keys from the environment, or load from /etc/poolparty/env.yml if the environment variable is not set def self.default_access_key ENV['EC2_ACCESS_KEY'] || load_keys_from_file[:access_key] || load_keys_from_credential_file[:access_key] end - + def self.default_secret_access_key ENV['EC2_SECRET_KEY'] || load_keys_from_file[:secret_access_key] || load_keys_from_credential_file[:secret_access_key] end - + def self.default_private_key ENV['EC2_PRIVATE_KEY'] || load_keys_from_file[:private_key] end - + def self.default_cert ENV['EC2_CERT'] || load_keys_from_file[:cert] end - + def self.default_user_id ENV['EC2_USER_ID'] || load_keys_from_file[:user_id] end - + def self.default_ec2_url ENV['EC2_URL'] || load_keys_from_file[:ec2_url] end - + def self.default_s3_url ENV['S3_URL'] || load_keys_from_file[:s3_url] end - + def self.default_cloud_cert ENV['CLOUD_CERT'] || ENV['EUCALYPTUS_CERT'] || load_keys_from_file[:cloud_cert] end def self.default_credential_file ENV['AWS_CREDENTIAL_FILE'] || load_keys_from_file[:credential_file] end - + # Load the yaml file containing keys. If the file does not exist, return an empty hash def self.load_keys_from_file(filename="#{ENV["HOME"]}/.poolparty/aws", caching=true) return @aws_yml if @aws_yml && caching==true return {} unless File.exists?(filename) puts("Reading keys from file: #{filename}") @@ -69,12 +71,12 @@ @secret_access_key=$1.chomp end } return {:access_key => @access_key, :secret_access_key => @secret_access_key} end - - + + default_options( :instance_type => 'm1.small', :availability_zones => ["us-east-1a"], :user_id => default_user_id, :private_key => default_private_key, @@ -82,11 +84,11 @@ :cloud_cert => default_cloud_cert, :access_key => default_access_key, :secret_access_key => default_secret_access_key, :ec2_url => default_ec2_url, :s3_url => default_s3_url, - :credential_file => default_credential_file, + :credential_file => default_credential_file, :min_count => 1, :max_count => 1, :user_data => '', :kernel_id => nil, :ramdisk_id => nil, @@ -107,10 +109,11 @@ puts " for cloud: #{cloud.name}" puts " minimum_instances: #{minimum_instances}" puts " maximum_instances: #{maximum_instances}" puts " security_groups: #{security_group_names.join(", ")}" puts " using keypair: #{keypair}" + puts " with user_data #{user_data.to_s[0..100]}" puts " user: #{user}\n" security_groups.each do |sg| sg.run end @@ -150,44 +153,44 @@ else autoscalers.each do |a| puts " autoscaler: #{a.name}" puts "-----> The autoscaling groups will launch the instances" a.run - + progress_bar_until("Waiting for autoscaler to launch instances") do reset! running_nodes = nodes.select {|n| n.running? } running_nodes.size >= minimum_instances end reset! end end - - from_ports = security_groups.map {|a| a.authorizes.map {|t| t.from_port.to_i }.flatten }.flatten + + from_ports = security_groups.map {|a| a.authorizes.map {|t| t.from_port.to_i }.flatten }.flatten if from_ports.include?(22) progress_bar_until("Waiting for the instances to be accessible by ssh") do running_nodes = nodes.select {|n| n.running? } accessible_count = running_nodes.map do |node| node.accessible? end.size accessible_count == running_nodes.size end end - + assign_elastic_ips cleanup_ssh_known_hosts! puts "Attaching EBS volumes" assign_ebs_volumes # Assign EBS volumes end - + def teardown puts "------ Tearing down and cleaning up #{cloud.name} cloud" unless autoscalers.empty? puts "Tearing down autoscalers" end end - + def expand_by(num=1) e = Ec2Instance.run!({ :image_id => image_id, :min_count => num, :max_count => num, @@ -206,40 +209,40 @@ progress_bar_until("Waiting for node to launch...") do wait_for_node(e) end all_nodes.detect {|n| n.instance_id == e.instance_id } end - + def decoded_user_data if user_data if File.file?(user_data) open(user_data).read else user_data end end end - + def wait_for_node(instance) reset! inst = all_nodes.detect {|n| n.instance_id == instance.instance_id } inst.running? if inst end - + def contract_by(num=1) raise RuntimeError, "Contracting instances by #{num} will lower the number of instances below specified minimum" unless nodes.size - num > minimum_instances num.times do |i| node = nodes[-num] id = node.instance_id node.ssh_cleanup_known_hosts! Ec2Instance.terminate!(:instance_id => id, :cloud => cloud) end reset! end - + def bootstrap_nodes!(tmp_path=nil) - unless security_groups.map {|a| a.authorizes.map {|t| t.from_port.to_i }.flatten }.flatten.include?(22) + unless security_groups.map {|a| a.authorizes.map {|t| t.from_port.to_i }.flatten }.flatten.include?(22) warn "Cloud security_groups are not authorized for ssh. Cannot bootstrap." return end tmp_path ||= cloud.tmp_path nodes.each do |node| @@ -248,22 +251,22 @@ node.rsync_dir(tmp_path) node.bootstrap_chef! node.run_chef! end end - + def configure_nodes!(tmp_path=nil) # removed duplicated code (now configure_nodes! invokes # node.bootstrap_chef!, while old version did not, but I believe # this is harmless) - bootstrap_nodes!(tmp_path) + bootstrap_nodes!(tmp_path) ebs_volume_groups.each do |vol_grp| vol_grp.verify_attachments nodes end end - + def assign_elastic_ips unless elastic_ips.empty? unused_elastic_ip_addresses = ElasticIp.unused_elastic_ips(self).map {|i| i.public_ip } used_elastic_ip_addresses = ElasticIp.elastic_ips(self).map {|i| i.public_ip } @@ -297,15 +300,31 @@ end def nodes all_nodes.select {|i| i.in_service? }#describe_instances.select {|i| i.in_service? && security_groups.include?(i.security_groups) } end - + + # === Description + # + # Return all the security groups of the instance that are prefixed with #poolparty. + # + # These are special security groups used only for tagging + # + # === Parameters + # instance - An ec2 instance as returned from describe_instances + def tags instance + instance.groupSet.item.collect{|g| g.groupId }.select {|s| s.start_with? "#poolparty"} + end + def all_nodes - @nodes ||= describe_instances.select {|i| security_group_names.include?(i.security_groups) }.sort {|a,b| DateTime.parse(a.launchTime) <=> DateTime.parse(b.launchTime)} + @nodes ||= describe_instances.select { |i| + !(security_group_names & tags(i)).empty? + }.sort {|a,b| + DateTime.parse(a.launchTime) <=> DateTime.parse(b.launchTime) + } end - + # Describe instances # Describe the instances that are available on this cloud # @params id (optional) if present, details about the instance # with the id given will be returned # if not given, details for all instances will be returned @@ -316,19 +335,19 @@ inst_options = i.merge(r.merge(:cloud => cloud)).merge(cloud.cloud_provider.dsl_options) Ec2Instance.new(inst_options) end end.flatten rescue AWS::InvalidClientTokenId => e # AWS credentials invalid - puts "Error contacting AWS: #{e}" - raise e + puts "Error contacting AWS: #{e}" + raise e rescue Exception => e [] end end - + # Extras! - + def block_device_mapping(o=[], given_name=cloud.proper_name ) @mappings ||= o end def load_balancer(given_name=cloud.proper_name, o={}, &block) @@ -355,14 +374,14 @@ # Proxy to the raw Grempe amazon-aws @ec2 instance def ec2 @ec2 ||= begin AWS::EC2::Base.new( :access_key_id => access_key, :secret_access_key => secret_access_key ) rescue AWS::ArgumentError => e # AWS credentials missing? - puts "Error contacting AWS: #{e}" - raise e + puts "Error contacting AWS: #{e}" + raise e rescue Exception => e - puts "Generic error #{e.class}: #{e}" + puts "Generic error #{e.class}: #{e}" end end # Proxy to the raw Grempe amazon-aws autoscaling instance def as @@ -396,11 +415,11 @@ def ebs_volume_groups @ebs_volume_groups ||= [] end - # dsl method for EBS volumes. E.G.: + # dsl method for EBS volumes. E.G.: # ebs_volumes do # volumes "vol-001248ff", "vol-01ff4b85" # use existing volumes, not mandatory # device "/dev/sdf" # snapshot_id "snap-602030dd" # size 200 @@ -410,11 +429,11 @@ end def assign_ebs_volumes ebs_volume_groups.each{|ebs_volume_group| ebs_volume_group.attach(nodes)} end - + def rds_instances @rds_instances ||= [] end # Clear the cache @@ -445,16 +464,16 @@ end # Read credentials from credential_file if one exists def credential_file(file=nil) unless file.nil? - dsl_options[:credential_file]=file + dsl_options[:credential_file]=file dsl_options.merge!(Ec2.load_keys_from_credential_file(file)) else fetch(:credential_file) end end - + private # Helper to get the options with self as parent def sub_opts dsl_options.merge(:parent => self, :cloud => cloud) end