checkmate 0.1.0

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