rubber 2.1.2 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
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'