rubber 2.0.5 → 2.0.6

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