cloudblocks 0.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.
data/bin/chief ADDED
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'yaml'
4
+ require File.expand_path("../../lib/config-chief", __FILE__)
5
+ require 'optparse'
6
+ begin
7
+ gem 'eventmachine', '~>1.0.0.beta.4'
8
+ gem 'faye', '~>0.8.0'
9
+
10
+ require 'eventmachine'
11
+ require 'faye'
12
+ rescue LoadError => exc
13
+ warn "Cannot find required ruby gems needed to run this agent. Please install cloudblocks gem by running 'gem install cloudblocks'"
14
+ warn exc
15
+ exit -1
16
+ end
17
+
18
+ def stop
19
+ puts "Stopping"
20
+ EM.stop
21
+ exit 0
22
+ end
23
+
24
+ config_file = 'cloudblocks.yaml'
25
+ config_dir = File.join(Dir.home, '.cloudblocks')
26
+ config_full = File.join(config_dir, config_file)
27
+
28
+ api_key = ''
29
+ faye_url = 'https://socket.thecloudblocks.com:8443/'
30
+
31
+ if File.exists?(config_full)
32
+ # config file present
33
+ config = YAML::load(File.open(config_full))
34
+ api_key = config['api_key']
35
+ else
36
+ # no config file
37
+ puts 'CloudBlocks ConfigChief'
38
+ puts 'Please enter your API key. (you can find it at https://www.thecloudblocks.com/me):'
39
+ api_key = gets
40
+ api_key = api_key.chomp
41
+ if api_key.length != 32
42
+ puts 'Invalid API key'
43
+ exit -1
44
+ else
45
+ if !FileTest::directory?(config_dir)
46
+ Dir.mkdir(config_dir)
47
+ end
48
+ File.open(config_full, 'w+') { |out| YAML::dump({ 'api_key' => api_key }, out) }
49
+ puts 'Configuration Saved'
50
+ end
51
+ end
52
+
53
+ @version = 1
54
+ str_version = "0.0.#{@version}"
55
+ workspace = nil
56
+ key = nil
57
+ is_test = false
58
+ parameters = {}
59
+ url = nil
60
+ id = nil
61
+ OptionParser.new do |opts|
62
+ opts.banner = <<-EOF
63
+ ConfigChief. v#{str_version} (c) 2012 CloudBlocks
64
+ For more information please visit http://www.thecloudblocks.com
65
+
66
+ Usage: chief [options]
67
+
68
+ Options:
69
+ EOF
70
+
71
+ opts.on("-w", "--workspace WORKSPACE", "Workspace id") do |v|
72
+ workspace = v
73
+ end
74
+
75
+ opts.on('-u', '--url URL', 'Server URL') do |v|
76
+ url = v
77
+ end
78
+ url = url || 'https://api.thecloudblocks.com'
79
+
80
+ opts.on('-k', '--key KEY', 'Config Key') do |v|
81
+ key = v
82
+ end
83
+
84
+ opts.on('--api-key APIKEY', 'API key') do |v|
85
+ api_key = v
86
+ end
87
+
88
+ opts.on('-i', '--id ID', 'Config Id') do |v|
89
+ id = v
90
+ end
91
+
92
+ opts.on('-p', '--parameters P1=V1,P2=V2,...', Array, 'Optional Parameters') do |v|
93
+ v.each do |p|
94
+ pv = p.split('=')
95
+ if pv.count != 2 || pv[0].nil? || pv[1].nil?
96
+ warn 'Invalid Parameters. Use -h for help'
97
+ exit -1
98
+ end
99
+
100
+ parameters = parameters.merge({pv[0].to_sym => pv[1]})
101
+ end
102
+ end
103
+
104
+ opts.on('-t', '--test', 'Test mode. Shows updates received from the server') do |v|
105
+ is_test = true
106
+ end
107
+
108
+ opts.on_tail("-h", "--help", "Show this message") do
109
+ puts opts
110
+ exit 0
111
+ end
112
+ end.parse!
113
+
114
+ if workspace.nil?
115
+ warn 'No workspace specified. Use --help for more info'
116
+ exit -1
117
+ end
118
+
119
+ if key.nil? && id.nil? && !is_test
120
+ warn 'Either id (-i) or key (-k) have to be present'
121
+ exit -1
122
+ end
123
+
124
+ if is_test
125
+ EM.run {
126
+ Signal.trap('INT') { stop }
127
+ Signal.trap('TERM'){ stop }
128
+
129
+ puts "Listening for /chief/keys/#{workspace} now on #{faye_url}"
130
+ client = Faye::Client.new(faye_url)
131
+ client.subscribe("/chief/keys/#{workspace}") do |message|
132
+ puts message.inspect
133
+ end
134
+ }
135
+ else
136
+ chief = ConfigChief.new(:api_key => api_key, :workspace => workspace, :url => url)
137
+ if (key.nil?)
138
+ puts chief.get_by_id(id, {}, parameters)
139
+ else
140
+ # let's get to bussiness
141
+ puts chief.get_by_key(key, {}, parameters)
142
+ end
143
+ end
data/bin/quartz ADDED
@@ -0,0 +1,414 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'yaml'
4
+ require File.expand_path("../../lib/cloud-quartz", __FILE__)
5
+ require 'optparse'
6
+ require 'socket'
7
+ require 'logger'
8
+ begin
9
+ gem 'eventmachine', '~>1.0.0.beta.4'
10
+ gem 'faye', '~>0.8.0'
11
+
12
+ require 'eventmachine'
13
+ require 'faye'
14
+ rescue LoadError => exc
15
+ warn "Cannot find required ruby gems needed to run this agent. Please install cloudblocks gem by running 'gem install cloudblocks'"
16
+ warn exc
17
+ exit -1
18
+ end
19
+
20
+ private
21
+
22
+ def save_config
23
+ if !FileTest::directory?(@config_dir)
24
+ Dir.mkdir(@config_dir)
25
+ end
26
+ File.open(@config_full, 'w+') { |out| YAML::dump({ 'api_key' => @api_key, 'agent_id' => @agent_id }, out) }
27
+ end
28
+
29
+ public
30
+
31
+ @load_path = File.expand_path(File.join(File.dirname(__FILE__), '../lib/plugins'))
32
+
33
+ def start
34
+ begin
35
+ @log.info "Starting CloudQuartz agent"
36
+ puts "Starting CloudQuartz agent"
37
+
38
+ pid = get_pid
39
+ if pid != 0
40
+ warn "Quartz is already running. Use stop command to stop it or --help for more info"
41
+ exit -1
42
+ end
43
+
44
+ check_version
45
+ load_plugins
46
+
47
+ @log.info @quartz.status(1, @version, plugin_meta_data)
48
+ rescue => exc
49
+ @log.error exc.message
50
+ exit -1
51
+ end
52
+
53
+ if @daemon_mode
54
+ pid = fork {
55
+ run
56
+ }
57
+
58
+ begin
59
+ file = File.new(@pid_full, "w")
60
+ file.write(pid)
61
+ file.close
62
+ Process.detach(pid)
63
+ rescue => exc
64
+ Process.kill('TERM', pid)
65
+ warn "Cannot start CloudQuartz agent: #{exc.message}"
66
+ end
67
+ else
68
+ run
69
+ end
70
+
71
+ exit 0
72
+ end
73
+
74
+ def stop
75
+ pid = get_pid
76
+ @log.info "Stopping CloudQuartz agent"
77
+ begin
78
+ @quartz.status(2, @version, plugin_meta_data)
79
+ EM.stop
80
+ rescue
81
+ end
82
+
83
+ if pid != 0
84
+ begin
85
+ Process.kill('HUP', pid.to_i)
86
+ rescue
87
+ end
88
+ File.delete(@pid_full)
89
+ puts "Stopped"
90
+ else
91
+ warn "Quartz is not running"
92
+ exit -1
93
+ end
94
+ end
95
+
96
+ def register
97
+ puts "Registering agent with #{@url} and API Key #{@api_key}"
98
+ os_name = RUBY_PLATFORM
99
+ os_id = os_name.include?('darwin') ? 5 : 1
100
+ agent = { :agent_type_id => os_id, :agent_name => Socket.gethostname, :agent_timezone => Time.new.zone, :extra => os_name}
101
+
102
+ result = @quartz.register(agent)
103
+ if result['ok']
104
+ @agent_id = result['uid']
105
+ puts "Registered with id #{@agent_id}"
106
+ save_config
107
+ else
108
+ puts "Failed to register due to #{result['error']}"
109
+ exit -1
110
+ end
111
+ end
112
+
113
+ def unregister
114
+ puts "Unregister agent #{@agent_id} with #{@url}"
115
+ @agent_id = ""
116
+ @quartz.unregister(@agent_id)
117
+ save_config
118
+ end
119
+
120
+ private
121
+
122
+ def load_plugins
123
+ @log.info "Loading plugins from #{@load_path}"
124
+
125
+ files = Dir.glob("#{@load_path}/*.rb")
126
+ files.each do |file|
127
+ if file != 'quartz_plugin'
128
+ # is it a valid plugin?
129
+ require "#{file}"
130
+ classname = File.basename(file, '.rb').capitalize
131
+ begin
132
+ clazz = Kernel.const_get(classname)
133
+ if clazz.ancestors[1].name == 'QuartzPlugin'
134
+ instance = clazz.new(@log, { :api_key => @api_key, :agent_id => @agent_id })
135
+ guid = instance.info[:uid]
136
+ @plugins = @plugins.merge({ guid => instance })
137
+ @log.info "Found plugin #{instance.info[:name]}/#{instance.info[:version]} with uid #{guid}"
138
+ end
139
+ rescue
140
+ end
141
+ end
142
+ end
143
+
144
+ @log.debug "All plugins #{plugin_meta_data}"
145
+ end
146
+
147
+ def plugin_meta_data
148
+ result = []
149
+ @plugins.each do |k, v|
150
+ result << v.info
151
+ end
152
+
153
+ result
154
+ end
155
+
156
+ def get_job
157
+ result = @quartz.get_job
158
+ if result['ok']
159
+ if result['empty']
160
+ @log.debug 'No jobs to run'
161
+ else
162
+ message = JSON.parse(result['message'])
163
+ guid = message['plugin_uid']
164
+ name = message['template_name']
165
+ drt = message['desired_run_time']
166
+
167
+ @log.info "Going to run #{name} (uid:#{guid})"
168
+
169
+ # get the plugin
170
+ if @plugins.include?(guid)
171
+ plugin = @plugins[guid]
172
+ # run it
173
+ operation = proc { run_plugin(plugin, message) }
174
+ EM.defer(operation)
175
+ else
176
+ @log.error "No plugin found with uid #{guid}"
177
+ # TODO: Send this back to server as run error
178
+ end
179
+ end
180
+ else
181
+ @log.error "Failed to retrieve job due to #{result['error']}"
182
+ end
183
+ end
184
+
185
+ def run_plugin(plugin, message)
186
+ run_start = Time.now.utc.to_i
187
+ begin
188
+ job_id = message['job_id']
189
+ result = plugin.run(message)
190
+ @log.debug "Run returned for job #{job_id} with #{result}"
191
+ @log.debug result
192
+ ok = result[:ok]
193
+ to_return = result[:message]
194
+ rescue => exc
195
+ @log.error "Failure during running plugin #{plugin} due to #{exc}"
196
+ ok = false
197
+ if result.nil?
198
+ to_return = exc.message
199
+ else
200
+ to_return = result[:message]
201
+ end
202
+ ensure
203
+ data = { :run_start => run_start, :run_end => Time.now.utc.to_i, :agent_uid => @agent_id, :ok => ok }
204
+ data = ok ? data.merge({ :run_result => to_return }) : data.merge({ :fail_reason => to_return })
205
+ begin
206
+ @log.debug "Posting results for job #{job_id} back to the server #{data}"
207
+ @quartz.post_results(job_id, data)
208
+ rescue => e
209
+ @log.error "Failed to post results back to server due to #{e}"
210
+ end
211
+ end
212
+ end
213
+
214
+ def check_version
215
+ begin
216
+ result = @quartz.check_version
217
+ if result['ok']
218
+ latest = result['latest']
219
+ @log.warn 'A newer version of CloudQuartz agent is available. Update the cloudblocks gem. See http://help.thecloudblocks.com for more info' if latest > @version
220
+ end
221
+ rescue => exc
222
+ warn "Cannot connect to the server"
223
+ exit -1
224
+ end
225
+ end
226
+
227
+ def run
228
+ EM.run{
229
+ Signal.trap('INT') { stop }
230
+ Signal.trap('TERM'){ stop }
231
+
232
+ if @realtime
233
+ @log.info "Listening to realtime nofitifications from /quartz/jobs/#{@agent_id} on #{@faye_url}"
234
+ client = Faye::Client.new(@faye_url)
235
+ client.subscribe("/quartz/jobs/#{@agent_id}") do |message|
236
+ @log.info "Got realtime notice for a new job #{message}"
237
+ get_job
238
+ end
239
+ else
240
+ @log.info "Checking for new jobs every 5 seconds"
241
+ EM.add_periodic_timer 5 do
242
+ get_job
243
+ end
244
+ end
245
+ }
246
+ end
247
+
248
+ def get_pid
249
+ if File.exists?(@pid_full)
250
+ file = File.new(@pid_full, "r")
251
+ pid = file.read
252
+ file.close
253
+
254
+ pid
255
+ else
256
+ 0
257
+ end
258
+ end
259
+
260
+ public
261
+
262
+ @version = 1
263
+ str_version = "0.0.#{@version}"
264
+
265
+ config_file = 'cloudblocks.yaml'
266
+ @pid_file = 'quartz.pid'
267
+ @log_file = 'quartz.log'
268
+ @config_dir = File.join(Dir.home, '.cloudblocks')
269
+ @config_full = File.join(@config_dir, config_file)
270
+ @pid_full = File.join('/tmp', @pid_file)
271
+ @log_full = File.join('/tmp', @log_file)
272
+
273
+ @api_key = ''
274
+ @faye_url = 'https://socket.thecloudblocks.com:8443/'
275
+ commands = %w[register unregister start stop]
276
+
277
+ @plugins = {}
278
+
279
+ @url = nil
280
+ @daemon_mode = true
281
+ command = nil
282
+ @agent_id = ''
283
+ @realtime = true
284
+ OptionParser.new do |opts|
285
+ opts.banner = <<-EOF
286
+ CloudQuartz Agent. v#{str_version} (c) 2012 CloudBlocks
287
+ For more information please visit http://www.thecloudblocks.com
288
+
289
+ Usage: quartz [register|unregister|start|stop] [options]
290
+
291
+ Options:
292
+ EOF
293
+
294
+ opts.on('--url URL', 'Server URL') do |server_url|
295
+ @url = server_url
296
+ end
297
+ @url = @url || 'https://api.thecloudblocks.com'
298
+
299
+ opts.on('--agent-id AGENTID', 'Agent id') do |v|
300
+ @agent_id = v
301
+ end
302
+
303
+ opts.on('-d', '--no-daemon', 'Not in daemon mode') do |v|
304
+ @daemon_mode = false
305
+ end
306
+
307
+ opts.on('-p', '--pid PID', 'PID file path') do |v|
308
+ @pid_full = v
309
+ end
310
+
311
+ opts.on('-l', '--log LOG', 'Log file path') do |v|
312
+ @log_full = v
313
+ end
314
+
315
+ opts.on('-c', '--config CONFIG', 'Config file path') do |v|
316
+ @config_full = v
317
+ end
318
+
319
+ opts.on('--sockets SOCKETS', 'Sockets URL') do |v|
320
+ @faye_url = v
321
+ @faye_url = "#{@faye_url}/" if @faye_url[-1] != '/'
322
+ end
323
+
324
+ opts.on('--api-key APIKEY', 'API key') do |v|
325
+ @api_key = v
326
+ end
327
+
328
+ opts.on('-n', '--no-realtime', 'Disable realtime notifications') do |v|
329
+ @realtime = false
330
+ end
331
+
332
+ opts.on_tail("-h", "--help", "Show this message") do
333
+ puts opts
334
+ puts <<-EOF
335
+
336
+ Commands:
337
+ register Register the agent
338
+ start Starts agent as deamon
339
+ stop Stops agent daemon
340
+ unregister Unregister the agent
341
+
342
+ EOF
343
+ exit 0
344
+ end
345
+ end.parse!
346
+
347
+ command = ARGV[0].downcase unless ARGV[0].nil?
348
+
349
+ if @daemon_mode
350
+ @log = Logger.new(@log_full)
351
+ else
352
+ @log = Logger.new(STDOUT)
353
+ end
354
+
355
+ @log.level = Logger::DEBUG
356
+
357
+ if (@api_key.empty? || @agent_id.empty?) && File.exists?(@config_full)
358
+ # config file present
359
+ config = YAML::load(File.open(@config_full))
360
+ @api_key = config['api_key']
361
+ @agent_id = config['agent_id']
362
+ end
363
+
364
+ # still no api key, we need to get it
365
+ if @api_key.empty?
366
+ puts 'CloudBlocks CloudQuartz'
367
+ print 'Please enter your API key. (you can find it at https://www.thecloudblocks.com/me):'
368
+ @api_key = gets.chomp
369
+ if @api_key.length != 32
370
+ puts 'Invalid API key'
371
+ exit -1
372
+ end
373
+
374
+ save_config
375
+ puts 'Configuration Saved'
376
+ end
377
+
378
+ # no agent id?
379
+ if @agent_id.empty?
380
+ print 'Register the agent? [Y/n]'
381
+ if gets.chomp.downcase != 'n'
382
+ @quartz = CloudQuartz.new(:api_key => @api_key, :url => @url)
383
+ register
384
+
385
+ print 'Start the agent as daemon? [Yn]'
386
+ if gets.chomp.downcase != 'n'
387
+ @quartz = CloudQuartz.new(:api_key => @api_key, :url => @url, :agent_id => @agent_id)
388
+ start
389
+ else
390
+ exit 0
391
+ end
392
+ else
393
+ exit 0
394
+ end
395
+ end
396
+
397
+ if command.nil? || command.empty?
398
+ puts 'No command found. Use --help for more information'
399
+ exit -1
400
+ end
401
+
402
+ unless commands.include?(command)
403
+ puts 'Invalid command. Use --help for more information'
404
+ exit -1
405
+ end
406
+
407
+ if (@agent_id.nil? || @agent_id.empty? || @agent_id.empty?) && command != 'register'
408
+ puts 'No Agent id found. Have you registered it yet? Use --help for more information'
409
+ exit -1
410
+ end
411
+
412
+ @quartz = CloudQuartz.new(:api_key => @api_key, :url => @url, :agent_id => @agent_id)
413
+
414
+ send(command)
@@ -0,0 +1,55 @@
1
+ require 'httparty'
2
+ require 'json'
3
+
4
+ class CloudQuartz
5
+ include HTTParty
6
+ @api_key = ""
7
+ @agent_id = ""
8
+
9
+ def initialize(options = {})
10
+ @api_key = options[:api_key]
11
+ @agent_id = options[:agent_id]
12
+ self.class.base_uri options[:url] || 'https://api.thecloudblocks.com'
13
+ end
14
+
15
+ def get_job
16
+ process(self.class.get("/queue/#{@agent_id}.json", { :headers => http_headers } ))
17
+ end
18
+
19
+ def register(agent)
20
+ process(self.class.post('/agent.json', { :headers => http_headers.merge({'Content-Type' => 'application/json'}), :body => agent.to_json }))
21
+ end
22
+
23
+ def unregister(agent)
24
+ process(self.class.delete("/agent/#{agent}", :headers => http_headers))
25
+ end
26
+
27
+ def check_version
28
+ self.class.get("/agent/version", :headers => http_headers)
29
+ end
30
+
31
+ def post_results(job_id, data)
32
+ process(self.class.post("/job/#{job_id}/complete.json", { :headers => http_headers.merge({'Content-Type' => 'application/json'}), :body => data.to_json } ))
33
+ end
34
+
35
+ def status(stat, version, plugins)
36
+ data = { :status => stat, :version => version, :plugins => plugins }
37
+ process(self.class.post("/agent/#{@agent_id}/status.json", { :headers => http_headers.merge({'Content-Type' => 'application/json'}), :body => data.to_json }))
38
+ end
39
+
40
+ private
41
+
42
+ def http_headers
43
+ { 'api_key' => @api_key }
44
+ end
45
+
46
+ def process(response)
47
+ if response.code != 200
48
+ raise response.body
49
+ else
50
+ response.parsed_response
51
+ end
52
+ end
53
+ end
54
+
55
+
@@ -0,0 +1,100 @@
1
+ require 'HTTParty'
2
+ require 'socket'
3
+ require 'json'
4
+
5
+ class ConfigChief
6
+ include HTTParty
7
+
8
+ @api_key = ""
9
+ @workspace = ""
10
+
11
+ def initialize(options = {})
12
+ @api_key = options[:api_key]
13
+ @workspace = options[:workspace]
14
+ self.class.base_uri options[:url] || 'https://api.thecloudblocks.com'
15
+ end
16
+
17
+ def get_by_key(key, options = {}, params = {})
18
+ opts = {
19
+ :default_value => nil,
20
+ :full_obj => false
21
+ }.merge(options)
22
+
23
+ optionals = {}
24
+ params.each do |k, v|
25
+ optionals["cc-#{k}"] = v
26
+ end
27
+
28
+ headers = { :headers => http_headers.merge(optionals) }
29
+
30
+ result = self.class.get("/workspaces/#{@workspace}/value.json", headers.merge(:query => { :query => key } ))
31
+
32
+ if result.response.code.to_i == 200
33
+ retrieved = result.parsed_response
34
+ return retrieved if opts[:full_obj]
35
+ return retrieved['parsed_value']
36
+ end
37
+
38
+ return opts[:default_value]
39
+ end
40
+
41
+ def get_by_id(id, options = {}, params = {})
42
+ opts = {
43
+ :default_value => nil,
44
+ :full_obj => false
45
+ }.merge(options)
46
+
47
+ optionals = {}
48
+ params.each do |k, v|
49
+ optionals["cc-#{k}"] = v
50
+ end
51
+
52
+ headers = { :headers => http_headers.merge(optionals) }
53
+
54
+ result = self.class.get("/workspaces/#{@workspace}/config_keys/#{id}.json", headers )
55
+
56
+ if result.response.code.to_i == 200
57
+ retrieved = result.parsed_response
58
+ return retrieved if opts[:full_obj]
59
+ return retrieved['parsed_value']
60
+ end
61
+
62
+ return opts[:default_value]
63
+ end
64
+
65
+ def workspaces
66
+ self.class.get("/workspaces.json", { :headers => http_headers } )
67
+ end
68
+
69
+ def config_items(query = '*')
70
+ headers = { :headers => http_headers }
71
+ self.class.get("/workspaces/#{@workspace}/config_keys.json", headers.merge(:query => { :query => query } ))
72
+ end
73
+
74
+ def set_value(key, value)
75
+ headers = { :headers => http_headers.merge({'Content-Type' => 'application/json'}) }
76
+ self.class.post("/workspaces/#{@workspace}/config_keys.json", headers.merge(:body => { :key => key, :value => value }.to_json))
77
+ end
78
+
79
+ def register_node(options = {})
80
+ headers = { :headers => http_headers.merge({'Content-Type' => 'application/json'}) }
81
+ self.class.post("/workspaces/#{@workspace}/node.json", headers.merge(:body => options.to_json))
82
+ end
83
+
84
+ def unregister_node(node_uid)
85
+ headers = { :headers => http_headers }
86
+ self.class.delete("/workspaces/#{@workspace}/node/#{node_uid}.json", headers)
87
+ end
88
+
89
+ def update_node_status(node_uid, status)
90
+ headers = { :headers => http_headers.merge({'Content-Type' => 'application/json'}) }
91
+ self.class.post("/workspaces/#{@workspace}/node/#{node_uid}/status.json", headers.merge({ :body => { :status => status}.to_json}))
92
+ end
93
+
94
+ private
95
+
96
+ def http_headers
97
+ { 'api_key' => @api_key, 'ConfigChief-Node' => Socket.gethostname }
98
+ end
99
+
100
+ end
@@ -0,0 +1,14 @@
1
+ require File.join(File.dirname(__FILE__), 'quartz_plugin')
2
+
3
+ class Broken < QuartzPlugin
4
+ def info
5
+ { :uid => "04165a45fde840a9a17b41f019b3dca3", :name => "Broken", :version => "0.0.0" }
6
+ end
7
+
8
+ def run(message)
9
+ @log.info "Running with #{message}"
10
+ @log.info "This is a failure"
11
+
12
+ run_result(false, "Boo! It's broken")
13
+ end
14
+ end
@@ -0,0 +1,85 @@
1
+ require File.join(File.dirname(__FILE__), 'quartz_plugin')
2
+ require 'fileutils'
3
+
4
+ class Mysql < QuartzPlugin
5
+
6
+ def info
7
+ { :uid => "67deb35a555344c8a7651c656e6c8e2e", :name => "MySQL Backup", :version => "0.0.0" }
8
+ end
9
+
10
+ def run(message)
11
+ pl = payload(message)
12
+ pl = pl.select { |k,v| !v.nil? && !v.empty? }
13
+
14
+ @log.debug "Pruned payload #{pl}"
15
+
16
+ @job_name = pl['job_name'].gsub(/[^\w\s_-]+/, '').gsub(/(^|\b\s)\s+($|\s?\b)/, '\\1\\2').gsub(/\s/, '_')
17
+ @mysqldump_utility = pl['dump utility'] || '/usr/bin/mysqldump'
18
+ @name = pl['db name'] || :all
19
+ @username = pl['username']
20
+ @password = pl['password']
21
+ @socket = pl['socket']
22
+ @host = pl['host']
23
+ @port = pl['port']
24
+ @skip_tables = pl['skip tables']
25
+ @only_tables = pl['only tables']
26
+ @additional_options = pl['additional options'] || ['--single-transaction', '--quick']
27
+ @path = pl['backup folder']
28
+
29
+ dump_cmd = "#{mysqldump} | gzip > '#{ File.join(@path, @job_name.downcase) }.sql.gz'"
30
+ @log.debug "Running #{dump_cmd}"
31
+
32
+ FileUtils.mkdir_p(@path)
33
+
34
+ result = run_shell dump_cmd
35
+ if result[:ok]
36
+ run_result(true, "MySQL Backup finished successfully")
37
+ else
38
+ run_result(false, result[:message])
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ # copied from backup gem with little mods
45
+ def mysqldump
46
+ "#{ @mysqldump_utility } #{ credential_options } #{ connectivity_options } " +
47
+ "#{ user_options } #{ @name } #{ tables_to_dump } #{ tables_to_skip }"
48
+ end
49
+
50
+ def credential_options
51
+ %w[username password].map do |option|
52
+ value = instance_variable_get("@#{option}")
53
+ next if value.to_s.empty?
54
+ "--#{option}='#{value}'".gsub('--username', '--user')
55
+ end.compact.join(' ')
56
+ end
57
+
58
+ def connectivity_options
59
+ %w[host port socket].map do |option|
60
+ value = instance_variable_get("@#{option}")
61
+ next if value.to_s.empty?
62
+ "--#{option}='#{value}'"
63
+ end.compact.join(' ')
64
+ end
65
+
66
+ def user_options
67
+ @additional_options.join(' ') unless @additional_options.nil?
68
+ end
69
+
70
+ def tables_to_dump
71
+ @only_tables.join(' ') unless @only_tables.nil? || dump_all?
72
+ end
73
+
74
+ def tables_to_skip
75
+ return '' if @skip_tables.nil?
76
+ @skip_tables.map do |table|
77
+ "--ignore-table='#{@name}.#{table}'"
78
+ end.join(' ') unless dump_all?
79
+ end
80
+
81
+ def dump_all?
82
+ @name == :all
83
+ end
84
+
85
+ end
@@ -0,0 +1,49 @@
1
+ require 'open4'
2
+ require 'json'
3
+
4
+ class QuartzPlugin
5
+
6
+ def initialize(log, options)
7
+ @log = log
8
+ @options = options
9
+ end
10
+
11
+ def run_result(success, message)
12
+ result = { :ok => success, :message => message }
13
+ @log.debug "Job finished with result #{result}"
14
+
15
+ result
16
+ end
17
+
18
+ def payload(message)
19
+ @log.debug "Message #{message}"
20
+ raw_payload = message['payload']
21
+ @log.debug "Payload #{raw_payload}"
22
+ parsed_payload = JSON.parse(raw_payload) unless raw_payload.nil?
23
+ @log.debug "Parsed payload #{parsed_payload}"
24
+
25
+ v = {}
26
+ unless parsed_payload.nil?
27
+ parsed_payload.each do |p|
28
+ v = v.merge({ p['name'] => p['value']})
29
+ end
30
+ end
31
+
32
+ v = v.merge({'job_name' => message['job_name']})
33
+
34
+ @log.debug "Payload #{v}"
35
+
36
+ v
37
+ end
38
+
39
+ def run_shell(command)
40
+ pid, stdin, stdout, stderr = Open4::popen4("#{command}")
41
+ ignored, status = Process::waitpid2 pid
42
+
43
+ if status.exitstatus == 0
44
+ { :ok => true, :message => stdout.read.strip }
45
+ else
46
+ { :ok => false, :message => stderr.read.strip}
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,23 @@
1
+ require File.join(File.dirname(__FILE__), 'quartz_plugin')
2
+
3
+ class Rake < QuartzPlugin
4
+ def info
5
+ { :uid => "62e3583abfc24f209916c4ff97661fa0", :name => "Rake", :version => "0.0.0" }
6
+ end
7
+
8
+ def run(message)
9
+ @log.debug "Running with #{message}"
10
+ payload = payload(message)
11
+ task = payload['task']
12
+ location = payload['location']
13
+ params = payload['params']
14
+ @log.info "Rake #{task} in #{location} with params:#{params}"
15
+
16
+ begin
17
+ result = run_shell("bundle exec rake #{task} #{params}")
18
+ run_result(result[:ok], result[:message])
19
+ rescue => ex
20
+ run_result(false, "Failed to run rake due to #{ex}")
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ require File.join(File.dirname(__FILE__), 'quartz_plugin')
2
+
3
+ class Rake < QuartzPlugin
4
+ def info
5
+ { :uid => "20e07c656e2f477d969e9561e13229fb", :name => "Shell", :version => "0.0.0" }
6
+ end
7
+
8
+ def run(message)
9
+ @log.debug "Running with #{message}"
10
+ payload = payload(message)
11
+ command = payload['command']
12
+ params = payload['params']
13
+ @log.info "Shell command '#{command}' with '#{params}'"
14
+
15
+ begin
16
+ result = run_shell("#{command} #{params}")
17
+ run_result(result[:ok], result[:message])
18
+ rescue => ex
19
+ run_result(false, "Failed to run shell command due to #{ex}")
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ require File.join(File.dirname(__FILE__), 'quartz_plugin')
2
+
3
+ class Tester < QuartzPlugin
4
+ def info
5
+ { :uid => "c0bb6ed7950b489f9abba8071ff0e0ab", :name => "Tester", :version => "0.0.0" }
6
+ end
7
+
8
+ def run(message)
9
+ @log.info "Running with #{message}"
10
+ i = Random.rand(10)
11
+ @log.info "Waiting for #{i} seconds"
12
+ sleep i
13
+ @log.info "Done"
14
+
15
+ run_result(true, "Super! Done in #{i} seconds")
16
+ end
17
+ end
@@ -0,0 +1,34 @@
1
+ require File.join(File.dirname(__FILE__), 'quartz_plugin')
2
+ require 'httparty'
3
+
4
+ class Webget < QuartzPlugin
5
+ def info
6
+ { :uid => "6b5f722d214f4d71a5be237d44094721", :name => "WebGet", :version => "0.0.0" }
7
+ end
8
+
9
+ def run(message)
10
+ @log.debug "Running with #{message}"
11
+ payload = payload(message)
12
+ url = payload['url']
13
+ local = payload['local file']
14
+ @log.info "Webget from #{url} into #{local}"
15
+
16
+ begin
17
+ response = HTTParty.get(url)
18
+ if response.code == 200
19
+ body = response.body
20
+ file = File.new(local, "w")
21
+ begin
22
+ file.write(body)
23
+ run_result(true, "Saved WebGet to local file")
24
+ ensure
25
+ file.close
26
+ end
27
+ else
28
+ run_result(false, response.message)
29
+ end
30
+ rescue => ex
31
+ run_result(false, "Failed to webget due to #{ex}")
32
+ end
33
+ end
34
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cloudblocks
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.6
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Khash Sajadi
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-16 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: httparty
16
+ requirement: &70331935927900 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.8.1
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70331935927900
25
+ - !ruby/object:Gem::Dependency
26
+ name: json
27
+ requirement: &70331935924160 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 1.6.3
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70331935924160
36
+ - !ruby/object:Gem::Dependency
37
+ name: eventmachine
38
+ requirement: &70331935922320 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: 0.12.10
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70331935922320
47
+ - !ruby/object:Gem::Dependency
48
+ name: faye
49
+ requirement: &70331935911040 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 0.8.0
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70331935911040
58
+ - !ruby/object:Gem::Dependency
59
+ name: open4
60
+ requirement: &70331935910460 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: 1.3.0
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: *70331935910460
69
+ description: See http://www.thecloudblocks.com for more info
70
+ email: khash@thecloudblocks.com
71
+ executables:
72
+ - chief
73
+ - quartz
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - lib/config-chief.rb
78
+ - lib/cloud-quartz.rb
79
+ - lib/plugins/broken.rb
80
+ - lib/plugins/mysql.rb
81
+ - lib/plugins/quartz_plugin.rb
82
+ - lib/plugins/rake.rb
83
+ - lib/plugins/shell.rb
84
+ - lib/plugins/tester.rb
85
+ - lib/plugins/webget.rb
86
+ - bin/chief
87
+ - bin/quartz
88
+ homepage: http://www.thecloudblocks.com
89
+ licenses: []
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 1.8.17
109
+ signing_key:
110
+ specification_version: 3
111
+ summary: CloudBlocks Gem and Agent
112
+ test_files: []