rubber 2.0.5 → 2.0.6

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 (46) hide show
  1. data/.travis.yml +2 -2
  2. data/CHANGELOG +50 -0
  3. data/lib/rubber/commands/cron.rb +9 -11
  4. data/lib/rubber/commands/util.rb +1 -0
  5. data/lib/rubber/dns.rb +29 -4
  6. data/lib/rubber/dns/aws.rb +181 -0
  7. data/lib/rubber/dns/nettica.rb +74 -36
  8. data/lib/rubber/dns/zerigo.rb +110 -4
  9. data/lib/rubber/instance.rb +1 -1
  10. data/lib/rubber/recipes/rubber/instances.rb +13 -5
  11. data/lib/rubber/recipes/rubber/security_groups.rb +1 -1
  12. data/lib/rubber/recipes/rubber/setup.rb +80 -64
  13. data/lib/rubber/util.rb +7 -0
  14. data/lib/rubber/version.rb +1 -1
  15. data/templates/base/config/rubber/deploy-setup.rb +12 -0
  16. data/templates/base/config/rubber/rubber-dns.yml +17 -21
  17. data/templates/base/config/rubber/rubber.yml +2 -2
  18. data/templates/collectd/config/rubber/role/passenger/passenger-status-sudoers.conf +1 -1
  19. data/templates/elasticsearch/config/rubber/rubber-elasticsearch.yml +1 -1
  20. data/templates/graphite/config/rubber/role/graphite_web/dashboard.html +5 -3
  21. data/templates/jenkins/config/environments/jenkins.rb +1 -1
  22. data/templates/jenkins/config/rubber/rubber-jenkins.yml +1 -1
  23. data/templates/passenger/config/rubber/deploy-passenger.rb +0 -8
  24. data/templates/postgresql/config/rubber/rubber-postgresql.yml +2 -2
  25. data/templates/resque/config/initializers/resque.rb +2 -2
  26. data/templates/resque/config/resque.yml +4 -0
  27. data/templates/resque/config/rubber/common/resque.yml +1 -1
  28. data/templates/resque_scheduler/config/rubber/common/resque_schedule.yml +9 -0
  29. data/templates/resque_scheduler/config/rubber/deploy-resque_scheduler.rb +38 -0
  30. data/templates/resque_scheduler/config/rubber/role/resque_scheduler/resque-scheduler-upstart.conf +20 -0
  31. data/templates/resque_scheduler/config/rubber/rubber-resque_scheduler.yml +7 -0
  32. data/templates/resque_scheduler/lib/tasks/resque-scheduler.rake +28 -0
  33. data/templates/resque_scheduler/templates.rb +1 -0
  34. data/templates/resque_scheduler/templates.yml +3 -0
  35. data/templates/torquebox/config/rubber/rubber-torquebox.yml +1 -1
  36. data/templates/zookeeper/config/rubber/role/zookeeper/myid.conf +5 -4
  37. data/templates/zookeeper/config/rubber/role/zookeeper/zoo.cfg +6 -1
  38. data/test/command_test.rb +0 -55
  39. data/test/commands/cron_test.rb +83 -0
  40. data/test/dns/aws_test.rb +192 -0
  41. data/test/dns/zerigo_test.rb +180 -0
  42. data/test/instance_test.rb +17 -2
  43. data/test/test_helper.rb +37 -2
  44. metadata +20 -8
  45. data/lib/rubber/dns/fog.rb +0 -219
  46. data/test/dns/fog_test.rb +0 -169
data/.travis.yml CHANGED
@@ -3,8 +3,8 @@ rvm:
3
3
  - 1.8.7
4
4
  - 1.9.2
5
5
  - 1.9.3
6
- - jruby-18mode
7
- - jruby-19mode
6
+ # - jruby-18mode
7
+ # - jruby-19mode
8
8
  # - jruby-head
9
9
  # - rbx-18mode
10
10
  # - rbx-19mode
data/CHANGELOG CHANGED
@@ -1,3 +1,53 @@
1
+ 2.0.6 (08/01/2012)
2
+ ------------------
3
+
4
+ New Features:
5
+ ============
6
+ [resque_scheduler] Merge pull request #194 from messick/resque_scheduler_template <dc20ecb>
7
+ [resque_scheduler] Rake task file must end in .rake not .rb <03a7cfa>
8
+ [resque_scheduler] append to log on restart <eadd00a>
9
+ [resque_scheduler] enable scheduler tabs in resque-web ui <3e68509>
10
+ [resque_scheduler] first pass at a resque scheduler template <e9e0111>
11
+ [resque_scheduler] log output from resque-scheduler upstart job <91c3790>
12
+ [resque_scheduler] redirect error to log as well <d75b96a>
13
+ [resque_scheduler] use resque_scheduler PID path from yaml file. <e699193>
14
+
15
+ Improvements:
16
+ ============
17
+ [core] multi-host rubber:reboot <56ef06f>
18
+ [core] update nettica to work with new ruber-dns.yml format <1bfa525>
19
+ [base] format change to better support multi-value records like MX, round robin A, etc refactor to better handle route53/zerigo differences in fog add export_dns_records to allow dumping from provider to a rubber-dns.yml format <7c806cf>
20
+ [base] update base ami <be8c0f4>
21
+
22
+ Bug Fixes:
23
+ =========
24
+ [core] isoalte security groups from global namespace so having a key with the same name as a capistrano role doesn't cause problems <41ba2cf>
25
+ [core] make cron logs more user friendly and prevent exponential explosion of files in log dir due to interaction with rubber:util:rotate_logs <7e11a7f>
26
+ [core] fix nettica support (fix super call, credentials) <14fd048>
27
+ [core] Merge pull request #192 from garrytan/master <837d8be>
28
+ [core] don't attempt to read a non-existant instance file in cloud storage <c4d11ac>
29
+ [core] Merge pull request #173 from viniciusnz/master <d7e8f9f>
30
+ [core] Avoid error if localtime file already exists <cff1fb4>
31
+ [base] Merge pull request #190 from davebenvenuti/ssh_env_whitelist <48260c9>
32
+ [base] whitelist ssh-related variables in sudoers so capistrano tasks can take advantage of ssh-agent forwarding <1009e7b>
33
+ [collectd] Merge pull request #191 from davebenvenuti/passenger_sudoers_fix <09511a8>
34
+ [collectd] add to env variable whitelist rather than overwrite it <dff23f7>
35
+ [elasticsearch] Ensure unzip package present for elasticsearch <40af636>
36
+ [graphite] fix for multiple targets in graph defn <b72c469>
37
+ [jenkins] Merge pull request #171 from ScotterC/patch-1 <133aa3e>
38
+ [jenkins] removed hardcoded application, thanks to scotterc for finding this <b5b26b4>
39
+ [jenkins] sun-java6 fails on Ubuntu 10.04 <88779a9>
40
+ [passenger] Removed the code to remove config.ru from Rails apps as it's no longer needed in Passenger 3. <517d23e>
41
+ [postgresql] Fixed an issue with PostgreSQL messing with casing on us. <72e63b5>
42
+ [resque] Merge pull request #174 from messick/resque_fix <dc70092>
43
+ [resque] Merge pull request #179 from messick/resque_redis_fix <d2d436c>
44
+ [resque] connect to redis_master role, rather than just redis <f778dc8>
45
+ [resque] explicitly make Rubber.root a string <c0852b2>
46
+ [resque] placeholder config for dev <d855381>
47
+ [resque] rails_env was changed to Rubber.env a while ago I believe <2376db2>
48
+ [torquebox] Ensure unzip package present for torquebox <7f4d7aa>
49
+ [zookeeper] fix zookeeper server ids <3273b53>
50
+
1
51
  2.0.5 (06/12/2012)
2
52
  ------------------
3
53
 
@@ -56,28 +56,25 @@ module Rubber
56
56
  parameter "COMMAND ...", "the command to run"
57
57
 
58
58
  def execute
59
- self.rootdir ||= Rubber.root
60
- self.logfile ||= "#{Rubber.root}/log/cron-sh-#{Time.now.tv_sec}.log"
61
-
62
59
  cmd = command_list
60
+ self.rootdir ||= Rubber.root
61
+ ident = cmd[0].gsub(/\W+/, "_").gsub(/(^_+)|(_+$)/, '')[0..19]
62
+ self.logfile ||= "#{Rubber.root}/log/cron-sh-#{ident}.log"
63
63
  log = logfile
64
64
 
65
65
  if task?
66
- ident = cmd[0].gsub(/\W+/, "_").gsub(/(^_+)|(_+$)/, '')[0..19]
67
- log = "#{rootdir}/log/cron-task-#{ident}-#{Time.now.tv_sec}.log"
66
+ log = "#{rootdir}/log/cron-task-#{ident}.log"
68
67
  cmd = ["rubber"] + cmd
69
68
  elsif ruby?
70
69
  ruby_code = cmd.join(' ')
71
70
  ident = ruby_code.gsub(/\W+/, "_").gsub(/(^_+)|(_+$)/, '')[0..19]
72
- log = "#{rootdir}/log/cron-ruby-#{ident}-#{Time.now.tv_sec}.log"
71
+ log = "#{rootdir}/log/cron-ruby-#{ident}.log"
73
72
  cmd = ["ruby", "-e", ruby_code]
74
73
  elsif runner?
75
- ident = cmd[0].gsub(/\W+/, "_").gsub(/(^_+)|(_+$)/, '')[0..19]
76
- log = "#{rootdir}/log/cron-runner-#{ident}-#{Time.now.tv_sec}.log"
74
+ log = "#{rootdir}/log/cron-runner-#{ident}.log"
77
75
  cmd = ["rails", "runner"] + cmd
78
76
  elsif rake?
79
- ident = cmd[0].gsub(/\W+/, "_").gsub(/(^_+)|(_+$)/, '')[0..19]
80
- log = "#{rootdir}/log/cron-rake-#{ident}-#{Time.now.tv_sec}.log"
77
+ log = "#{rootdir}/log/cron-rake-#{ident}.log"
81
78
  cmd = ["rake"] + cmd
82
79
  end
83
80
 
@@ -99,7 +96,8 @@ module Rubber
99
96
  # Open4 forks, which JRuby doesn't support. But JRuby added a popen4-compatible method on the IO class,
100
97
  # so we can use that instead.
101
98
  status = (defined?(JRUBY_VERSION) ? IO : Open4).popen4(*cmd) do |pid, stdin, stdout, stderr|
102
- File.open(log, "w") do | fh |
99
+ File.open(log, "a") do | fh |
100
+ fh.puts "\nrubber:cron running #{cmd.inspect} at #{Time.now}\n"
103
101
  threads = []
104
102
  threads << Thread.new(stdout) do |stdout|
105
103
  stdout.each { |line| $stdout.puts line if echoout?; fh.print line; fh.flush }
@@ -1,3 +1,4 @@
1
+ require 'date'
1
2
 
2
3
  module Rubber
3
4
  module Commands
data/lib/rubber/dns.rb CHANGED
@@ -4,10 +4,35 @@ module Rubber
4
4
  module Dns
5
5
 
6
6
  def self.get_provider(provider, env)
7
- require "rubber/dns/#{provider}"
8
- clazz = Rubber::Dns.const_get(provider.capitalize)
9
- provider_env = env.dns_providers[provider]
10
- return clazz.new(provider_env)
7
+
8
+ if provider == 'fog'
9
+ # TODO: remove backwards compatibility in next major release
10
+
11
+ puts "deprecated dns provider config: #{env.inspect}"
12
+ provider_env = env.dns_providers['fog']
13
+ creds = provider_env.credentials
14
+ real_provider = creds.provider
15
+ require "rubber/dns/#{real_provider}"
16
+ clazz = Rubber::Dns.const_get(real_provider.capitalize)
17
+ case real_provider
18
+ when 'aws'
19
+ provider_env['access_key'] = creds['aws_access_key_id']
20
+ provider_env['access_secret'] = creds['aws_secret_access_key']
21
+ when 'zerigo'
22
+ provider_env['email'] = creds['zerigo_email']
23
+ provider_env['token'] = creds['zerigo_token']
24
+ end
25
+ return clazz.new(provider_env)
26
+
27
+ else
28
+
29
+ require "rubber/dns/#{provider}"
30
+ clazz = Rubber::Dns.const_get(provider.capitalize)
31
+ provider_env = env.dns_providers[provider]
32
+ return clazz.new(provider_env)
33
+
34
+ end
35
+
11
36
  end
12
37
 
13
38
  end
@@ -0,0 +1,181 @@
1
+ require 'rubygems'
2
+ require 'fog'
3
+
4
+ module Rubber
5
+ module Dns
6
+
7
+ class Aws < Base
8
+
9
+ attr_accessor :client
10
+
11
+ def initialize(env)
12
+ super(env)
13
+ creds = { :provider => 'aws', :aws_access_key_id => env.access_key, :aws_secret_access_key => env.access_secret }
14
+ @client = ::Fog::DNS.new(creds)
15
+ end
16
+
17
+ def normalize_name(name, domain)
18
+ domain = domain.gsub(/\.$/, "")
19
+
20
+ if name
21
+ name = name.gsub(/\.$/, "")
22
+ name = name.gsub(/.?#{domain}$/, "")
23
+ # Route 53 encodes asterisks in their ASCII octal representation.
24
+ name = name.gsub("\\052", "*")
25
+ end
26
+
27
+ return name, domain
28
+ end
29
+
30
+ def denormalize_name(name, domain)
31
+ if ! name || name.strip.empty?
32
+ name = "#{domain}"
33
+ else
34
+ name = "#{name}.#{domain}"
35
+ end
36
+
37
+ name = "#{name}."
38
+ domain = "#{domain}."
39
+
40
+ return name, domain
41
+ end
42
+
43
+ # Convert from fog/aws model to rubber option hash that represents a dns record
44
+ def host_to_opts(host)
45
+ opts = {}
46
+
47
+ opts[:host] ||= host.name || ''
48
+ opts[:domain] ||= host.zone.domain
49
+ opts[:host], opts[:domain] = normalize_name(opts[:host], opts[:domain])
50
+
51
+ opts[:type] ||= host.type
52
+ opts[:ttl] ||= host.ttl.to_i if host.ttl
53
+
54
+ opts[:data] ||= []
55
+ if host.type =~ /MX/i
56
+ host.value.each do |val|
57
+ parts = val.split(" ")
58
+ opts[:data] << {'priority' => parts[0], 'value' => parts[1]}
59
+ end
60
+ elsif ! host.alias_target.nil?
61
+ # Convert from camel-case to snake-case for Route 53 ALIAS records
62
+ # so the match the rubber config format.
63
+ opts[:data] << {
64
+ 'hosted_zone_id' => host.alias_target['HostedZoneId'],
65
+ 'dns_name' => host.alias_target['DNSName'].split('.')[0..-1].join('.')
66
+ }
67
+ # Route 53 ALIAS records do not have a TTL, so delete the rubber-supplied default value.
68
+ opts.delete(:ttl)
69
+ else
70
+ opts[:data].concat(Array(host.value))
71
+ end
72
+
73
+ return opts
74
+ end
75
+
76
+ # Convert from rubber option hash that represents a dns record to fog/aws model
77
+ def opts_to_host(opts)
78
+ host = {}
79
+ host[:name], domain = denormalize_name(opts[:host], opts[:domain])
80
+
81
+ host[:type] = opts[:type]
82
+ host[:ttl] = opts[:ttl] if opts[:ttl]
83
+
84
+ if opts[:data]
85
+ # Route 53 requires the priority to be munged with the data value.
86
+ if host[:type] =~ /MX/i
87
+ host[:value] = opts[:data].collect {|o| "#{o[:priority]} #{o[:value]}"}
88
+ elsif opts[:data].first.is_a?(Hash)
89
+ # Route 53 allows creation of ALIAS records, which will always be
90
+ # a Hash in the DNS config. ALIAS records cannot have a TTL.
91
+ host[:alias_target] = opts[:data].first
92
+ host.delete(:value)
93
+ host.delete(:ttl)
94
+ else
95
+ host[:value] = opts[:data]
96
+ end
97
+ end
98
+
99
+ return host
100
+ end
101
+
102
+ def find_or_create_zone(domain)
103
+ zone = @client.zones.all.find {|z| z.domain =~ /^#{domain}\.?/}
104
+ if ! zone
105
+ zone = @client.zones.create(:domain => domain)
106
+ end
107
+ return zone
108
+ end
109
+
110
+ def all_hosts(zone)
111
+ hosts = []
112
+ opts = {}
113
+ has_more = true
114
+
115
+ while has_more
116
+ all_hosts = zone.records.all(opts)
117
+ has_more = all_hosts.is_truncated
118
+ opts = {:name => all_hosts.next_record_name,
119
+ :type => all_hosts.next_record_type
120
+ }
121
+ hosts.concat(all_hosts)
122
+ end
123
+
124
+ return hosts
125
+ end
126
+
127
+
128
+ def find_hosts(opts = {})
129
+ opts = setup_opts(opts, [:host, :domain])
130
+ result = []
131
+ zone = find_or_create_zone(opts[:domain])
132
+ host = opts_to_host(opts)
133
+
134
+ if opts[:host] && opts[:host] != '*'
135
+ found_host = zone.records.all(:name => host[:name], :type => host[:type], :max_items => 1).first
136
+ found_host = nil if found_host.name != "#{host[:name]}." && found_host.type != host[:type]
137
+ hosts = Array(found_host)
138
+ else
139
+ hosts = all_hosts(zone)
140
+ end
141
+
142
+ hosts = hosts.select {|h| h.name == host[:name] } if opts.has_key?(:host) && opts[:host] != '*'
143
+ hosts = hosts.select {|h| h.type == host[:type] } if opts.has_key?(:type) && opts[:type] != '*'
144
+
145
+ return hosts
146
+ end
147
+
148
+ def find_host_records(opts = {})
149
+ hosts = find_hosts(opts)
150
+ result = hosts.collect {|h| host_to_opts(h).merge(:domain => opts[:domain]) }
151
+ return result
152
+ end
153
+
154
+ def create_host_record(opts = {})
155
+ opts = setup_opts(opts, [:host, :data, :domain, :type, :ttl])
156
+ zone = find_or_create_zone(opts[:domain])
157
+ zone.records.create(opts_to_host(opts))
158
+ end
159
+
160
+ def destroy_host_record(opts = {})
161
+ opts = setup_opts(opts, [:host, :domain])
162
+
163
+ find_hosts(opts).each do |h|
164
+ h.destroy || raise("Failed to destroy #{h.hostname}")
165
+ end
166
+ end
167
+
168
+ def update_host_record(old_opts={}, new_opts={})
169
+ old_opts = setup_opts(old_opts, [:host, :domain, :type])
170
+ new_opts = setup_opts(new_opts, [:host, :domain, :type, :data])
171
+ new_host = opts_to_host(new_opts)
172
+
173
+ host = find_hosts(old_opts).first
174
+ result = host.modify(new_host)
175
+ result || raise("Failed to update host #{host.name}, #{host.errors.full_messages.join(', ')}")
176
+ end
177
+
178
+ end
179
+
180
+ end
181
+ end
@@ -10,8 +10,8 @@ module Rubber
10
10
  class Nettica < Base
11
11
 
12
12
  def initialize(env)
13
- super(env, "nettica")
14
- @client = ::Nettica::Client.new(provider_env.user, provider_env.password)
13
+ super(env)
14
+ @client = ::Nettica::Client.new(env.user, env.password)
15
15
  end
16
16
 
17
17
  def check_status(response)
@@ -31,13 +31,12 @@ module Rubber
31
31
  return response
32
32
  end
33
33
 
34
- def find_host_records(opts = {})
34
+ def find_hosts(opts = {})
35
35
  opts = setup_opts(opts, [:host, :domain])
36
36
 
37
37
  result = []
38
38
  hn = opts[:host]
39
39
  ht = opts[:type]
40
- hd = opts[:data]
41
40
 
42
41
  domain_info = find_or_create_zone(opts[:domain])
43
42
 
@@ -49,37 +48,49 @@ module Rubber
49
48
  if ht && h.recordType != ht && ht != '*'
50
49
  keep = false
51
50
  end
52
- if hd && h.data != hd
53
- keep = false
54
- end
55
- result << record_to_opts(h) if keep
51
+ result << h if keep
56
52
  end
57
53
 
58
54
  return result
59
55
  end
60
56
 
57
+ def find_host_records(opts = {})
58
+ hosts = find_hosts(opts)
59
+ group = {}
60
+ hosts.each do |h|
61
+ key = "#{h.hostName}.#{h.domainName} #{h.recordType}"
62
+ group[key] ||= []
63
+ group[key] << h
64
+ end
65
+ result = group.values.collect {|h| hosts_to_opts(h).merge(:domain => opts[:domain])}
66
+ return result
67
+ end
68
+
61
69
  def create_host_record(opts = {})
62
70
  opts = setup_opts(opts, [:host, :data, :domain, :type, :ttl])
63
71
  find_or_create_zone(opts[:domain])
64
- record = opts_to_record(opts)
65
- check_status @client.add_record(record)
72
+ opts_to_hosts(opts).each do |host|
73
+ check_status @client.add_record(host)
74
+ end
66
75
  end
67
76
 
68
77
  def destroy_host_record(opts = {})
69
- find_host_records(opts).each do |h|
70
- record = opts_to_record(h)
71
- check_status @client.delete_record(record)
78
+ opts = setup_opts(opts, [:host, :domain])
79
+
80
+ find_hosts(opts).each do |h|
81
+ check_status @client.delete_record(h)
72
82
  end
73
83
  end
74
84
 
75
85
  def update_host_record(old_opts = {}, new_opts = {})
76
- old_opts = setup_opts(old_opts, [:host, :domain])
77
- new_opts = setup_opts(new_opts.merge(:no_defaults =>true), [])
78
- find_host_records(old_opts).each do |h|
79
- old_record = opts_to_record(h)
80
- new_record = opts_to_record(h.merge(new_opts))
81
- check_status @client.update_record(old_record, new_record)
82
- end
86
+ old_opts = setup_opts(old_opts, [:host, :domain, :type])
87
+ new_opts = setup_opts(new_opts, [:host, :domain, :type, :data])
88
+
89
+ # Tricky to update existing hosts since nettica needs a separate host
90
+ # entry for multiple records of same type (MX, etc), so take the easy
91
+ # way out and destroy/create instead of update
92
+ destroy_host_record(old_opts)
93
+ create_host_record(new_opts)
83
94
  end
84
95
 
85
96
  private
@@ -95,27 +106,54 @@ module Rubber
95
106
  end
96
107
  return domain_info
97
108
  end
109
+
110
+ # multiple hosts with same name/type convert to a single rubber-dns.yml opts format
111
+ def hosts_to_opts(hosts)
112
+ opts = {}
113
+
114
+ hosts.each do |record|
115
+ opts[:host] ||= record.hostName || ''
116
+ opts[:domain] ||= record.domainName
117
+ opts[:type] ||= record.recordType
118
+ opts[:ttl] ||= record.tTL if record.tTL
119
+
120
+ opts[:data] ||= []
121
+ if opts[:type] =~ /MX/i
122
+ opts[:data] << {:priority => record.priority, :value => record.data}
123
+ else
124
+ opts[:data] << record.data
125
+ end
126
+ end
127
+
128
+ return opts
129
+ end
98
130
 
99
- def opts_to_record(opts)
100
- record = @client.create_domain_record(opts[:domain],
131
+ # a single rubber-dns.yml opts format converts to multiple hosts with same name/type
132
+ def opts_to_hosts(opts)
133
+ hosts = []
134
+
135
+ opts[:data].each do |o|
136
+
137
+ data, priority = nil, 0
138
+ if o.kind_of?(Hash) && o[:priority]
139
+ priority = o[:priority]
140
+ data = o[:value]
141
+ else
142
+ data = o
143
+ end
144
+
145
+ host = @client.create_domain_record(opts[:domain],
101
146
  opts[:host],
102
147
  opts[:type],
103
- opts[:data],
148
+ data,
104
149
  opts[:ttl],
105
- opts[:priority] || 0)
106
- return record
107
- end
108
-
109
- def record_to_opts(record)
110
- opts = {}
111
- opts[:host] = record.hostName || ''
112
- opts[:domain] = record.domainName
113
- opts[:type] = record.recordType
114
- opts[:data] = record.data if record.data
115
- opts[:ttl] = record.tTL if record.tTL
116
- opts[:priority] = record.priority if record.priority
117
- return opts
150
+ priority)
151
+ hosts << host
152
+ end
153
+
154
+ return hosts
118
155
  end
156
+
119
157
  end
120
158
 
121
159
  end