cloudblocks 0.0.6

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