rubber 2.1.2 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. data/.travis.yml +2 -4
  2. data/CHANGELOG +98 -0
  3. data/LICENSE +10 -336
  4. data/lib/rubber/cloud/aws.rb +8 -8
  5. data/lib/rubber/commands/cron.rb +1 -1
  6. data/lib/rubber/commands/util.rb +46 -0
  7. data/lib/rubber/encryption.rb +46 -0
  8. data/lib/rubber/environment.rb +24 -5
  9. data/lib/rubber/recipes/rubber.rb +1 -1
  10. data/lib/rubber/recipes/rubber/instances.rb +97 -37
  11. data/lib/rubber/recipes/rubber/setup.rb +9 -4
  12. data/lib/rubber/recipes/rubber/static_ips.rb +3 -2
  13. data/lib/rubber/version.rb +1 -1
  14. data/rubber.gemspec +1 -2
  15. data/templates/apache/config/rubber/rubber-apache.yml +2 -2
  16. data/templates/base/config/deploy.rb +1 -1
  17. data/templates/base/config/rubber/common/crontab +1 -3
  18. data/templates/base/config/rubber/common/gemrc +5 -0
  19. data/templates/base/config/rubber/common/rubber.profile +0 -3
  20. data/templates/base/config/rubber/common/ruby.profile +9 -0
  21. data/templates/base/config/rubber/deploy-setup.rb +45 -65
  22. data/templates/base/config/rubber/rubber-ruby.yml +21 -0
  23. data/templates/base/config/rubber/rubber.yml +8 -3
  24. data/templates/base/script/rubber +7 -3
  25. data/templates/collectd/config/rubber/role/passenger/collectd-passenger.rb.conf +2 -2
  26. data/templates/collectd/config/rubber/role/passenger/passenger-status-sudoers.conf +2 -2
  27. data/templates/complete_unicorn_nginx/config/rubber/rubber-complete.yml +2 -3
  28. data/templates/complete_unicorn_nginx/templates.yml +0 -1
  29. data/templates/elasticsearch/config/rubber/deploy-elasticsearch.rb +1 -1
  30. data/templates/elasticsearch/config/rubber/rubber-elasticsearch.yml +1 -1
  31. data/templates/graphite/config/rubber/deploy-graphite.rb +9 -52
  32. data/templates/graphite/config/rubber/role/graphite_server/carbon.conf +1 -1
  33. data/templates/graphite/config/rubber/role/graphite_server/graphite-collectd-ping.conf +11 -0
  34. data/templates/graphite/config/rubber/role/graphite_server/monit-graphite_server.conf +8 -0
  35. data/templates/graphite/config/rubber/role/graphite_web/graphite_web-upstart.conf +15 -0
  36. data/templates/graphite/config/rubber/role/graphite_web/monit-graphite_web.conf +7 -0
  37. data/templates/graphite/config/rubber/role/graphite_web/uwsgi.ini +16 -0
  38. data/templates/graphite/config/rubber/rubber-graphite.yml +6 -4
  39. data/templates/graphite/templates.yml +2 -0
  40. data/templates/mongrel/config/rubber/deploy-mongrel.rb +1 -1
  41. data/templates/monit/config/rubber/role/mongrel/monit-mongrel.conf +2 -2
  42. data/templates/munin/config/rubber/role/munin/munin-plugins.conf +3 -3
  43. data/templates/munin/config/rubber/role/passenger/munin-passenger-memory.conf +1 -1
  44. data/templates/munin/config/rubber/role/passenger/munin-passenger-sudoers.conf +2 -2
  45. data/templates/munin/config/rubber/role/passenger/munin-passenger.conf +1 -1
  46. data/templates/munin/config/rubber/role/passenger_nginx/munin-passenger-memory.conf +1 -1
  47. data/templates/munin/config/rubber/role/passenger_nginx/munin-passenger-sudoers.conf +2 -2
  48. data/templates/munin/config/rubber/role/passenger_nginx/munin-passenger.conf +1 -1
  49. data/templates/passenger/config/rubber/deploy-passenger.rb +5 -11
  50. data/templates/passenger/config/rubber/role/passenger/passenger-apache-vhost.conf +12 -0
  51. data/templates/passenger/config/rubber/rubber-passenger.yml +3 -3
  52. data/templates/passenger_nginx/config/rubber/role/passenger_nginx/nginx.conf +1 -1
  53. data/templates/passenger_nginx/config/rubber/rubber-passenger_nginx.yml +2 -2
  54. data/templates/postgresql/config/rubber/deploy-postgresql.rb +47 -0
  55. data/templates/redis/config/rubber/rubber-redis.yml +1 -1
  56. data/templates/resque/templates.yml +0 -1
  57. data/templates/sidekiq/config/rubber/deploy-sidekiq.rb +35 -0
  58. data/templates/sidekiq/config/rubber/role/sidekiq/monit-sidekiq.conf +10 -0
  59. data/templates/sidekiq/config/rubber/rubber-sidekiq.yml +2 -0
  60. data/templates/sidekiq/templates.rb +2 -0
  61. data/templates/sidekiq/templates.yml +4 -0
  62. data/templates/solr/config/rubber/common/solr_sunspot.yml +10 -0
  63. data/templates/solr/config/rubber/deploy-solr.rb +78 -0
  64. data/templates/solr/config/rubber/rubber-solr.yml +10 -0
  65. data/templates/solr/templates.yml +3 -0
  66. data/templates/sphinx/config/rubber/deploy-sphinx.rb +4 -4
  67. data/templates/torquebox/config/rubber/deploy-torquebox.rb +16 -4
  68. data/templates/torquebox/config/rubber/role/app/torquebox-apache-vhost.conf +15 -5
  69. data/templates/torquebox/config/rubber/role/torquebox/graylog-logging-module.xml +9 -0
  70. data/templates/torquebox/config/rubber/role/torquebox/standalone-ha.xml +25 -7
  71. data/templates/torquebox/config/rubber/role/torquebox/standalone.conf +2 -2
  72. data/templates/torquebox/config/rubber/rubber-torquebox.yml +6 -3
  73. data/templates/zookeeper/config/rubber/rubber-zookeeper.yml +1 -1
  74. data/test/command_test.rb +7 -5
  75. data/test/commands/cron_test.rb +22 -18
  76. data/test/commands/util_test.rb +40 -0
  77. data/test/encryption_test.rb +59 -0
  78. data/test/environment_test.rb +14 -0
  79. data/test/fixtures/obfuscated/rubber.yml +3 -0
  80. data/test/fixtures/obfuscated/secret.yml +1 -0
  81. metadata +33 -12
  82. data/templates/base/config/rubber/rubber-rvm.yml +0 -42
  83. data/templates/complete_passenger/templates.rb +0 -9
  84. data/templates/complete_passenger_nginx/templates.rb +0 -8
  85. data/templates/complete_unicorn_nginx/config/rubber/role/haproxy/haproxy-unicorn.conf +0 -21
  86. data/templates/complete_unicorn_nginx/templates.rb +0 -8
  87. data/templates/graphite/config/rubber/role/graphite_web/graphite-vhost.conf +0 -62
@@ -28,19 +28,21 @@ module Rubber
28
28
  end
29
29
 
30
30
  def create_image(image_name)
31
+
32
+ # validate all needed config set
33
+ ["key_file", "pk_file", "cert_file", "account", "secret_access_key", "image_bucket"].each do |k|
34
+ raise "Set #{k} in rubber.yml" unless "#{env[k]}".strip.size > 0
35
+ end
36
+ raise "create_image can only be called from a capistrano scope" unless capistrano
37
+
31
38
  ec2_key = env.key_file
32
39
  ec2_pk = env.pk_file
33
40
  ec2_cert = env.cert_file
41
+
34
42
  ec2_key_dest = "/mnt/#{File.basename(ec2_key)}"
35
43
  ec2_pk_dest = "/mnt/#{File.basename(ec2_pk)}"
36
44
  ec2_cert_dest = "/mnt/#{File.basename(ec2_cert)}"
37
45
 
38
- # validate all needed config set
39
- ["key_file", "pk_file", "cert_file", "account", "secret_access_key", "image_bucket"].each do |k|
40
- raise "Set #{k} in rubber.yml" unless "#{env[k]}".strip.size > 0
41
- end
42
- raise "create_image can only be called from a capistrano scope" unless capistrano
43
-
44
46
  storage(env.image_bucket).ensure_bucket
45
47
 
46
48
  capistrano.put(File.read(ec2_key), ec2_key_dest)
@@ -51,7 +53,6 @@ module Rubber
51
53
  arch = case arch when /i\d86/ then "i386" else arch end
52
54
 
53
55
  capistrano.sudo_script "create_bundle", <<-CMD
54
- rvm use system
55
56
  export RUBYLIB=/usr/lib/site_ruby/
56
57
  unset RUBYOPT
57
58
  nohup ec2-bundle-vol --batch -d /mnt -k #{ec2_pk_dest} -c #{ec2_cert_dest} -u #{env.account} -p #{image_name} -r #{arch} &> /tmp/ec2-bundle-vol.log &
@@ -66,7 +67,6 @@ module Rubber
66
67
  CMD
67
68
 
68
69
  capistrano.sudo_script "register_bundle", <<-CMD
69
- rvm use system
70
70
  export RUBYLIB=/usr/lib/site_ruby/
71
71
  unset RUBYOPT
72
72
  echo "Uploading image to S3..."
@@ -64,7 +64,7 @@ module Rubber
64
64
 
65
65
  if task?
66
66
  log = "#{rootdir}/log/cron-task-#{ident}.log"
67
- cmd = ["rubber"] + cmd
67
+ cmd = [$0] + cmd
68
68
  elsif ruby?
69
69
  ruby_code = cmd.join(' ')
70
70
  ident = ruby_code.gsub(/\W+/, "_").gsub(/(^_+)|(_+$)/, '')[0..19]
@@ -310,6 +310,52 @@ module Rubber
310
310
  end
311
311
 
312
312
  end
313
+
314
+ class Obfuscation < Clamp::Command
315
+
316
+ def self.subcommand_name
317
+ "util:obfuscation"
318
+ end
319
+
320
+ def self.subcommand_description
321
+ "Obfuscates rubber-secret.yml using encryption"
322
+ end
323
+
324
+ option ["-f", "--secretfile"],
325
+ "SECRETFILE",
326
+ "The rubber_secret file\n (default: <Rubber.config.rubber_secret>)"
327
+
328
+ option ["-k", "--secretkey"],
329
+ "SECRETKEY",
330
+ "The rubber_secret_key\n (default: <Rubber.config.rubber_secret_key>)"
313
331
 
332
+ option ["-d", "--decrypt"],
333
+ :flag,
334
+ "Decrypt and display the current rubber_secret"
335
+
336
+ option ["-g", "--generate"],
337
+ :flag,
338
+ "Generate a key for rubber_secret_key"
339
+
340
+ def execute
341
+ if generate?
342
+ puts "Obfuscation key: " + Rubber::Encryption.generate_encrypt_key.inspect
343
+ exit
344
+ else
345
+ signal_usage_error "Need to define a rubber_secret in rubber.yml" unless secretfile
346
+ signal_usage_error "Need to define a rubber_secret_key in rubber.yml" unless secretkey
347
+ signal_usage_error "The file pointed to by rubber_secret needs to exist" unless File.exist?(secretfile)
348
+ data = IO.read(secretfile)
349
+
350
+ if decrypt?
351
+ puts Rubber::Encryption.decrypt(data, secretkey)
352
+ else
353
+ puts Rubber::Encryption.encrypt(data, secretkey)
354
+ end
355
+ end
356
+ end
357
+
358
+ end
359
+
314
360
  end
315
361
  end
@@ -0,0 +1,46 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+
4
+ module Rubber
5
+ module Encryption
6
+
7
+ def cipher_algorithm
8
+ OpenSSL::Cipher.new("AES-256-CBC")
9
+ end
10
+
11
+ def cipher_digest
12
+ OpenSSL::Digest.new("SHA256")
13
+ end
14
+
15
+ def generate_encrypt_key
16
+ OpenSSL::Digest.hexdigest('md5', rand.to_s)
17
+ end
18
+
19
+ def encrypt(payload, secret)
20
+ cipher = cipher_algorithm
21
+
22
+ cipher.encrypt
23
+ cipher.pkcs5_keyivgen(cipher_digest.hexdigest(secret))
24
+
25
+ encrypted_data = cipher.update(payload) + cipher.final
26
+ encoded_encrypted_data = Base64.encode64(encrypted_data)
27
+
28
+ return encoded_encrypted_data
29
+ end
30
+
31
+ def decrypt(encoded_encrypted_data, secret)
32
+ cipher = cipher_algorithm
33
+
34
+ cipher.decrypt
35
+ cipher.pkcs5_keyivgen(cipher_digest.hexdigest(secret))
36
+
37
+ encrypted_data = Base64.decode64(encoded_encrypted_data)
38
+ payload = cipher.update(encrypted_data) + cipher.final
39
+
40
+ return payload
41
+ end
42
+
43
+ extend self
44
+
45
+ end
46
+ end
@@ -1,6 +1,8 @@
1
1
  require 'yaml'
2
2
  require 'socket'
3
3
  require 'delegate'
4
+ require 'rubber/encryption'
5
+
4
6
 
5
7
  module Rubber
6
8
  module Configuration
@@ -28,15 +30,17 @@ module Rubber
28
30
 
29
31
  @items = {}
30
32
  @config_files.each { |file| read_config(file) }
31
- @config_secret = bind().rubber_secret
32
- read_config(@config_secret) if @config_secret
33
+
34
+ read_secret_config
33
35
  end
34
36
 
35
37
  def read_config(file)
36
38
  Rubber.logger.debug{"Reading rubber configuration from #{file}"}
37
39
  if File.exist?(file)
38
40
  begin
39
- @items = Environment.combine(@items, YAML::load(ERB.new(IO.read(file)).result) || {})
41
+ data = IO.read(file)
42
+ data = yield(data) if block_given?
43
+ @items = Environment.combine(@items, YAML::load(ERB.new(data).result) || {})
40
44
  rescue Exception => e
41
45
  Rubber.logger.error{"Unable to read rubber configuration from #{file}"}
42
46
  raise
@@ -44,6 +48,21 @@ module Rubber
44
48
  end
45
49
  end
46
50
 
51
+ def read_secret_config
52
+ bound = bind()
53
+ @config_secret = bound.rubber_secret
54
+ if @config_secret
55
+ obfuscation_key = bound.rubber_secret_key
56
+ if obfuscation_key
57
+ read_config(@config_secret) do |data|
58
+ Rubber::Encryption.decrypt(data, obfuscation_key)
59
+ end
60
+ else
61
+ read_config(@config_secret)
62
+ end
63
+ end
64
+ end
65
+
47
66
  def known_roles
48
67
  return @known_roles if @known_roles
49
68
 
@@ -89,7 +108,7 @@ module Rubber
89
108
  if old.is_a?(Hash) && new.is_a?(Hash)
90
109
  value = old.clone
91
110
  new.each do |nk, nv|
92
- if nk[0] == '^'
111
+ if nk.to_s[0..0] == '^'
93
112
  nk = nk[1..-1]
94
113
  value[nk] = combine(nil, nv)
95
114
  else
@@ -212,4 +231,4 @@ module Rubber
212
231
  end
213
232
 
214
233
  end
215
- end
234
+ end
@@ -63,7 +63,7 @@ namespace :rubber do
63
63
  # NOTE: for some reason Capistrano requires you to have both the public and
64
64
  # the private key in the same folder, the public key should have the
65
65
  # extension ".pub".
66
- ssh_options[:keys] = cloud.env.key_file
66
+ ssh_options[:keys] = [cloud.env.key_file].flatten
67
67
  ssh_options[:timeout] = fetch(:ssh_timeout, 5)
68
68
  end
69
69
 
@@ -79,18 +79,22 @@ namespace :rubber do
79
79
  Stop the EC2 instance for the give ALIAS
80
80
  DESC
81
81
  required_task :stop do
82
- instance_alias = get_env('ALIAS', "Instance alias (e.g. web01)", true)
82
+ instance_aliases = get_env('ALIAS', "Instance alias (e.g. web01 or web01~web05,web09)", true)
83
+
84
+ aliases = Rubber::Util::parse_aliases(instance_aliases)
83
85
  ENV.delete('ROLES') # so we don't get an error if people leave ROLES in env from :create CLI
84
- stop_instance(instance_alias)
86
+ stop_instances(aliases)
85
87
  end
86
88
 
87
89
  desc <<-DESC
88
90
  Start the EC2 instance for the give ALIAS
89
91
  DESC
90
92
  required_task :start do
91
- instance_alias = get_env('ALIAS', "Instance alias (e.g. web01)", true)
93
+ instance_aliases = get_env('ALIAS', "Instance alias (e.g. web01 or web01~web05,web09)", true)
94
+
95
+ aliases = Rubber::Util::parse_aliases(instance_aliases)
92
96
  ENV.delete('ROLES') # so we don't get an error if people leave ROLES in env from :create CLI
93
- start_instance(instance_alias)
97
+ start_instances(aliases)
94
98
  end
95
99
 
96
100
  desc <<-DESC
@@ -465,50 +469,106 @@ namespace :rubber do
465
469
 
466
470
  cloud.reboot_instance(instance_item.instance_id)
467
471
  end
468
-
469
- # Stops the given ec2 instance. Note that this operation only works for instances that use an EBS volume for the root
472
+
473
+ # Stops the given ec2 instances. Note that this operation only works for instances that use an EBS volume for the root
470
474
  # device and that are not spot instances.
471
- def stop_instance(instance_alias)
472
- instance_item = rubber_instances[instance_alias]
473
- fatal "Instance does not exist: #{instance_alias}" if ! instance_item
474
- fatal "Cannot stop spot instances!" if ! instance_item.spot_instance_request_id.nil?
475
- fatal "Cannot stop instances with instance-store root device!" if (instance_item.root_device_type != 'ebs')
476
-
477
- env = rubber_cfg.environment.bind(instance_item.role_names, instance_item.name)
478
-
479
- value = Capistrano::CLI.ui.ask("About to STOP #{instance_alias} (#{instance_item.instance_id}) in mode #{Rubber.env}. Are you SURE [yes/NO]?: ")
475
+ def stop_instances(aliases)
476
+ stop_threads = []
477
+
478
+ instance_items = aliases.collect{|instance_alias| rubber_instances[instance_alias]}
479
+ instance_items = aliases.collect do |instance_alias|
480
+ instance_item = rubber_instances[instance_alias]
481
+
482
+ fatal "Instance does not exist: #{instance_alias}" if ! instance_item
483
+ fatal "Cannot stop spot instances!" if ! instance_item.spot_instance_request_id.nil?
484
+ fatal "Cannot stop instances with instance-store root device!" if (instance_item.root_device_type != 'ebs')
485
+
486
+ instance_item
487
+ end
488
+
489
+ # Get user confirmation
490
+ human_instance_list = instance_items.collect{|instance_item| "#{instance_item.name} (#{instance_item.instance_id})"}.join(', ')
491
+ value = Capistrano::CLI.ui.ask("About to STOP #{human_instance_list} in mode #{Rubber.env}. Are you SURE [yes/NO]?: ")
480
492
  fatal("Exiting", 0) if value != "yes"
493
+
494
+ instance_items.each do |instance_item|
495
+ logger.info "Stopping instance alias=#{instance_item.name}, instance_id=#{instance_item.instance_id}"
496
+
497
+ stop_threads << Thread.new do
498
+ env = rubber_cfg.environment.bind(instance_item.role_names, instance_item.name)
481
499
 
482
- logger.info "Stopping instance alias=#{instance_alias}, instance_id=#{instance_item.instance_id}"
483
-
484
- cloud.stop_instance(instance_item.instance_id)
500
+ cloud.stop_instance(instance_item.instance_id)
501
+
502
+ stopped = false
503
+ while !stopped
504
+ sleep 1
505
+ instance = cloud.describe_instances(instance_item.instance_id).first rescue {}
506
+ stopped = (instance[:state] == "stopped")
507
+ end
508
+ end
509
+ end
510
+
511
+ print "Waiting for #{instance_items.size == 1 ? 'instance' : 'instances'} to stop"
512
+ while true do
513
+ print "."
514
+ sleep 2
515
+ break unless stop_threads.any?(&:alive?)
516
+ end
517
+ print "\n"
518
+
519
+ stop_threads.each(&:join)
485
520
  end
486
521
 
487
- # Starts the given ec2 instance. Note that this operation only works for instances that use an EBS volume for the root
522
+ # Starts the given ec2 instances. Note that this operation only works for instances that use an EBS volume for the root
488
523
  # device, that are not spot instances, and that are already stopped.
489
- def start_instance(instance_alias)
490
- instance_item = rubber_instances[instance_alias]
491
- fatal "Instance does not exist: #{instance_alias}" if ! instance_item
492
- fatal "Cannot start spot instances!" if ! instance_item.spot_instance_request_id.nil?
493
- fatal "Cannot start instances with instance-store root device!" if (instance_item.root_device_type != 'ebs')
494
-
495
- env = rubber_cfg.environment.bind(instance_item.role_names, instance_item.name)
496
-
497
- value = Capistrano::CLI.ui.ask("About to START #{instance_alias} (#{instance_item.instance_id}) in mode #{Rubber.env}. Are you SURE [yes/NO]?: ")
524
+ def start_instances(aliases)
525
+ start_threads = []
526
+ refresh_threads = []
527
+
528
+ instance_items = aliases.collect do |instance_alias|
529
+ instance_item = rubber_instances[instance_alias]
530
+
531
+ fatal "Instance does not exist: #{instance_alias}" if ! instance_item
532
+ fatal "Cannot start spot instances!" if ! instance_item.spot_instance_request_id.nil?
533
+ fatal "Cannot start instances with instance-store root device!" if (instance_item.root_device_type != 'ebs')
534
+
535
+ instance_item
536
+ end
537
+
538
+ # Get user confirmation
539
+ human_instance_list = instance_items.collect{|instance_item| "#{instance_item.name} (#{instance_item.instance_id})"}.join(', ')
540
+ value = Capistrano::CLI.ui.ask("About to START #{human_instance_list} in mode #{Rubber.env}. Are you SURE [yes/NO]?: ")
498
541
  fatal("Exiting", 0) if value != "yes"
499
-
500
- logger.info "Starting instance alias=#{instance_alias}, instance_id=#{instance_item.instance_id}"
501
-
502
- cloud.start_instance(instance_item.instance_id)
503
-
504
- # Re-starting an instance will almost certainly give it a new set of IPs and DNS entries, so refresh the values.
505
- print "Waiting for instance to start"
542
+
543
+ instance_items.each do |instance_item|
544
+ logger.info "Starting instance alias=#{instance_item.name}, instance_id=#{instance_item.instance_id}"
545
+
546
+ start_threads << Thread.new do
547
+ env = rubber_cfg.environment.bind(instance_item.role_names, instance_item.name)
548
+
549
+ cloud.start_instance(instance_item.instance_id)
550
+
551
+ # Re-starting an instance will almost certainly give it a new set of IPs and DNS entries, so refresh the values.
552
+ refresh_threads << Thread.new do
553
+ while ! refresh_instance(instance_item.name)
554
+ sleep 1
555
+ end
556
+ end
557
+ end
558
+ end
559
+
560
+ print "Waiting for #{instance_items.size == 1 ? 'instance' : 'instances'} to start"
506
561
  while true do
507
562
  print "."
508
563
  sleep 2
509
-
510
- break if refresh_instance(instance_alias)
564
+ break unless start_threads.any?(&:alive?)
511
565
  end
566
+
567
+ start_threads.each(&:join)
568
+ refresh_threads.each(&:join)
569
+
570
+ # Static IPs, DNS, etc. need to be set up for the started instances
571
+ post_refresh
512
572
  end
513
573
 
514
574
  # delete from ~/.ssh/known_hosts all lines that begin with ec2- or instance_alias
@@ -515,12 +515,17 @@ namespace :rubber do
515
515
  if reboot
516
516
 
517
517
  logger.info "Rebooting ..."
518
- run("#{sudo} reboot", :hosts => reboot_hosts)
518
+ begin
519
+ run("#{sudo} reboot", :hosts => reboot_hosts)
520
+ # since we rebooted, teardown the connections to force cap to reconnect
521
+ teardown_connections_to(sessions.keys)
522
+ rescue
523
+ # swallow exception since there is a chance
524
+ # net:ssh throws an Exception
525
+ end
526
+
519
527
  sleep 30
520
528
 
521
- # since we rebooted, teardown the connections to force cap to reconnect
522
- teardown_connections_to(sessions.keys)
523
-
524
529
  reboot_hosts.each do |host|
525
530
  direct_connection(host) do
526
531
  run "echo"
@@ -19,8 +19,9 @@ namespace :rubber do
19
19
  rubber_instances.save
20
20
  end
21
21
 
22
- # then, associate it if we don't have a record (on instance) of association
23
- if ! ic.static_ip
22
+ # then, associate it if we don't have a record (on instance) of association or it
23
+ # doesn't match the instance's current external ip
24
+ if !ic.static_ip || ip != ic.external_ip
24
25
  logger.info "Associating static ip #{ip} with #{ic.full_name}"
25
26
  associate_static_ip(ip, ic.instance_id)
26
27
 
@@ -1,3 +1,3 @@
1
1
  module Rubber
2
- VERSION = "2.1.2"
2
+ VERSION = "2.2.0"
3
3
  end
data/rubber.gemspec CHANGED
@@ -29,8 +29,7 @@ Gem::Specification.new do |s|
29
29
  s.require_paths = ["lib"]
30
30
 
31
31
  s.add_dependency 'capistrano', '~> 2.12'
32
- # TODO: force older version of net-ssh till connectivity issues with 2.5.x get resolved
33
- s.add_dependency 'net-ssh', '~> 2.4.0'
32
+ s.add_dependency 'net-ssh', '~> 2.6'
34
33
  s.add_dependency 'thor'
35
34
  s.add_dependency 'clamp'
36
35
  s.add_dependency 'open4'