scout 1.1.8 → 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby -wKU
2
+
3
+ require "pp"
4
+
5
+ module Scout
6
+ class Command
7
+ class Test < Command
8
+ def run
9
+ plugin, options = @args
10
+
11
+ # read the plugin_code from the file specified
12
+ plugin_code = File.read(plugin)
13
+ plugin_options = if options.to_s[0, 1] == "{"
14
+ eval(options) # options from command-line
15
+ elsif options
16
+ #
17
+ # read the plugin_options from the YAML file specified,
18
+ # parse each option and use the default value specified
19
+ # in the options as the value to be passed to the test plugin
20
+ #
21
+ Hash[ *File.open(options) { |f| YAML.load(f) }["options"].
22
+ map { |name, details| [name, details["default"]] }.flatten ]
23
+ else
24
+ Hash.new
25
+ end
26
+
27
+ Scout::Server.new(nil, nil, history, log) do |scout|
28
+ pp scout.process_plugin( :interval => 0,
29
+ :plugin_id => 1,
30
+ :name => "Local Plugin",
31
+ :code => plugin_code,
32
+ :options => plugin_options,
33
+ :path => plugin )
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,225 @@
1
+ #!/usr/bin/env ruby -wKU
2
+
3
+ require "optparse"
4
+ require "logger"
5
+ require "fileutils"
6
+
7
+ module Scout
8
+ class Command
9
+ def self.user
10
+ @user ||= ENV["USER"] || ENV["USERNAME"] || "root"
11
+ end
12
+
13
+ def self.program_name
14
+ @program_name ||= File.basename($PROGRAM_NAME)
15
+ end
16
+
17
+ def self.program_path
18
+ @program_path ||= File.expand_path($PROGRAM_NAME)
19
+ end
20
+
21
+ def self.usage
22
+ @usage
23
+ end
24
+
25
+ def self.parse_options(argv)
26
+ options = { }
27
+
28
+ ARGV.options do |opts|
29
+ opts.banner = "Usage:"
30
+
31
+ opts.separator " Normal checkin with server:"
32
+ opts.separator " #{program_name} [OPTIONS] CLIENT_KEY"
33
+ opts.separator " ... OR ..."
34
+ opts.separator " #{program_name} [OPTIONS] run CLIENT_KEY"
35
+ opts.separator " Install:"
36
+ opts.separator " #{program_name}"
37
+ opts.separator " ... OR ..."
38
+ opts.separator " #{program_name} [OPTIONS] install"
39
+ opts.separator " Local plugin testing:"
40
+ opts.separator " #{program_name} [OPTIONS] test " +
41
+ "PATH_TO_PLUGIN [PLUGIN_OPTIONS]"
42
+ opts.separator " Clone a client setup:"
43
+ opts.separator " #{program_name} [OPTIONS] clone " +
44
+ "CLIENT_KEY NEW_CLIENT_NAME"
45
+ opts.separator ""
46
+ opts.separator "CLIENT_KEY is the indentification key assigned to"
47
+ opts.separator "this client by the server."
48
+ opts.separator ""
49
+ opts.separator "PATH_TO_PLUGIN is the file system path to a Ruby file"
50
+ opts.separator "that contains a Scout plugin."
51
+ opts.separator ""
52
+ opts.separator "PLUGIN_OPTIONS can be the code for a Ruby Hash or the"
53
+ opts.separator "path to a YAML options file containing defaults. These"
54
+ opts.separator "options will be used for the plugin run."
55
+ opts.separator ""
56
+ opts.separator "NEW_CLIENT_NAME is name you wish to use for the new"
57
+ opts.separator "client the server creates."
58
+ opts.separator ""
59
+ opts.separator "Note: This client is meant to be installed and"
60
+ opts.separator "invoked through cron or any other scheduler."
61
+ opts.separator ""
62
+ opts.separator "Specific Options:"
63
+
64
+ opts.on( "-s", "--server SERVER", String,
65
+ "The URL for the server to report to." ) do |url|
66
+ options[:server] = url
67
+ end
68
+
69
+ opts.separator ""
70
+
71
+ opts.on( "-d", "--data DATA", String,
72
+ "The data file used to track history." ) do |file|
73
+ options[:history] = file
74
+ end
75
+ opts.on( "-l", "--level LEVEL",
76
+ Logger::SEV_LABEL.map { |l| l.downcase },
77
+ "The level of logging to report." ) do |level|
78
+ options[:level] = level
79
+ end
80
+
81
+ opts.separator "Common Options:"
82
+
83
+ opts.on( "-h", "--help",
84
+ "Show this message." ) do
85
+ puts opts
86
+ exit
87
+ end
88
+ opts.on( "-v", "--[no-]verbose",
89
+ "Turn on logging to STDOUT" ) do |bool|
90
+ options[:verbose] = bool
91
+ end
92
+
93
+ opts.on( "-V", "--version",
94
+ "Display the current version") do |version|
95
+ puts Scout::VERSION::STRING
96
+ exit
97
+ end
98
+
99
+ begin
100
+ opts.parse!
101
+ @usage = opts.to_s
102
+ rescue
103
+ puts opts
104
+ exit
105
+ end
106
+ end
107
+
108
+ options
109
+ end
110
+ private_class_method :parse_options
111
+
112
+ def self.dispatch(argv)
113
+ options = parse_options(argv)
114
+ command = if name_or_key = argv.shift
115
+ if cls = Scout::Command.const_get(name_or_key.capitalize) \
116
+ rescue nil
117
+ cls.new(options, argv)
118
+ else
119
+ Run.new(options, [name_or_key] + argv)
120
+ end
121
+ else
122
+ Install.new(options, argv)
123
+ end
124
+ command.create_pid_file_or_exit.run
125
+ end
126
+
127
+ def initialize(options, args)
128
+ @server = options[:server] || "https://scoutapp.com/"
129
+ @history = options[:history] ||
130
+ File.join( File.join( (File.expand_path("~") rescue "/"),
131
+ ".scout" ),
132
+ "client_history.yaml" )
133
+ @verbose = options[:verbose] || false
134
+ @level = options[:level] || "info"
135
+
136
+ @args = args
137
+ end
138
+
139
+ attr_reader :server, :history
140
+
141
+ def config_dir
142
+ return @config_dir if defined? @config_dir
143
+ @config_dir = File.dirname(history)
144
+ FileUtils.mkdir_p(@config_dir) # ensure dir exists
145
+ @config_dir
146
+ end
147
+
148
+ def verbose?
149
+ @verbose
150
+ end
151
+
152
+ def log
153
+ return @log if defined? @log
154
+ @log = if verbose?
155
+ log = Logger.new($stdout)
156
+ log.datetime_format = "%Y-%m-%d %H:%M:%S "
157
+ log.level = level
158
+ log
159
+ else
160
+ nil
161
+ end
162
+ end
163
+
164
+ def level
165
+ Logger.const_get(@level.upcase) rescue Logger::INFO
166
+ end
167
+
168
+ def user
169
+ @user ||= Command.user
170
+ end
171
+
172
+ def program_name
173
+ @program_name ||= Command.program_name
174
+ end
175
+
176
+ def program_path
177
+ @program_path ||= Command.program_path
178
+ end
179
+
180
+ def usage
181
+ @usage ||= Command.usage
182
+ end
183
+
184
+ def create_pid_file_or_exit
185
+ pid_file = File.join(config_dir, "scout_client_pid.txt")
186
+ begin
187
+ File.open(pid_file, File::CREAT|File::EXCL|File::WRONLY) do |pid|
188
+ pid.puts $$
189
+ end
190
+ at_exit do
191
+ begin
192
+ File.unlink(pid_file)
193
+ rescue
194
+ log.error "Unable to unlink pid file: #{$!.message}" if log
195
+ end
196
+ end
197
+ rescue
198
+ pid = File.read(pid_file).strip.to_i rescue "unknown"
199
+ running = true
200
+ begin
201
+ Process.kill(0, pid)
202
+ rescue Errno::ESRCH
203
+ running = false
204
+ rescue
205
+ # do nothing, we didn't have permission to check the running process
206
+ end
207
+ if running
208
+ log.warn "Process #{pid} was already running" if log
209
+ exit
210
+ else
211
+ log.info "Stale PID file found. Clearing it and reloading..." if log
212
+ File.unlink(pid_file) rescue nil
213
+ retry
214
+ end
215
+ end
216
+
217
+ self
218
+ end
219
+ end
220
+ end
221
+
222
+ # dynamically load all available commands
223
+ Dir.glob(File.join(File.dirname(__FILE__), *%w[command *.rb])) do |command|
224
+ require command
225
+ end
data/lib/scout/plugin.rb CHANGED
@@ -15,11 +15,107 @@ module Scout
15
15
  end
16
16
 
17
17
  # Creates a new Scout Plugin to run.
18
- #
19
18
  def initialize(last_run, memory, options)
20
19
  @last_run = last_run
21
20
  @memory = memory
22
21
  @options = options
23
22
  end
23
+
24
+ def option(name)
25
+ @options[name] ||
26
+ @options[name.is_a?(String) ? name.to_sym : String(name)]
27
+ end
28
+
29
+ # Builds the data to send to the server.
30
+ #
31
+ # We programatically define several helper methods for creating this data.
32
+ #
33
+ # Usage:
34
+ #
35
+ # reports << {:data => "here"}
36
+ # report(:data => "here")
37
+ # add_report(:data => "here")
38
+ #
39
+ # alerts << {:subject => "subject", :body => "body"}
40
+ # alert("subject", "body")
41
+ # alert(:subject => "subject", :body => "body")
42
+ # add_alert("subject", "body")
43
+ # add_alert(:subject => "subject", :body => "body")
44
+ #
45
+ # errors << {:subject => "subject", :body => "body"}
46
+ # error("subject", "body")
47
+ # error(:subject => "subject", :body => "body")
48
+ # add_error("subject", "body")
49
+ # add_error(:subject => "subject", :body => "body")
50
+ #
51
+ def data_for_server
52
+ @data_for_server ||= { :reports => [ ],
53
+ :alerts => [ ],
54
+ :errors => [ ],
55
+ :memory => { } }
56
+ end
57
+
58
+ %w[report alert error].each do |kind|
59
+ class_eval <<-END
60
+ def #{kind}s
61
+ data_for_server[:#{kind}s]
62
+ end
63
+
64
+ if "#{kind}" == "report"
65
+ def report(new_entry)
66
+ reports << new_entry
67
+ end
68
+ else
69
+ def #{kind}(*fields)
70
+ #{kind}s << ( fields.first.is_a?(Hash) ?
71
+ fields.first :
72
+ {:subject => fields.first, :body => fields.last} )
73
+ end
74
+ end
75
+ alias_method :add_#{kind}, :#{kind}
76
+ END
77
+ end
78
+
79
+ #
80
+ # Usage:
81
+ #
82
+ # memory(:no_track)
83
+ # memory.delete(:no_track)
84
+ # memory.clear
85
+ #
86
+ def memory(name = nil)
87
+ if name.nil?
88
+ data_for_server[:memory]
89
+ else
90
+ @memory[name] ||
91
+ @memory[name.is_a?(String) ? name.to_sym : String(name)]
92
+ end
93
+ end
94
+
95
+ #
96
+ # Usage:
97
+ #
98
+ # remember(:name, value)
99
+ # remember(:name1, value1, :name2, value2)
100
+ # remember(:name => value)
101
+ # remember(:name1 => value1, :name2 => value2)
102
+ # remember(:name1, value1, :name2 => value2)
103
+ #
104
+ def remember(*args)
105
+ hashes, other = args.partition { |value| value.is_a? Hash }
106
+ hashes.each { |hash| memory.merge!(hash) }
107
+ (0...other.size).step(2) { |i| memory.merge!(other[i] => other[i + 1]) }
108
+ end
109
+
110
+ #
111
+ # Old plugins will work because they override this method. New plugins can
112
+ # now leave this method in place, add a build_report() method instead, and
113
+ # use the new helper methods to build up content inside which will
114
+ # automatically be returned as the end result of the run.
115
+ #
116
+ def run
117
+ build_report
118
+ data_for_server
119
+ end
24
120
  end
25
121
  end
data/lib/scout/server.rb CHANGED
@@ -14,7 +14,8 @@ module Scout
14
14
  URLS = { :plan => "/clients/CLIENT_KEY/plugins.scout?version=CLIENT_VERSION",
15
15
  :report => "/clients/CLIENT_KEY/plugins/PLUGIN_ID/reports.scout?version=CLIENT_VERSION",
16
16
  :error => "/clients/CLIENT_KEY/plugins/PLUGIN_ID/errors.scout?version=CLIENT_VERSION",
17
- :alert => "/clients/CLIENT_KEY/plugins/PLUGIN_ID/alerts.scout?version=CLIENT_VERSION" }
17
+ :alert => "/clients/CLIENT_KEY/plugins/PLUGIN_ID/alerts.scout?version=CLIENT_VERSION",
18
+ :clone => "/clients/CLIENT_KEY/clone_from?version=CLIENT_VERSION" }
18
19
 
19
20
  #
20
21
  # A plugin cannot take more than PLUGIN_TIMEOUT seconds to execute,
@@ -96,6 +97,7 @@ module Scout
96
97
  eval(plugin[:code], TOPLEVEL_BINDING, plugin[:path] || plugin[:name])
97
98
  info "Plugin compiled."
98
99
  rescue Exception
100
+ raise if $!.is_a? SystemExit
99
101
  error "Plugin would not compile: #{$!.message}"
100
102
  return
101
103
  end
@@ -113,6 +115,7 @@ module Scout
113
115
  error "Plugin took too long to run."
114
116
  return
115
117
  rescue Exception
118
+ raise if $!.is_a? SystemExit
116
119
  error "Plugin failed to run: #{$!.backtrace}"
117
120
  end
118
121
  info "Plugin completed its run."
@@ -151,6 +154,7 @@ module Scout
151
154
  Plugin.last_defined = nil
152
155
  info "Plugin Removed."
153
156
  rescue
157
+ raise if $!.is_a? SystemExit
154
158
  error "Unable to remove plugin."
155
159
  end
156
160
  end
@@ -223,11 +227,28 @@ module Scout
223
227
  info "Error sent."
224
228
  end
225
229
 
230
+ def clone_client(new_name, success_output)
231
+ url = urlify(:clone)
232
+ debug "Sending clone request to #{url}..."
233
+ post( url,
234
+ "Unable to send clone request to server.",
235
+ "client[name]" => new_name ) do |response|
236
+ case server_reply = response.body
237
+ when /\AError:/i
238
+ fatal "Clone error."
239
+ abort server_reply
240
+ else
241
+ info "Client cloned."
242
+ puts success_output.gsub(/\bCLIENT_KEY\b/, server_reply.strip)
243
+ end
244
+ end
245
+ end
246
+
226
247
  private
227
248
 
228
249
  def urlify(url_name, options = Hash.new)
229
250
  return unless @server
230
- options.merge!(:client_version => Scout::VERSION)
251
+ options.merge!(:client_version => Scout::VERSION::STRING)
231
252
  URI.join( @server,
232
253
  URLS[url_name].
233
254
  gsub(/\bCLIENT_KEY\b/, @client_key).
@@ -278,6 +299,7 @@ module Scout
278
299
  fatal "Request timed out."
279
300
  exit
280
301
  rescue Exception
302
+ raise if $!.is_a? SystemExit
281
303
  fatal "An HTTP error occurred: #{$!.message}"
282
304
  exit
283
305
  end
@@ -0,0 +1,9 @@
1
+ module Scout
2
+ module VERSION #:nodoc:
3
+ MAJOR = 2
4
+ MINOR = 0
5
+ TINY = 4
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
data/lib/scout.rb CHANGED
@@ -1,8 +1,6 @@
1
1
  #!/usr/bin/env ruby -wKU
2
2
 
3
+ require "scout/version"
4
+ require "scout/command"
3
5
  require "scout/plugin"
4
6
  require "scout/server"
5
-
6
- module Scout
7
- VERSION = "1.1.8".freeze
8
- end
data/script/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/scout.rb'}"
9
+ puts "Loading scout gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
data/script/txt2html ADDED
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ GEM_NAME = 'scout' # what ppl will type to install your gem
4
+ RUBYFORGE_PROJECT = 'scout'
5
+
6
+ require 'rubygems'
7
+ begin
8
+ require 'newgem'
9
+ require 'rubyforge'
10
+ rescue LoadError
11
+ puts "\n\nGenerating the website requires the newgem RubyGem"
12
+ puts "Install: gem install newgem\n\n"
13
+ exit(1)
14
+ end
15
+ require 'redcloth'
16
+ require 'syntax/convertors/html'
17
+ require 'erb'
18
+ require File.dirname(__FILE__) + "/../lib/#{GEM_NAME}/version.rb"
19
+
20
+ version = Scout::VERSION::STRING
21
+ download = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
22
+
23
+ def rubyforge_project_id
24
+ RubyForge.new.autoconfig["group_ids"][RUBYFORGE_PROJECT]
25
+ end
26
+
27
+ class Fixnum
28
+ def ordinal
29
+ # teens
30
+ return 'th' if (10..19).include?(self % 100)
31
+ # others
32
+ case self % 10
33
+ when 1: return 'st'
34
+ when 2: return 'nd'
35
+ when 3: return 'rd'
36
+ else return 'th'
37
+ end
38
+ end
39
+ end
40
+
41
+ class Time
42
+ def pretty
43
+ return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
44
+ end
45
+ end
46
+
47
+ def convert_syntax(syntax, source)
48
+ return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^<pre>|</pre>$!,'')
49
+ end
50
+
51
+ if ARGV.length >= 1
52
+ src, template = ARGV
53
+ template ||= File.join(File.dirname(__FILE__), '/../website/template.html.erb')
54
+ else
55
+ puts("Usage: #{File.split($0).last} source.txt [template.html.erb] > output.html")
56
+ exit!
57
+ end
58
+
59
+ template = ERB.new(File.open(template).read)
60
+
61
+ title = nil
62
+ body = nil
63
+ File.open(src) do |fsrc|
64
+ title_text = fsrc.readline
65
+ body_text_template = fsrc.read
66
+ body_text = ERB.new(body_text_template).result(binding)
67
+ syntax_items = []
68
+ body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)</\1>!m){
69
+ ident = syntax_items.length
70
+ element, syntax, source = $1, $2, $3
71
+ syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}</#{element}>"
72
+ "syntax-temp-#{ident}"
73
+ }
74
+ title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
75
+ body = RedCloth.new(body_text).to_html
76
+ body.gsub!(%r!(?:<pre><code>)?syntax-temp-(\d+)(?:</code></pre>)?!){ syntax_items[$1.to_i] }
77
+ end
78
+ stat = File.stat(src)
79
+ created = stat.ctime
80
+ modified = stat.mtime
81
+
82
+ $stdout << template.result(binding)