scout 1.1.8 → 2.0.4

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,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)