Class: Sumo
- Inherits:
-
Object
- Object
- Sumo
- Defined in:
- lib/sumo.rb
Instance Method Summary collapse
- #attach(volume, instance, device) ⇒ Object
- #attached_volumes ⇒ Object
- #available_volumes ⇒ Object
- #bootstrap_chef(hostname) ⇒ Object
- #config ⇒ Object
- #console_output(instance_id) ⇒ Object
- #create_keypair ⇒ Object
- #create_security_group ⇒ Object
- #create_volume(size) ⇒ Object
- #default_config ⇒ Object
- #destroy_volume(volume) ⇒ Object
- #detach(volume) ⇒ Object
- #ec2 ⇒ Object
- #fetch_list ⇒ Object
- #fetch_resources(hostname) ⇒ Object
- #find(id_or_hostname) ⇒ Object
- #find_volume(volume_id) ⇒ Object
- #instance_info(instance_id) ⇒ Object
- #keypair_file ⇒ Object
- #launch ⇒ Object
- #list ⇒ Object
- #list_by_status(status) ⇒ Object
- #nondestroyed_volumes ⇒ Object
- #open_firewall(port) ⇒ Object
- #parse_resources(raw, hostname) ⇒ Object
- #pending ⇒ Object
- #read_config ⇒ Object
- #resources(hostname) ⇒ Object
- #running ⇒ Object
- #setup_role(hostname, role) ⇒ Object
- #ssh(hostname, cmds) ⇒ Object
- #sumo_dir ⇒ Object
- #terminate(instance_id) ⇒ Object
- #volumes ⇒ Object
- #wait_for_hostname(instance_id) ⇒ Object
- #wait_for_ssh(hostname) ⇒ Object
Instance Method Details
#attach(volume, instance, device) ⇒ Object
56 57 58 59 60 61 62 63 |
# File 'lib/sumo.rb', line 56 def attach(volume, instance, device) result = ec2.attach_volume( :volume_id => volume, :instance_id => instance, :device => device ) "done" end |
#attached_volumes ⇒ Object
48 49 50 |
# File 'lib/sumo.rb', line 48 def attached_volumes volumes.select { |vol| vol[:status] == 'in-use' } end |
#available_volumes ⇒ Object
44 45 46 |
# File 'lib/sumo.rb', line 44 def available_volumes volumes.select { |vol| vol[:status] == 'available' } end |
#bootstrap_chef(hostname) ⇒ Object
162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/sumo.rb', line 162 def bootstrap_chef(hostname) commands = [ 'apt-get update', 'apt-get autoremove -y', 'apt-get install -y ruby ruby-dev rubygems git-core', 'gem sources -a http://gems.opscode.com', 'gem install chef ohai --no-rdoc --no-ri', "git clone #{config['cookbooks_url']}", ] ssh(hostname, commands) end |
#config ⇒ Object
217 218 219 |
# File 'lib/sumo.rb', line 217 def config @config ||= default_config.merge read_config end |
#console_output(instance_id) ⇒ Object
213 214 215 |
# File 'lib/sumo.rb', line 213 def console_output(instance_id) ec2.get_console_output(:instance_id => instance_id)["output"] end |
#create_keypair ⇒ Object
243 244 245 246 247 |
# File 'lib/sumo.rb', line 243 def create_keypair keypair = ec2.create_keypair(:key_name => "sumo").keyMaterial File.open(keypair_file, 'w') { |f| f.write keypair } File.chmod 0600, keypair_file end |
#create_security_group ⇒ Object
249 250 251 252 |
# File 'lib/sumo.rb', line 249 def create_security_group ec2.create_security_group(:group_name => 'sumo', :group_description => 'Sumo') rescue AWS::InvalidGroupDuplicate end |
#create_volume(size) ⇒ Object
70 71 72 73 74 75 76 |
# File 'lib/sumo.rb', line 70 def create_volume(size) result = ec2.create_volume( :availability_zone => config['availability_zone'], :size => size.to_s ) result["volumeId"] end |
#default_config ⇒ Object
221 222 223 224 225 226 227 |
# File 'lib/sumo.rb', line 221 def default_config { 'user' => 'root', 'ami' => 'ami-ed46a784', 'availability_zone' => 'us-east-1b' } end |
#destroy_volume(volume) ⇒ Object
78 79 80 81 |
# File 'lib/sumo.rb', line 78 def destroy_volume(volume) ec2.delete_volume(:volume_id => volume) "done" end |
#detach(volume) ⇒ Object
65 66 67 68 |
# File 'lib/sumo.rb', line 65 def detach(volume) result = ec2.detach_volume(:volume_id => volume, :force => "true") "done" end |
#ec2 ⇒ Object
265 266 267 |
# File 'lib/sumo.rb', line 265 def ec2 @ec2 ||= AWS::EC2::Base.new(:access_key_id => config['access_id'], :secret_access_key => config['access_secret']) end |
#fetch_list ⇒ Object
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/sumo.rb', line 83 def fetch_list result = ec2.describe_instances return [] unless result.reservationSet instances = [] result.reservationSet.item.each do |r| r.instancesSet.item.each do |item| instances << { :instance_id => item.instanceId, :status => item.instanceState.name, :hostname => item.dnsName } end end instances end |
#fetch_resources(hostname) ⇒ Object
196 197 198 199 200 201 |
# File 'lib/sumo.rb', line 196 def fetch_resources(hostname) cmd = "ssh -i #{keypair_file} #{config['user']}@#{hostname} 'cat /root/resources' 2>&1" out = IO.popen(cmd, 'r') { |pipe| pipe.read } abort "failed to read resources, output:\n#{out}" unless $?.success? parse_resources(out, hostname) end |
#find(id_or_hostname) ⇒ Object
100 101 102 103 104 105 106 107 108 |
# File 'lib/sumo.rb', line 100 def find(id_or_hostname) return unless id_or_hostname id_or_hostname = id_or_hostname.strip.downcase list.detect do |inst| inst[:hostname] == id_or_hostname or inst[:instance_id] == id_or_hostname or inst[:instance_id].gsub(/^i-/, '') == id_or_hostname end end |
#find_volume(volume_id) ⇒ Object
110 111 112 113 114 115 116 117 |
# File 'lib/sumo.rb', line 110 def find_volume(volume_id) return unless volume_id volume_id = volume_id.strip.downcase volumes.detect do |volume| volume[:volume_id] == volume_id or volume[:volume_id].gsub(/^vol-/, '') == volume_id end end |
#instance_info(instance_id) ⇒ Object
131 132 133 134 135 |
# File 'lib/sumo.rb', line 131 def instance_info(instance_id) fetch_list.detect do |inst| inst[:instance_id] == instance_id end end |
#keypair_file ⇒ Object
239 240 241 |
# File 'lib/sumo.rb', line 239 def keypair_file "#{sumo_dir}/keypair.pem" end |
#launch ⇒ Object
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/sumo.rb', line 6 def launch ami = config['ami'] raise "No AMI selected" unless ami create_keypair unless File.exists? keypair_file create_security_group open_firewall(22) result = ec2.run_instances( :image_id => ami, :instance_type => config['instance_size'] || 'm1.small', :key_name => 'sumo', :group_id => [ 'sumo' ], :availability_zone => config['availability_zone'] ) result.instancesSet.item[0].instanceId end |
#list ⇒ Object
25 26 27 |
# File 'lib/sumo.rb', line 25 def list @list ||= fetch_list end |
#list_by_status(status) ⇒ Object
127 128 129 |
# File 'lib/sumo.rb', line 127 def list_by_status(status) list.select { |i| i[:status] == status } end |
#nondestroyed_volumes ⇒ Object
52 53 54 |
# File 'lib/sumo.rb', line 52 def nondestroyed_volumes volumes.select { |vol| vol[:status] != 'deleting' } end |
#open_firewall(port) ⇒ Object
254 255 256 257 258 259 260 261 262 263 |
# File 'lib/sumo.rb', line 254 def open_firewall(port) ec2.( :group_name => 'sumo', :ip_protocol => 'tcp', :from_port => port, :to_port => port, :cidr_ip => '0.0.0.0/0' ) rescue AWS::InvalidPermissionDuplicate end |
#parse_resources(raw, hostname) ⇒ Object
203 204 205 206 207 |
# File 'lib/sumo.rb', line 203 def parse_resources(raw, hostname) raw.split("\n").map do |line| line.gsub(/localhost/, hostname) end end |
#pending ⇒ Object
123 124 125 |
# File 'lib/sumo.rb', line 123 def pending list_by_status('pending') end |
#read_config ⇒ Object
233 234 235 236 237 |
# File 'lib/sumo.rb', line 233 def read_config YAML.load File.read("#{sumo_dir}/config.yml") rescue Errno::ENOENT raise "Sumo is not configured, please fill in ~/.sumo/config.yml" end |
#resources(hostname) ⇒ Object
191 192 193 194 |
# File 'lib/sumo.rb', line 191 def resources(hostname) @resources ||= {} @resources[hostname] ||= fetch_resources(hostname) end |
#running ⇒ Object
119 120 121 |
# File 'lib/sumo.rb', line 119 def running list_by_status('running') end |
#setup_role(hostname, role) ⇒ Object
174 175 176 177 178 179 180 |
# File 'lib/sumo.rb', line 174 def setup_role(hostname, role) commands = [ "cd chef-cookbooks", "/var/lib/gems/1.8/bin/chef-solo -c config.json -j roles/#{role}.json" ] ssh(hostname, commands) end |
#ssh(hostname, cmds) ⇒ Object
182 183 184 185 186 187 188 189 |
# File 'lib/sumo.rb', line 182 def ssh(hostname, cmds) IO.popen("ssh -i #{keypair_file} #{config['user']}@#{hostname} > ~/.sumo/ssh.log 2>&1", "w") do |pipe| pipe.puts cmds.join(' && ') end unless $?.success? abort "failed\nCheck ~/.sumo/ssh.log for the output" end end |
#sumo_dir ⇒ Object
229 230 231 |
# File 'lib/sumo.rb', line 229 def sumo_dir "#{ENV['HOME']}/.sumo" end |
#terminate(instance_id) ⇒ Object
209 210 211 |
# File 'lib/sumo.rb', line 209 def terminate(instance_id) ec2.terminate_instances(:instance_id => [ instance_id ]) end |
#volumes ⇒ Object
29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/sumo.rb', line 29 def volumes result = ec2.describe_volumes return [] unless result.volumeSet result.volumeSet.item.map do |row| { :volume_id => row["volumeId"], :size => row["size"], :status => row["status"], :device => (row["attachmentSet"]["item"].first["device"] rescue ""), :instance_id => (row["attachmentSet"]["item"].first["instanceId"] rescue ""), } end end |
#wait_for_hostname(instance_id) ⇒ Object
137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/sumo.rb', line 137 def wait_for_hostname(instance_id) raise ArgumentError unless instance_id and instance_id.match(/^i-/) loop do if inst = instance_info(instance_id) if hostname = inst[:hostname] return hostname end end sleep 1 end end |
#wait_for_ssh(hostname) ⇒ Object
149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/sumo.rb', line 149 def wait_for_ssh(hostname) raise ArgumentError unless hostname loop do begin Timeout::timeout(4) do TCPSocket.new(hostname, 22) return end rescue SocketError, Timeout::Error, Errno::ECONNREFUSED, Errno::EHOSTUNREACH end end end |