checkmate 0.1.0

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.
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Peter MacRobert
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,18 @@
1
+ = checkmate
2
+
3
+ Most server monitoring apps are too complicated. Checkmate is designed to be simple, using the power
4
+ of rake and remote_task (extracted from Vlad) to run simple commands on your remote servers.
5
+
6
+ == Note on Patches/Pull Requests
7
+
8
+ * Fork the project.
9
+ * Make your feature addition or bug fix.
10
+ * Add tests for it. This is important so I don't break it in a
11
+ future version unintentionally.
12
+ * Commit, do not mess with rakefile, version, or history.
13
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
14
+ * Send me a pull request. Bonus points for topic branches.
15
+
16
+ == Copyright
17
+
18
+ Copyright (c) 2010 Peter MacRobert. See LICENSE for details.
@@ -0,0 +1,47 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "checkmate"
8
+ gem.summary = %Q{Ruby framework to run maintenance checks on remote servers.}
9
+ gem.description = %Q{ Define your maintenance checks, e.g. disk usage, mysql status, etc. Then run these
10
+ checks on multiple servers using rake, and have the results emailed back to you. Set up a cron job
11
+ to run the rake task on a regular period, and BANG! You've got simple and effective server monitoring. }
12
+ gem.email = "originalpete@gmail.com"
13
+ gem.homepage = "http://github.com/originalpete/checkmate"
14
+ gem.authors = ["Peter MacRobert"]
15
+
16
+ gem.add_development_dependency "yard", ">= 0"
17
+ gem.add_development_dependency "rspec", ">= 1.2.9"
18
+ gem.add_development_dependency "rr", ">= 0.10.5"
19
+ end
20
+ Jeweler::GemcutterTasks.new
21
+ rescue LoadError
22
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
23
+ end
24
+
25
+ require 'spec/rake/spectask'
26
+ Spec::Rake::SpecTask.new(:spec) do |spec|
27
+ spec.libs << 'lib' << 'spec'
28
+ spec.spec_files = FileList['spec/**/*_spec.rb']
29
+ end
30
+
31
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
32
+ spec.libs << 'lib' << 'spec'
33
+ spec.pattern = 'spec/**/*_spec.rb'
34
+ spec.rcov = true
35
+ end
36
+
37
+ task :spec => :check_dependencies
38
+ task :default => :spec
39
+
40
+ begin
41
+ require 'yard'
42
+ YARD::Rake::YardocTask.new
43
+ rescue LoadError
44
+ task :yardoc do
45
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
46
+ end
47
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,2 @@
1
+ module Checkmate
2
+ end
File without changes
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format specdoc
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'spec'
4
+ require 'spec/autorun'
5
+ require 'checkmate'
6
+
7
+ Spec::Runner.configure do |config|
8
+ config.mock_with :rr
9
+ end
@@ -0,0 +1,56 @@
1
+ = rake-remote_task
2
+
3
+ * http://rubyhitsquad.com/
4
+ * http://rubyforge.org/projects/hitsquad/
5
+
6
+ == DESCRIPTION:
7
+
8
+ Vlad the Deployer's sexy brainchild is rake-remote_task, extending
9
+ Rake with remote task goodness.
10
+
11
+ == FEATURES/PROBLEMS:
12
+
13
+ * Run remote commands on one or more servers.
14
+ * Mix and match local and remote tasks.
15
+ * Uses ssh with your ssh settings already in place.
16
+ * Uses rsync for efficient transfers.
17
+
18
+ == SYNOPSIS:
19
+
20
+ remote_task :setup_app, :roles => :app do
21
+ # ...
22
+ run "umask #{umask} && mkdir -p #{dirs.join(' ')}"
23
+ end
24
+
25
+ == REQUIREMENTS:
26
+
27
+ * rake, >= 0.8
28
+
29
+ == INSTALL:
30
+
31
+ * sudo gem install rake-remote_task
32
+
33
+ == LICENSE:
34
+
35
+ (The MIT License)
36
+
37
+ Copyright (c) Ryan Davis, RubyHitSquad
38
+
39
+ Permission is hereby granted, free of charge, to any person obtaining
40
+ a copy of this software and associated documentation files (the
41
+ 'Software'), to deal in the Software without restriction, including
42
+ without limitation the rights to use, copy, modify, merge, publish,
43
+ distribute, sublicense, and/or sell copies of the Software, and to
44
+ permit persons to whom the Software is furnished to do so, subject to
45
+ the following conditions:
46
+
47
+ The above copyright notice and this permission notice shall be
48
+ included in all copies or substantial portions of the Software.
49
+
50
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
51
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
52
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
53
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
54
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
55
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
56
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,630 @@
1
+ require 'rubygems'
2
+ require 'open4'
3
+ require 'rake'
4
+
5
+ $TESTING ||= false
6
+ $TRACE = Rake.application.options.trace
7
+ $-w = true if $TRACE # asshat, don't mess with my warn.
8
+
9
+ [
10
+ ["Thread.current[:task]", :get, :put, :rsync, :run, :sudo, :target_host],
11
+ ["Rake::RemoteTask", :host, :remote_task, :role, :set]
12
+ ].each do |methods|
13
+ receiver = methods.shift
14
+ methods.each do |method|
15
+ eval "def #{method} *args, &block; #{receiver}.#{method}(*args, &block);end"
16
+ end
17
+ end
18
+
19
+ module Rake
20
+ ##
21
+ # Base error class for all Vlad errors.
22
+ class Error < RuntimeError; end
23
+
24
+ ##
25
+ # Raised when you have incorrectly configured Vlad.
26
+ class ConfigurationError < Error; end
27
+
28
+ ##
29
+ # Raised when a remote command fails.
30
+ class CommandFailedError < Error; end
31
+
32
+ ##
33
+ # Raised when an environment variable hasn't been set.
34
+ class FetchError < Error; end
35
+ end
36
+
37
+ ##
38
+ # Rake::RemoteTask is a subclass of Rake::Task that adds
39
+ # remote_actions that execute in parallel on multiple hosts via ssh.
40
+
41
+ class Rake::RemoteTask < Rake::Task
42
+
43
+ VERSION = "2.0.0"
44
+
45
+ @@current_roles = []
46
+
47
+ include Open4
48
+
49
+ ##
50
+ # Options for execution of this task.
51
+
52
+ attr_accessor :options
53
+
54
+ ##
55
+ # The host this task is running on during execution.
56
+
57
+ attr_reader :target_host
58
+
59
+ ##
60
+ # The directory on the host this task is running in during execution.
61
+
62
+ attr_reader :target_dir
63
+
64
+ ##
65
+ # An Array of Actions this host will perform during execution. Use
66
+ # enhance to add new actions to a task.
67
+
68
+ attr_reader :remote_actions
69
+
70
+ def self.current_roles
71
+ @@current_roles
72
+ end
73
+
74
+ ##
75
+ # Create a new task named +task_name+ attached to Rake::Application +app+.
76
+
77
+ def initialize(task_name, app)
78
+ super
79
+
80
+ @remote_actions = []
81
+ @happy = false # used for deprecation warnings on get/put/rsync
82
+ end
83
+
84
+ ##
85
+ # Add a local action to this task. This calls Rake::Task#enhance.
86
+
87
+ alias_method :original_enhance, :enhance
88
+
89
+ ##
90
+ # Add remote action +block+ to this task with dependencies +deps+. See
91
+ # Rake::Task#enhance.
92
+
93
+ def enhance(deps=nil, &block)
94
+ original_enhance(deps) # can't use super because block passed regardless.
95
+ @remote_actions << Action.new(self, block) if block_given?
96
+ self
97
+ end
98
+
99
+ ##
100
+ # Execute this action. Local actions will be performed first, then remote
101
+ # actions will be performed in parallel on each host configured for this
102
+ # RemoteTask.
103
+
104
+ def execute(args = nil)
105
+ raise(Rake::ConfigurationError,
106
+ "No target hosts specified on task #{self.name} for roles #{options[:roles].inspect}") unless
107
+ defined_target_hosts?
108
+
109
+ super args
110
+
111
+ @remote_actions.each { |act| act.execute(target_hosts, self, args) }
112
+ end
113
+
114
+ ##
115
+ # Pull +files+ from the remote +host+ using rsync to +local_dir+.
116
+ # TODO: what if role has multiple hosts & the files overlap? subdirs?
117
+
118
+ def get local_dir, *files
119
+ @happy = true
120
+ host = target_host
121
+ rsync files.map { |f| "#{host}:#{f}" }, local_dir
122
+ @happy = false
123
+ end
124
+
125
+ ##
126
+ # Copy a (usually generated) file to +remote_path+. Contents of block
127
+ # are copied to +remote_path+ and you may specify an optional
128
+ # base_name for the tempfile (aids in debugging).
129
+
130
+ def put remote_path, base_name = File.basename(remote_path)
131
+ require 'tempfile'
132
+ Tempfile.open base_name do |fp|
133
+ fp.puts yield
134
+ fp.flush
135
+ @happy = true
136
+ rsync fp.path, "#{target_host}:#{remote_path}"
137
+ @happy = false
138
+ end
139
+ end
140
+
141
+ ##
142
+ # Execute rsync with +args+. Tacks on pre-specified +rsync_cmd+ and
143
+ # +rsync_flags+.
144
+ #
145
+ # Favor #get and #put for most tasks. Old-style direct use where the
146
+ # target_host was implicit is now deprecated.
147
+
148
+ def rsync *args
149
+ unless @happy || args[-1] =~ /:/ then
150
+ warn "rsync deprecation: pass target_host:remote_path explicitly"
151
+ args[-1] = "#{target_host}:#{args[-1]}"
152
+ end
153
+
154
+ cmd = [rsync_cmd, rsync_flags, args].flatten.compact
155
+ cmdstr = cmd.join ' '
156
+
157
+ warn cmdstr if $TRACE
158
+
159
+ success = system(*cmd)
160
+
161
+ raise Rake::CommandFailedError, "execution failed: #{cmdstr}" unless success
162
+ end
163
+
164
+ ##
165
+ # Use ssh to execute +command+ on target_host. If +command+ uses sudo, the
166
+ # sudo password will be prompted for then saved for subsequent sudo commands.
167
+
168
+ def run command
169
+ command = "cd #{target_dir} && #{command}" if target_dir
170
+ cmd = [ssh_cmd, ssh_flags, target_host, command].flatten
171
+ result = []
172
+
173
+ trace = [ssh_cmd, ssh_flags, target_host, "'#{command}'"].flatten.join(' ')
174
+ warn trace if $TRACE
175
+
176
+ pid, inn, out, err = popen4(*cmd)
177
+
178
+ inn.sync = true
179
+ streams = [out, err]
180
+ out_stream = {
181
+ out => $stdout,
182
+ err => $stderr,
183
+ }
184
+
185
+ # Handle process termination ourselves
186
+ status = nil
187
+ Thread.start do
188
+ status = Process.waitpid2(pid).last
189
+ end
190
+
191
+ until streams.empty? do
192
+ # don't busy loop
193
+ selected, = select streams, nil, nil, 0.1
194
+
195
+ next if selected.nil? or selected.empty?
196
+
197
+ selected.each do |stream|
198
+ if stream.eof? then
199
+ streams.delete stream if status # we've quit, so no more writing
200
+ next
201
+ end
202
+
203
+ data = stream.readpartial(1024)
204
+ out_stream[stream].write data
205
+
206
+ if stream == err and data =~ sudo_prompt then
207
+ inn.puts sudo_password
208
+ data << "\n"
209
+ $stderr.write "\n"
210
+ end
211
+
212
+ result << data
213
+ end
214
+ end
215
+
216
+ unless status.success? then
217
+ raise(Rake::CommandFailedError,
218
+ "execution failed with status #{status.exitstatus}: #{cmd.join ' '}")
219
+ end
220
+
221
+ result.join
222
+ ensure
223
+ inn.close rescue nil
224
+ out.close rescue nil
225
+ err.close rescue nil
226
+ end
227
+
228
+ ##
229
+ # Returns an Array with every host configured.
230
+
231
+ def self.all_hosts
232
+ hosts_for(roles.keys)
233
+ end
234
+
235
+ ##
236
+ # The default environment values. Used for resetting (mostly for
237
+ # tests).
238
+
239
+ def self.default_env
240
+ @@default_env
241
+ end
242
+
243
+ def self.per_thread
244
+ @@per_thread
245
+ end
246
+
247
+ ##
248
+ # The vlad environment.
249
+
250
+ def self.env
251
+ @@env
252
+ end
253
+
254
+ ##
255
+ # Fetches environment variable +name+ from the environment using
256
+ # default +default+.
257
+
258
+ def self.fetch name, default = nil
259
+ name = name.to_s if Symbol === name
260
+ if @@env.has_key? name then
261
+ protect_env(name) do
262
+ v = @@env[name]
263
+ v = @@env[name] = v.call if Proc === v unless per_thread[name]
264
+ v = v.call if Proc === v
265
+ v
266
+ end
267
+ elsif default || default == false
268
+ v = @@env[name] = default
269
+ else
270
+ raise Rake::FetchError
271
+ end
272
+ end
273
+
274
+ ##
275
+ # Add host +host_name+ that belongs to +roles+. Extra arguments may
276
+ # be specified for the host as a hash as the last argument.
277
+ #
278
+ # host is the inversion of role:
279
+ #
280
+ # host 'db1.example.com', :db, :master_db
281
+ #
282
+ # Is equivalent to:
283
+ #
284
+ # role :db, 'db1.example.com'
285
+ # role :master_db, 'db1.example.com'
286
+
287
+ def self.host host_name, *roles
288
+ opts = Hash === roles.last ? roles.pop : {}
289
+
290
+ roles.each do |role_name|
291
+ role role_name, host_name, opts.dup
292
+ end
293
+ end
294
+
295
+ ##
296
+ # Returns an Array of all hosts in +roles+.
297
+
298
+ def self.hosts_for *roles
299
+ roles.flatten.map { |r|
300
+ self.roles[r].keys
301
+ }.flatten.uniq.sort
302
+ end
303
+
304
+ def self.mandatory name, desc # :nodoc:
305
+ self.set(name) do
306
+ raise(Rake::ConfigurationError,
307
+ "Please specify the #{desc} via the #{name.inspect} variable")
308
+ end
309
+ end
310
+
311
+ ##
312
+ # Ensures exclusive access to +name+.
313
+
314
+ def self.protect_env name # :nodoc:
315
+ @@env_locks[name].synchronize do
316
+ yield
317
+ end
318
+ end
319
+
320
+ ##
321
+ # Adds a remote task named +name+ with options +options+ that will
322
+ # execute +block+.
323
+
324
+ def self.remote_task name, *args, &block
325
+ options = (Hash === args.last) ? args.pop : {}
326
+ t = Rake::RemoteTask.define_task(name, *args, &block)
327
+ options[:roles] = Array options[:roles]
328
+ options[:roles] |= @@current_roles
329
+ t.options = options
330
+ t
331
+ end
332
+
333
+ ##
334
+ # Ensures +name+ does not conflict with an existing method.
335
+
336
+ def self.reserved_name? name # :nodoc:
337
+ !@@env.has_key?(name.to_s) && self.respond_to?(name)
338
+ end
339
+
340
+ ##
341
+ # Resets vlad, restoring all roles, tasks and environment variables
342
+ # to the defaults.
343
+
344
+ def self.reset
345
+ @@def_role_hash = {} # official default role value
346
+ @@env = {}
347
+ @@tasks = {}
348
+ @@roles = Hash.new { |h,k| h[k] = @@def_role_hash }
349
+ @@env_locks = Hash.new { |h,k| h[k] = Mutex.new }
350
+
351
+ @@default_env.each do |k,v|
352
+ case v
353
+ when Symbol, Fixnum, nil, true, false, 42 then # ummmm... yeah. bite me.
354
+ @@env[k] = v
355
+ else
356
+ @@env[k] = v.dup
357
+ end
358
+ end
359
+ end
360
+
361
+ ##
362
+ # Adds role +role_name+ with +host+ and +args+ for that host.
363
+ # TODO: merge:
364
+ # Declare a role and assign a remote host to it. Equivalent to the
365
+ # <tt>host</tt> method; provided for capistrano compatibility.
366
+
367
+ def self.role role_name, host = nil, args = {}
368
+ if block_given? then
369
+ raise ArgumentError, 'host not allowed with block' unless host.nil?
370
+
371
+ begin
372
+ current_roles << role_name
373
+ yield
374
+ ensure
375
+ current_roles.delete role_name
376
+ end
377
+ else
378
+ raise ArgumentError, 'host required' if host.nil?
379
+
380
+ [*host].each do |hst|
381
+ raise ArgumentError, "invalid host: #{hst}" if hst.nil? or hst.empty?
382
+ end
383
+ @@roles[role_name] = {} if @@def_role_hash.eql? @@roles[role_name]
384
+ @@roles[role_name][host] = args
385
+ end
386
+ end
387
+
388
+ ##
389
+ # The configured roles.
390
+
391
+ def self.roles
392
+ host domain, :app, :web, :db if @@roles.empty?
393
+
394
+ @@roles
395
+ end
396
+
397
+ ##
398
+ # Set environment variable +name+ to +value+ or +default_block+.
399
+ #
400
+ # If +default_block+ is defined, the block will be executed the
401
+ # first time the variable is fetched, and the value will be used for
402
+ # every subsequent fetch.
403
+
404
+ def self.set name, value = nil, &default_block
405
+ raise ArgumentError, "cannot provide both a value and a block" if
406
+ value and default_block unless
407
+ value == :per_thread
408
+ raise ArgumentError, "cannot set reserved name: '#{name}'" if
409
+ Rake::RemoteTask.reserved_name?(name) unless $TESTING
410
+
411
+ name = name.to_s
412
+
413
+ Rake::RemoteTask.per_thread[name] = true if
414
+ default_block && value == :per_thread
415
+
416
+ Rake::RemoteTask.default_env[name] = Rake::RemoteTask.env[name] =
417
+ default_block || value
418
+
419
+ Object.send :define_method, name do
420
+ Rake::RemoteTask.fetch name
421
+ end
422
+ end
423
+
424
+ ##
425
+ # Sets all the default values. Should only be called once. Use reset
426
+ # if you need to restore values.
427
+
428
+ def self.set_defaults
429
+ @@default_env ||= {}
430
+ @@per_thread ||= {}
431
+ self.reset
432
+
433
+ mandatory :repository, "repository path"
434
+ mandatory :deploy_to, "deploy path"
435
+ mandatory :domain, "server domain"
436
+
437
+ simple_set(:deploy_timestamped, true,
438
+ :deploy_via, :export,
439
+ :keep_releases, 5,
440
+ :rake_cmd, "rake",
441
+ :revision, "head",
442
+ :rsync_cmd, "rsync",
443
+ :rsync_flags, ['-azP', '--delete'],
444
+ :ssh_cmd, "ssh",
445
+ :ssh_flags, [],
446
+ :sudo_cmd, "sudo",
447
+ :sudo_flags, ['-p Password:'],
448
+ :sudo_prompt, /^Password:/,
449
+ :umask, '02',
450
+ :mkdirs, [],
451
+ :shared_paths, {})
452
+
453
+ set(:current_release) { File.join(releases_path, releases[-1]) }
454
+ set(:latest_release) { deploy_timestamped?release_path:current_release}
455
+ set(:previous_release) { File.join(releases_path, releases[-2]) }
456
+ set(:release_name) { Time.now.utc.strftime("%Y%m%d%H%M%S") }
457
+ set(:release_path) { File.join(releases_path, release_name) }
458
+ set(:releases) { task.run("ls -x #{releases_path}").split.sort }
459
+
460
+ set_path :current_path, "current"
461
+ set_path :releases_path, "releases"
462
+ set_path :scm_path, "scm"
463
+ set_path :shared_path, "shared"
464
+
465
+ set(:sudo_password) do
466
+ state = `stty -g`
467
+
468
+ raise Rake::Error, "stty(1) not found" unless $?.success?
469
+
470
+ begin
471
+ system "stty -echo"
472
+ $stdout.print "sudo password: "
473
+ $stdout.flush
474
+ sudo_password = $stdin.gets
475
+ $stdout.puts
476
+ ensure
477
+ system "stty #{state}"
478
+ end
479
+ sudo_password
480
+ end
481
+ end
482
+
483
+ def self.set_path(name, subdir) # :nodoc:
484
+ set(name) { File.join(deploy_to, subdir) }
485
+ end
486
+
487
+ def self.simple_set(*args) # :nodoc:
488
+ args = Hash[*args]
489
+ args.each do |k, v|
490
+ set k, v
491
+ end
492
+ end
493
+
494
+ ##
495
+ # The Rake::RemoteTask executing in this Thread.
496
+
497
+ def self.task
498
+ Thread.current[:task]
499
+ end
500
+
501
+ ##
502
+ # The configured Rake::RemoteTasks.
503
+
504
+ def self.tasks
505
+ @@tasks
506
+ end
507
+
508
+ ##
509
+ # Execute +command+ under sudo using run.
510
+
511
+ def sudo command
512
+ run [sudo_cmd, sudo_flags, command].flatten.compact.join(" ")
513
+ end
514
+
515
+ ##
516
+ # Sets the target host. Allows you to set an optional directory
517
+ # using the format:
518
+ #
519
+ # host.domain:/dir
520
+
521
+ def target_host= host
522
+ if host =~ /^(.+):(.+?)$/
523
+ @target_host = $1
524
+ @target_dir = $2
525
+ else
526
+ @target_host = host
527
+ @target_dir = nil
528
+ end
529
+ end
530
+
531
+ ##
532
+ # The hosts this task will execute on. The hosts are determined from
533
+ # the role this task belongs to.
534
+ #
535
+ # The target hosts may be overridden by providing a comma-separated
536
+ # list of commands to the HOSTS environment variable:
537
+ #
538
+ # rake my_task HOSTS=app1.example.com,app2.example.com
539
+
540
+ def target_hosts
541
+ if hosts = ENV["HOSTS"] then
542
+ hosts.strip.gsub(/\s+/, '').split(",")
543
+ else
544
+ roles = Array options[:roles]
545
+
546
+ if roles.empty? then
547
+ Rake::RemoteTask.all_hosts
548
+ else
549
+ Rake::RemoteTask.hosts_for roles
550
+ end
551
+ end
552
+ end
553
+
554
+ ##
555
+ # Similar to target_hosts, but returns true if user defined any hosts, even
556
+ # an empty list.
557
+
558
+ def defined_target_hosts?
559
+ return true if ENV["HOSTS"]
560
+ roles = Array options[:roles]
561
+ return true if roles.empty?
562
+ # borrowed from hosts_for:
563
+ roles.flatten.each { |r|
564
+ return true unless @@def_role_hash.eql? Rake::RemoteTask.roles[r]
565
+ }
566
+ return false
567
+ end
568
+
569
+ ##
570
+ # Action is used to run a task's remote_actions in parallel on each
571
+ # of its hosts. Actions are created automatically in
572
+ # Rake::RemoteTask#enhance.
573
+
574
+ class Action
575
+
576
+ ##
577
+ # The task this action is attached to.
578
+
579
+ attr_reader :task
580
+
581
+ ##
582
+ # The block this action will execute.
583
+
584
+ attr_reader :block
585
+
586
+ ##
587
+ # An Array of threads, one for each host this action executes on.
588
+
589
+ attr_reader :workers
590
+
591
+ ##
592
+ # Creates a new Action that will run +block+ for +task+.
593
+
594
+ def initialize task, block
595
+ @task = task
596
+ @block = block
597
+ @workers = ThreadGroup.new
598
+ end
599
+
600
+ def == other # :nodoc:
601
+ return false unless Action === other
602
+ block == other.block && task == other.task
603
+ end
604
+
605
+ ##
606
+ # Execute this action on +hosts+ in parallel. Returns when block
607
+ # has completed for each host.
608
+
609
+ def execute hosts, task, args
610
+ hosts.each do |host|
611
+ t = task.clone
612
+ t.target_host = host
613
+ thread = Thread.new(t) do |task|
614
+ Thread.current[:task] = task
615
+ case block.arity
616
+ when 1
617
+ block.call task
618
+ else
619
+ block.call task, args
620
+ end
621
+ Thread.current[:task] = nil
622
+ end
623
+ @workers.add thread
624
+ end
625
+ @workers.list.each { |thr| thr.join }
626
+ end
627
+ end
628
+ end
629
+
630
+ Rake::RemoteTask.set_defaults
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: checkmate
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Peter MacRobert
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-03-08 00:00:00 +00:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: yard
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.2.9
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: rr
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.10.5
44
+ version:
45
+ description: " Define your maintenance checks, e.g. disk usage, mysql status, etc. Then run these\n checks on multiple servers using rake, and have the results emailed back to you. Set up a cron job\n to run the rake task on a regular period, and BANG! You've got simple and effective server monitoring. "
46
+ email: originalpete@gmail.com
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - LICENSE
53
+ - README.rdoc
54
+ files:
55
+ - .gitignore
56
+ - LICENSE
57
+ - README.rdoc
58
+ - Rakefile
59
+ - VERSION
60
+ - lib/checkmate.rb
61
+ - spec/checkmate_spec.rb
62
+ - spec/spec.opts
63
+ - spec/spec_helper.rb
64
+ - vendor/rake_remote_task/README.txt
65
+ - vendor/rake_remote_task/remote_task.rb
66
+ has_rdoc: true
67
+ homepage: http://github.com/originalpete/checkmate
68
+ licenses: []
69
+
70
+ post_install_message:
71
+ rdoc_options:
72
+ - --charset=UTF-8
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: "0"
80
+ version:
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: "0"
86
+ version:
87
+ requirements: []
88
+
89
+ rubyforge_project:
90
+ rubygems_version: 1.3.5
91
+ signing_key:
92
+ specification_version: 3
93
+ summary: Ruby framework to run maintenance checks on remote servers.
94
+ test_files:
95
+ - spec/checkmate_spec.rb
96
+ - spec/spec_helper.rb