Class: Judo::Server
- Inherits:
-
Object
- Object
- Judo::Server
- Defined in:
- lib/judo/server.rb
Instance Attribute Summary collapse
-
#name ⇒ Object
Returns the value of attribute name.
Class Method Summary collapse
Instance Method Summary collapse
- #<=>(s) ⇒ Object
- #add(key, value) ⇒ Object
- #add_ip(public_ip) ⇒ Object
- #add_volume(volume_id, device) ⇒ Object
- #allocate_disk(snapshots) ⇒ Object
- #allocate_ip ⇒ Object
- #ami ⇒ Object
- #attach_ip ⇒ Object
- #attach_volumes ⇒ Object
- #clone ⇒ Object
- #clone_snapshots(snapshots) ⇒ Object
- #cloned? ⇒ Boolean
- #config ⇒ Object
- #connect_ssh ⇒ Object
- #console_output ⇒ Object
- #create(options) ⇒ Object
- #create_volumes ⇒ Object
- #debug(str) ⇒ Object
- #delete ⇒ Object
- #destroy ⇒ Object
- #dns_name ⇒ Object
- #ec2_instance ⇒ Object
- #ec2_instance_type ⇒ Object
- #ec2_state ⇒ Object
- #ec2_volumes ⇒ Object
- #elastic_ip ⇒ Object
- #fetch_state ⇒ Object
- #force_detach_volumes ⇒ Object
- #generic? ⇒ Boolean
- #generic_name? ⇒ Boolean
- #get(key) ⇒ Object
- #group ⇒ Object
- #has_ip? ⇒ Boolean
- #has_volumes? ⇒ Boolean
- #hostname ⇒ Object
- #ia32? ⇒ Boolean
- #ia64? ⇒ Boolean
-
#initialize(base, name, group, version = nil) ⇒ Server
constructor
A new instance of Server.
- #instance_id ⇒ Object
-
#instance_size ⇒ Object
end simple DB access #######.
- #invalid(str) ⇒ Object
- #ip ⇒ Object
- #kuzushi_action ⇒ Object
- #launch_ec2 ⇒ Object
- #note ⇒ Object
- #reload ⇒ Object
- #remove(key, value = nil) ⇒ Object
- #remove_ip ⇒ Object
- #remove_volume(volume_id, device) ⇒ Object
- #restart(force = false) ⇒ Object
- #running? ⇒ Boolean
- #secret ⇒ Object
- #security_groups ⇒ Object
- #size_desc ⇒ Object
- #snapshot(name) ⇒ Object
- #snapshots ⇒ Object
- #start(new_version = nil) ⇒ Object
- #state ⇒ Object
- #stop(force = false) ⇒ Object
- #task(msg, &block) ⇒ Object
- #to_s ⇒ Object
- #update(attrs) ⇒ Object
- #update_version(new_version) ⇒ Object
- #url ⇒ Object
- #user_data ⇒ Object
- #validate ⇒ Object
- #version ⇒ Object
- #version_desc ⇒ Object
- #virgin? ⇒ Boolean
- #volumes ⇒ Object
- #wait_for_hostname ⇒ Object
- #wait_for_running ⇒ Object
- #wait_for_ssh ⇒ Object
- #wait_for_termination ⇒ Object
- #wait_for_volumes_detached ⇒ Object
Constructor Details
#initialize(base, name, group, version = nil) ⇒ Server
Returns a new instance of Server.
5 6 7 8 9 |
# File 'lib/judo/server.rb', line 5 def initialize(base, name, group, version = nil) @base = base @name = name @group_name = group end |
Instance Attribute Details
#name ⇒ Object
Returns the value of attribute name.
3 4 5 |
# File 'lib/judo/server.rb', line 3 def name @name end |
Class Method Details
.commit ⇒ Object
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 |
# File 'lib/judo/server.rb', line 466 def self.commit ## FIXME Config.group_dirs.each do |group_dir| group = File.basename(group_dir) next if Config.group and Config.group != group puts "commiting #{group}" doc = Config.couchdb.get(group) rescue {} config = Config.read_config(group) config['_id'] = group config['_rev'] = doc['_rev'] if doc.has_key?('_rev') response = Config.couchdb.save_doc(config) doc = Config.couchdb.get(response['id']) # walk subdirs and save as _attachments ['files', 'templates', 'packages', 'scripts'].each { |subdir| Dir["#{group_dir}/#{subdir}/*"].each do |f| puts "storing attachment #{f}" doc.("#{subdir}/#{File.basename(f)}", File.read(f)) end } end end |
.domain ⇒ Object
122 123 124 |
# File 'lib/judo/server.rb', line 122 def self.domain "judo_servers" end |
.task(msg, &block) ⇒ Object
226 227 228 229 230 231 232 233 234 235 236 |
# File 'lib/judo/server.rb', line 226 def self.task(msg, &block) printf "---> %-24s ", "#{msg}..." STDOUT.flush start = Time.now result = block.call result = "done" unless result.is_a? String finish = Time.now time = sprintf("%0.1f", finish - start) puts "#{result} (#{time}s)" result end |
Instance Method Details
#<=>(s) ⇒ Object
543 544 545 |
# File 'lib/judo/server.rb', line 543 def <=>(s) [group.name, name] <=> [s.group.name, s.name] end |
#add(key, value) ⇒ Object
131 132 133 134 |
# File 'lib/judo/server.rb', line 131 def add(key, value) @base.sdb.put_attributes(self.class.domain, name, { key => value }) (state[key] ||= []) << value end |
#add_ip(public_ip) ⇒ Object
418 419 420 421 |
# File 'lib/judo/server.rb', line 418 def add_ip(public_ip) update "elastic_ip" => public_ip attach_ip end |
#add_volume(volume_id, device) ⇒ Object
450 451 452 453 454 455 456 457 458 |
# File 'lib/judo/server.rb', line 450 def add_volume(volume_id, device) invalid("Server already has a volume on that device") if volumes[device] add "volumes", "#{device}:#{volume_id}" @base.ec2.attach_volume(volume_id, instance_id, device) if running? volume_id end |
#allocate_disk(snapshots) ⇒ Object
165 166 167 168 169 170 171 |
# File 'lib/judo/server.rb', line 165 def allocate_disk(snapshots) if snapshots clone_snapshots(snapshots) else create_volumes end end |
#allocate_ip ⇒ Object
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/judo/server.rb', line 204 def allocate_ip begin if config["elastic_ip"] and not elastic_ip ### EC2 allocate_address task("Adding an elastic ip") do ip = @base.ec2.allocate_address add_ip(ip) end end rescue Aws::AwsError => e if e. =~ /AddressLimitExceeded/ invalid "Failed to allocate ip address: Limit Exceeded" else raise end end end |
#ami ⇒ Object
351 352 353 |
# File 'lib/judo/server.rb', line 351 def ami ia32? ? config["ami32"] : config["ami64"] end |
#attach_ip ⇒ Object
423 424 425 426 427 |
# File 'lib/judo/server.rb', line 423 def attach_ip return unless running? and elastic_ip ### EC2 associate_address @base.ec2.associate_address(instance_id, elastic_ip) end |
#attach_volumes ⇒ Object
434 435 436 437 438 439 440 |
# File 'lib/judo/server.rb', line 434 def attach_volumes return unless running? volumes.each do |device,volume_id| ### EC2 attach_volume @base.ec2.attach_volume(volume_id, instance_id, device) end end |
#clone ⇒ Object
98 99 100 |
# File 'lib/judo/server.rb', line 98 def clone get("clone") end |
#clone_snapshots(snapshots) ⇒ Object
173 174 175 176 177 178 179 180 |
# File 'lib/judo/server.rb', line 173 def clone_snapshots(snapshots) snapshots.each do |device,snap_id| task("Creating EC2 Volume #{device} from #{snap_id}") do volume_id = @base.ec2.create_volume(snap_id, nil, config["availability_zone"])[:aws_id] add_volume(volume_id, device) end end end |
#cloned? ⇒ Boolean
102 103 104 |
# File 'lib/judo/server.rb', line 102 def cloned? !!clone end |
#config ⇒ Object
157 158 159 |
# File 'lib/judo/server.rb', line 157 def config group.config end |
#connect_ssh ⇒ Object
460 461 462 463 464 |
# File 'lib/judo/server.rb', line 460 def connect_ssh wait_for_ssh system "chmod 600 #{group.keypair_file}" system "ssh -i #{group.keypair_file} #{config["user"]}@#{hostname}" end |
#console_output ⇒ Object
346 347 348 349 |
# File 'lib/judo/server.rb', line 346 def console_output invalid "not running" unless running? @base.ec2.get_console_output(instance_id)[:aws_output] end |
#create(options) ⇒ Object
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/judo/server.rb', line 11 def create() raise JudoError, "no group specified" unless @group_name snapshots = [:snapshots] note = [:note] version = [:version] version ||= group.version if @name.nil? index = @base.servers.map { |s| (s.name =~ /^#{s.group.name}.(\d*)$/); $1.to_i }.sort.last.to_i + 1 @name = "#{group.name}.#{index}" end raise JudoError, "there is already a server named #{name}" if @base.servers.detect { |s| s.name == @name and s != self} task("Creating server #{name}") do update "name" => name, "group" => @group_name, "note" => note, "virgin" => true, "secret" => rand(2 ** 128).to_s(36), "version" => version @base.sdb.put_attributes("judo_config", "groups", @group_name => name) end allocate_disk(snapshots) allocate_ip self end |
#create_volumes ⇒ Object
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/judo/server.rb', line 182 def create_volumes if config["volumes"] [config["volumes"]].flatten.each do |volume_config| device = volume_config["device"] if volume_config["media"] == "ebs" size = volume_config["size"] if not volumes[device] task("Creating EC2 Volume #{device} #{size}") do ### EC2 create_volume volume_id = @base.ec2.create_volume(nil, size, config["availability_zone"])[:aws_id] add_volume(volume_id, device) end else puts "Volume #{device} already exists." end else puts "device #{device || volume_config["mount"]} is not of media type 'ebs', skipping..." end end end end |
#debug(str) ⇒ Object
337 338 339 340 |
# File 'lib/judo/server.rb', line 337 def debug(str) return unless ENV['JUDO_DEBUG'] == "1" puts "<JUDO_DEBUG>#{str}</JUDO_DEBUG>" end |
#delete ⇒ Object
146 147 148 149 |
# File 'lib/judo/server.rb', line 146 def delete group.delete_server(self) if group @base.sdb.delete_attributes(self.class.domain, name) end |
#destroy ⇒ Object
256 257 258 259 260 261 262 |
# File 'lib/judo/server.rb', line 256 def destroy stop if running? ### EC2 release_address task("Deleting Elastic Ip") { remove_ip } if has_ip? volumes.each { |dev,v| remove_volume(v,dev) } task("Destroying server #{name}") { delete } end |
#dns_name ⇒ Object
429 430 431 432 |
# File 'lib/judo/server.rb', line 429 def dns_name return nil unless elastic_ip `dig +short -x #{elastic_ip}`.strip end |
#ec2_instance ⇒ Object
268 269 270 271 |
# File 'lib/judo/server.rb', line 268 def ec2_instance ### EC2 describe_instances @base.ec2_instances.detect { |e| e[:aws_instance_id] == instance_id } or {} end |
#ec2_instance_type ⇒ Object
489 490 491 |
# File 'lib/judo/server.rb', line 489 def ec2_instance_type ec2_instance[:aws_instance_type] rescue nil end |
#ec2_state ⇒ Object
264 265 266 |
# File 'lib/judo/server.rb', line 264 def ec2_state ec2_instance[:aws_state] rescue "offline" end |
#ec2_volumes ⇒ Object
246 247 248 249 |
# File 'lib/judo/server.rb', line 246 def ec2_volumes return [] if volumes.empty? @base.ec2.describe_volumes( volumes.values ) end |
#elastic_ip ⇒ Object
58 59 60 |
# File 'lib/judo/server.rb', line 58 def elastic_ip get "elastic_ip" end |
#fetch_state ⇒ Object
42 43 44 |
# File 'lib/judo/server.rb', line 42 def fetch_state @base.sdb.get_attributes(self.class.domain, name)[:attributes] end |
#force_detach_volumes ⇒ Object
305 306 307 308 309 310 311 |
# File 'lib/judo/server.rb', line 305 def force_detach_volumes volumes.each do |device,volume_id| task("Force detaching #{volume_id}") do @base.ec2.detach_volume(volume_id, instance_id, device, true) rescue nil end end end |
#generic? ⇒ Boolean
297 298 299 |
# File 'lib/judo/server.rb', line 297 def generic? volumes.empty? and not has_ip? and generic_name? end |
#generic_name? ⇒ Boolean
293 294 295 |
# File 'lib/judo/server.rb', line 293 def generic_name? name =~ /^#{group}[.]\d*$/ end |
#get(key) ⇒ Object
50 51 52 |
# File 'lib/judo/server.rb', line 50 def get(key) state[key] && [state[key]].flatten.first end |
#group ⇒ Object
38 39 40 |
# File 'lib/judo/server.rb', line 38 def group @group ||= @base.groups.detect { |g| g.name == @group_name } end |
#has_ip? ⇒ Boolean
238 239 240 |
# File 'lib/judo/server.rb', line 238 def has_ip? !!elastic_ip end |
#has_volumes? ⇒ Boolean
242 243 244 |
# File 'lib/judo/server.rb', line 242 def has_volumes? not volumes.empty? end |
#hostname ⇒ Object
363 364 365 |
# File 'lib/judo/server.rb', line 363 def hostname ec2_instance[:dns_name] == "" ? nil : ec2_instance[:dns_name] end |
#ia32? ⇒ Boolean
355 356 357 |
# File 'lib/judo/server.rb', line 355 def ia32? ["m1.small", "c1.medium"].include?(instance_size) end |
#ia64? ⇒ Boolean
359 360 361 |
# File 'lib/judo/server.rb', line 359 def ia64? not ia32? end |
#instance_id ⇒ Object
54 55 56 |
# File 'lib/judo/server.rb', line 54 def instance_id get "instance_id" end |
#instance_size ⇒ Object
end simple DB access #######
153 154 155 |
# File 'lib/judo/server.rb', line 153 def instance_size config["instance_size"] end |
#invalid(str) ⇒ Object
301 302 303 |
# File 'lib/judo/server.rb', line 301 def invalid(str) raise JudoInvalid, str end |
#ip ⇒ Object
493 494 495 |
# File 'lib/judo/server.rb', line 493 def ip hostname || config["state_ip"] end |
#kuzushi_action ⇒ Object
82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/judo/server.rb', line 82 def kuzushi_action if virgin? if cloned? "start" else "init" end else "start" end end |
#launch_ec2 ⇒ Object
322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/judo/server.rb', line 322 def launch_ec2 # validate ## EC2 launch_instances ud = user_data debug(ud) result = @base.ec2.launch_instances(ami, :instance_type => config["instance_size"], :availability_zone => config["availability_zone"], :key_name => config["key_name"], :group_ids => security_groups, :user_data => ud).first update "instance_id" => result[:aws_instance_id], "virgin" => false end |
#note ⇒ Object
94 95 96 |
# File 'lib/judo/server.rb', line 94 def note get("note") end |
#reload ⇒ Object
497 498 499 500 |
# File 'lib/judo/server.rb', line 497 def reload @base.reload_ec2_instances @base.servers_state.delete(name) end |
#remove(key, value = nil) ⇒ Object
136 137 138 139 140 141 142 143 144 |
# File 'lib/judo/server.rb', line 136 def remove(key, value = nil) if value @base.sdb.delete_attributes(self.class.domain, name, key => value) state[key] - [value] else @base.sdb.delete_attributes(self.class.domain, name, [ key ]) state.delete(key) end end |
#remove_ip ⇒ Object
251 252 253 254 |
# File 'lib/judo/server.rb', line 251 def remove_ip @base.ec2.release_address(elastic_ip) rescue nil remove "elastic_ip" end |
#remove_volume(volume_id, device) ⇒ Object
442 443 444 445 446 447 448 |
# File 'lib/judo/server.rb', line 442 def remove_volume(volume_id, device) task("Deleting #{device} #{volume_id}") do ### EC2 delete_volume @base.ec2.delete_volume(volume_id) remove "volumes", "#{device}:#{volume_id}" end end |
#restart(force = false) ⇒ Object
288 289 290 291 |
# File 'lib/judo/server.rb', line 288 def restart(force = false) stop(force) if running? start end |
#running? ⇒ Boolean
273 274 275 276 |
# File 'lib/judo/server.rb', line 273 def running? ## other options are "terminated" and "nil" ["pending", "running", "shutting_down", "degraded"].include?(ec2_state) end |
#secret ⇒ Object
110 111 112 |
# File 'lib/judo/server.rb', line 110 def secret get "secret" end |
#security_groups ⇒ Object
342 343 344 |
# File 'lib/judo/server.rb', line 342 def security_groups [ config["security_group"] ].flatten end |
#size_desc ⇒ Object
62 63 64 65 66 67 68 |
# File 'lib/judo/server.rb', line 62 def size_desc if not running? or ec2_instance_type == instance_size instance_size else "#{ec2_instance_type}/#{instance_size}" end end |
#snapshot(name) ⇒ Object
538 539 540 541 |
# File 'lib/judo/server.rb', line 538 def snapshot(name) snap = @base.new_snapshot(name, self.name) snap.create end |
#snapshots ⇒ Object
114 115 116 |
# File 'lib/judo/server.rb', line 114 def snapshots @base.snapshots.select { |s| s.server == self } end |
#start(new_version = nil) ⇒ Object
278 279 280 281 282 283 284 285 286 |
# File 'lib/judo/server.rb', line 278 def start(new_version = nil) invalid "Already running" if running? invalid "No config has been commited yet, type 'judo commit'" unless group.version > 0 task("Updating server version") { update_version(new_version) } if new_version task("Starting server #{name}") { launch_ec2 } task("Wait for server") { wait_for_running } if elastic_ip or has_volumes? task("Attaching ip") { attach_ip } if elastic_ip task("Attaching volumes") { attach_volumes } if has_volumes? end |
#state ⇒ Object
46 47 48 |
# File 'lib/judo/server.rb', line 46 def state @base.servers_state[name] ||= fetch_state end |
#stop(force = false) ⇒ Object
313 314 315 316 317 318 319 320 |
# File 'lib/judo/server.rb', line 313 def stop(force = false) invalid "not running" unless running? ## EC2 terminate_isntaces task("Terminating instance") { @base.ec2.terminate_instances([ instance_id ]) } force_detach_volumes if force task("Wait for volumes to detach") { wait_for_volumes_detached } if volumes.size > 0 remove "instance_id" end |
#task(msg, &block) ⇒ Object
222 223 224 |
# File 'lib/judo/server.rb', line 222 def task(msg, &block) @base.task(msg, &block) end |
#to_s ⇒ Object
161 162 163 |
# File 'lib/judo/server.rb', line 161 def to_s "#{name}:#{@group_name}" end |
#update(attrs) ⇒ Object
126 127 128 129 |
# File 'lib/judo/server.rb', line 126 def update(attrs) @base.sdb.put_attributes(self.class.domain, name, attrs, :replace) state.merge! attrs end |
#update_version(new_version) ⇒ Object
78 79 80 |
# File 'lib/judo/server.rb', line 78 def update_version(new_version) update "version" => new_version end |
#url ⇒ Object
518 519 520 |
# File 'lib/judo/server.rb', line 518 def url @url ||= group.s3_url end |
#user_data ⇒ Object
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 |
# File 'lib/judo/server.rb', line 502 def user_data <<USER_DATA #!/bin/sh export DEBIAN_FRONTEND="noninteractive" export DEBIAN_PRIORITY="critical" export SECRET='#{secret}' apt-get update apt-get install ruby rubygems ruby-dev irb libopenssl-ruby libreadline-ruby -y gem install kuzushi --no-rdoc --no-ri GEM_BIN=`ruby -r rubygems -e "puts Gem.bindir"` echo "$GEM_BIN/kuzushi #{virgin? && "init" || "start"} '#{url}'" > /var/log/kuzushi.log $GEM_BIN/kuzushi #{virgin? && "init" || "start"} '#{url}' >> /var/log/kuzushi.log 2>&1 USER_DATA end |
#validate ⇒ Object
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 |
# File 'lib/judo/server.rb', line 522 def validate ### EC2 create_security_group @base.create_security_group ### EC2 desctibe_key_pairs k = @base.ec2.describe_key_pairs.detect { |kp| kp[:aws_key_name] == config["key_name"] } if k.nil? if config["key_name"] == "judo" @base.create_keypair else raise "cannot use key_pair #{config["key_name"]} b/c it does not exist" end end end |
#version ⇒ Object
74 75 76 |
# File 'lib/judo/server.rb', line 74 def version get("version").to_i end |
#version_desc ⇒ Object
70 71 72 |
# File 'lib/judo/server.rb', line 70 def version_desc group.version_desc(version) end |
#virgin? ⇒ Boolean
106 107 108 |
# File 'lib/judo/server.rb', line 106 def virgin? get("virgin").to_s == "true" ## I'm going to set it to true and it will come back from the db as "true" -> could be "false" or false or nil also end |
#volumes ⇒ Object
118 119 120 |
# File 'lib/judo/server.rb', line 118 def volumes Hash[ (state["volumes"] || []).map { |a| a.split(":") } ] end |
#wait_for_hostname ⇒ Object
375 376 377 378 379 380 381 |
# File 'lib/judo/server.rb', line 375 def wait_for_hostname loop do reload return hostname if hostname sleep 1 end end |
#wait_for_running ⇒ Object
367 368 369 370 371 372 373 |
# File 'lib/judo/server.rb', line 367 def wait_for_running loop do return if ec2_state == "running" reload sleep 1 end end |
#wait_for_ssh ⇒ Object
404 405 406 407 408 409 410 411 412 413 414 415 416 |
# File 'lib/judo/server.rb', line 404 def wait_for_ssh invalid "not running" unless running? loop do begin reload Timeout::timeout(4) do TCPSocket.new(hostname, 22) return end rescue SocketError, Timeout::Error, Errno::ECONNREFUSED, Errno::EHOSTUNREACH end end end |
#wait_for_termination ⇒ Object
396 397 398 399 400 401 402 |
# File 'lib/judo/server.rb', line 396 def wait_for_termination loop do reload break if ec2_instance[:aws_state] == "terminated" sleep 1 end end |
#wait_for_volumes_detached ⇒ Object
383 384 385 386 387 388 389 390 391 392 393 394 |
# File 'lib/judo/server.rb', line 383 def wait_for_volumes_detached begin Timeout::timeout(30) do loop do break if ec2_volumes.reject { |v| v[:aws_status] == "available" }.empty? sleep 2 end end rescue Timeout::Error force_detach_volumes end end |