scout 4.0.2 → 5.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README +18 -28
- data/Rakefile +41 -41
- data/bin/scout +2 -1
- data/lib/scout.rb +2 -1
- data/lib/scout/command.rb +52 -50
- data/lib/scout/command/install.rb +9 -6
- data/lib/scout/command/run.rb +19 -2
- data/lib/scout/command/test.rb +36 -14
- data/lib/scout/plugin.rb +17 -0
- data/lib/scout/plugin_options.rb +80 -0
- data/lib/scout/server.rb +165 -88
- metadata +18 -17
data/README
CHANGED
@@ -6,32 +6,32 @@ Scout by Highgroove Studios
|
|
6
6
|
|
7
7
|
The easier way to monitor servers and web applications.
|
8
8
|
|
9
|
-
Scout makes monitoring and reporting on your web applications
|
10
|
-
and simple as possible.
|
9
|
+
Scout makes monitoring and reporting on your web applications
|
10
|
+
as flexible and simple as possible.
|
11
11
|
|
12
|
-
Scout is a product of Highgroove Studios.
|
13
|
-
for more information.
|
12
|
+
Scout is a product of Highgroove Studios.
|
13
|
+
Please visit http://scoutapp.com for more information.
|
14
14
|
|
15
15
|
== Installing
|
16
16
|
|
17
|
-
|
17
|
+
Install the Scout gem:
|
18
18
|
|
19
19
|
$ sudo gem install scout
|
20
20
|
|
21
|
-
|
21
|
+
Then simply run:
|
22
22
|
|
23
23
|
$ scout
|
24
24
|
|
25
|
-
to run the installation wizard. You'll need your
|
25
|
+
to run the installation wizard. You'll need your server key to continue. Get the server key from your account at http://scoutapp.com
|
26
26
|
|
27
27
|
== Running the Scout Client
|
28
28
|
|
29
29
|
The Scout client has several modes of operation and commands. The normal, intended usage is through a scheduled interval with no output.
|
30
30
|
|
31
31
|
Normal checkin with server:
|
32
|
-
$ scout [OPTIONS]
|
32
|
+
$ scout [OPTIONS] SERVER_KEY
|
33
33
|
... OR ...
|
34
|
-
$ scout [OPTIONS] run
|
34
|
+
$ scout [OPTIONS] run SERVER_KEY
|
35
35
|
|
36
36
|
Install:
|
37
37
|
$ scout
|
@@ -41,36 +41,26 @@ Install:
|
|
41
41
|
Local plugin testing:
|
42
42
|
$ scout [OPTIONS] test PATH_TO_PLUGIN [PLUGIN_OPTIONS]
|
43
43
|
|
44
|
-
Clone a client setup:
|
45
|
-
$ scout [OPTIONS] clone CLIENT_KEY NEW_CLIENT_NAME
|
46
44
|
|
47
|
-
|
48
|
-
this client by the server.
|
45
|
+
SERVER_KEY is the identification key assigned by your account at http://scoutapp.com
|
49
46
|
|
50
47
|
PATH_TO_PLUGIN is the file system path to a Ruby file
|
51
48
|
that contains a Scout plugin.
|
52
49
|
|
53
|
-
PLUGIN_OPTIONS
|
54
|
-
|
55
|
-
options will be used for the plugin run.
|
50
|
+
PLUGIN_OPTIONS are one or more options in the form:
|
51
|
+
key1=val1 key2=val2
|
52
|
+
These options will be used for the plugin run.
|
56
53
|
|
57
|
-
NEW_CLIENT_NAME is name you wish to use for the new
|
58
|
-
client the server creates.
|
59
54
|
|
55
|
+
== Setting up in cron
|
60
56
|
|
61
|
-
|
57
|
+
Configure Scout to run every minute. Typically, this will look like:
|
62
58
|
|
63
|
-
|
64
|
-
|
65
|
-
An example usage, running:
|
66
|
-
|
67
|
-
server1 $ scout clone CLIENT_KEY 'My New Client'
|
68
|
-
|
69
|
-
Will create a new client called 'My New Client' on the Scout server with the same plugins as an already existing client (with the CLIENT_KEY specified). It will also return the new system crontab line:
|
70
|
-
|
71
|
-
*/30 * * * * deploy /usr/bin/scout NEW_CLIENT_KEY
|
59
|
+
* * * * * deploy /usr/bin/scout SERVER_KEY
|
72
60
|
|
61
|
+
It's often helpful to log the output to a file. To do so:
|
73
62
|
|
63
|
+
* * * * * deploy /usr/bin/scout SERVER_KEY > /path/to/anywhere/scout.out 2>&1
|
74
64
|
|
75
65
|
|
76
66
|
For additional help, please visit http://scoutapp.com
|
data/Rakefile
CHANGED
@@ -7,8 +7,8 @@ require "net/ssh"
|
|
7
7
|
require "rubygems"
|
8
8
|
require "rubyforge"
|
9
9
|
|
10
|
-
dir
|
11
|
-
lib
|
10
|
+
dir = File.dirname(__FILE__)
|
11
|
+
lib = File.join(dir, "lib", "scout.rb")
|
12
12
|
version = File.read(lib)[/^\s*VERSION\s*=\s*(['"])(\d\.\d\.\d)\1/, 2]
|
13
13
|
history = File.read("CHANGELOG").split(/^(===.*)/)
|
14
14
|
changes ||= history[0..2].join.strip
|
@@ -19,50 +19,50 @@ need_zip = true
|
|
19
19
|
task :default => [:test]
|
20
20
|
|
21
21
|
Rake::TestTask.new do |test|
|
22
|
-
test.libs
|
22
|
+
test.libs << "test"
|
23
23
|
test.test_files = [ "test/scout_test.rb" ]
|
24
|
-
test.verbose
|
24
|
+
test.verbose = true
|
25
25
|
end
|
26
26
|
|
27
27
|
Rake::RDocTask.new do |rdoc|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
28
|
+
rdoc.main = "README"
|
29
|
+
rdoc.rdoc_dir = "doc/html"
|
30
|
+
rdoc.title = "Scout Client Documentation"
|
31
|
+
rdoc.rdoc_files.include( "README", "INSTALL",
|
32
|
+
"TODO", "CHANGELOG",
|
33
|
+
"AUTHORS", "COPYING",
|
34
|
+
"LICENSE", "lib/" )
|
35
35
|
end
|
36
36
|
|
37
37
|
spec = Gem::Specification.new do |spec|
|
38
|
-
|
39
|
-
|
38
|
+
spec.name = "scout"
|
39
|
+
spec.version = version
|
40
40
|
|
41
|
-
|
42
|
-
|
41
|
+
spec.platform = Gem::Platform::RUBY
|
42
|
+
spec.summary = "Scout makes monitoring and reporting on your web applications as flexible and simple as possible."
|
43
43
|
|
44
44
|
# TODO: test suite
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
spec.executables
|
50
|
-
|
51
|
-
|
52
|
-
|
45
|
+
# spec.test_suite_file = "test/ts_all.rb"
|
46
|
+
spec.files = Dir.glob("{bin,lib}/**/*.rb") +
|
47
|
+
Dir.glob("{data,vendor}/**/*") +
|
48
|
+
%w[Rakefile]
|
49
|
+
spec.executables = ["scout"]
|
50
|
+
|
51
|
+
spec.has_rdoc = true
|
52
|
+
spec.extra_rdoc_files = %w[ AUTHORS COPYING README INSTALL TODO CHANGELOG
|
53
53
|
LICENSE ]
|
54
|
-
|
55
|
-
|
54
|
+
spec.rdoc_options << "--title" << "Scout Client Documentation" <<
|
55
|
+
"--main" << "README"
|
56
|
+
|
57
|
+
spec.require_path = "lib"
|
56
58
|
|
57
|
-
spec.require_path = "lib"
|
58
|
-
|
59
59
|
spec.add_dependency "elif"
|
60
60
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
61
|
+
spec.author = "Highgroove Studios"
|
62
|
+
spec.email = "scout@highgroove.com"
|
63
|
+
spec.rubyforge_project = "scout"
|
64
|
+
spec.homepage = "http://scoutapp.com"
|
65
|
+
spec.description = <<END_DESC
|
66
66
|
Scout makes monitoring and reporting on your web applications as flexible and simple as possible.
|
67
67
|
|
68
68
|
Scout is a product of Highgroove Studios.
|
@@ -70,8 +70,8 @@ END_DESC
|
|
70
70
|
end
|
71
71
|
|
72
72
|
Rake::GemPackageTask.new(spec) do |pkg|
|
73
|
-
|
74
|
-
|
73
|
+
pkg.need_zip = need_tar
|
74
|
+
pkg.need_tar = need_zip
|
75
75
|
end
|
76
76
|
|
77
77
|
desc "Publishes to Scout Gem Server and Rubyforge"
|
@@ -85,12 +85,12 @@ task :publish_rubyforge => [:package] do
|
|
85
85
|
puts "Logging in"
|
86
86
|
forge.login
|
87
87
|
|
88
|
-
release
|
88
|
+
release = forge.userconfig
|
89
89
|
release["release_changes"] = File.read(File.join(dir, "CHANGELOG"))
|
90
|
-
release["preformatted"]
|
90
|
+
release["preformatted"] = true
|
91
91
|
|
92
92
|
package = "pkg/#{spec.name}-#{version}"
|
93
|
-
files
|
93
|
+
files = %W[#{package}.tgz #{package}.zip #{package}.gem].compact
|
94
94
|
|
95
95
|
puts "Releasing #{spec.name}-#{version}"
|
96
96
|
forge.add_release(spec.rubyforge_project, spec.name, version, *files)
|
@@ -98,9 +98,9 @@ end
|
|
98
98
|
|
99
99
|
desc "Upload current documentation to Scout Gem Server and RubyForge"
|
100
100
|
task :upload_docs => [:rdoc] do
|
101
|
-
|
102
|
-
|
103
|
-
|
101
|
+
sh "scp -r doc/html/* " +
|
102
|
+
"deploy@gems.scoutapp.com:/var/www/gems/docs"
|
103
|
+
|
104
104
|
config = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml")))
|
105
105
|
host = "#{config["username"]}@rubyforge.org"
|
106
106
|
|
@@ -112,5 +112,5 @@ end
|
|
112
112
|
|
113
113
|
desc "Add new files to Subersion"
|
114
114
|
task :svn_add do
|
115
|
-
|
115
|
+
system "svn status | grep '^\?' | sed -e 's/? *//' | sed -e 's/ /\ /g' | xargs svn add"
|
116
116
|
end
|
data/bin/scout
CHANGED
data/lib/scout.rb
CHANGED
data/lib/scout/command.rb
CHANGED
@@ -9,7 +9,7 @@ module Scout
|
|
9
9
|
def self.user
|
10
10
|
@user ||= ENV["USER"] || ENV["USERNAME"] || "root"
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
def self.program_name
|
14
14
|
@program_name ||= File.basename($PROGRAM_NAME)
|
15
15
|
end
|
@@ -17,7 +17,7 @@ module Scout
|
|
17
17
|
def self.program_path
|
18
18
|
@program_path ||= File.expand_path($PROGRAM_NAME)
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
def self.usage
|
22
22
|
@usage
|
23
23
|
end
|
@@ -25,9 +25,10 @@ module Scout
|
|
25
25
|
def self.parse_options(argv)
|
26
26
|
options = { }
|
27
27
|
|
28
|
-
|
28
|
+
op = OptionParser.new do |opts|
|
29
29
|
opts.banner = "Usage:"
|
30
30
|
|
31
|
+
opts.separator "--------------------------------------------------------------------------"
|
31
32
|
opts.separator " Normal checkin with server:"
|
32
33
|
opts.separator " #{program_name} [OPTIONS] CLIENT_KEY"
|
33
34
|
opts.separator " ... OR ..."
|
@@ -39,47 +40,32 @@ module Scout
|
|
39
40
|
opts.separator " Local plugin testing:"
|
40
41
|
opts.separator " #{program_name} [OPTIONS] test " +
|
41
42
|
"PATH_TO_PLUGIN [PLUGIN_OPTIONS]"
|
42
|
-
opts.separator "
|
43
|
-
opts.separator "
|
44
|
-
|
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 ""
|
43
|
+
opts.separator "[PLUGIN_OPTIONS] format: opt1=val1 opt2=val2 opt2=val3 ..."
|
44
|
+
opts.separator "Plugin will use internal defaults if options aren't provided."
|
45
|
+
opts.separator " "
|
59
46
|
opts.separator "Note: This client is meant to be installed and"
|
60
47
|
opts.separator "invoked through cron or any other scheduler."
|
61
|
-
opts.separator ""
|
48
|
+
opts.separator " "
|
62
49
|
opts.separator "Specific Options:"
|
63
|
-
|
50
|
+
opts.separator "--------------------------------------------------------------------------"
|
64
51
|
opts.on( "-s", "--server SERVER", String,
|
65
52
|
"The URL for the server to report to." ) do |url|
|
66
53
|
options[:server] = url
|
67
54
|
end
|
68
55
|
|
69
|
-
opts.separator ""
|
70
|
-
|
71
56
|
opts.on( "-d", "--data DATA", String,
|
72
57
|
"The data file used to track history." ) do |file|
|
73
58
|
options[:history] = file
|
74
59
|
end
|
75
60
|
opts.on( "-l", "--level LEVEL",
|
76
61
|
Logger::SEV_LABEL.map { |l| l.downcase },
|
77
|
-
"The level of logging to report." ) do |level|
|
62
|
+
"The level of logging to report. Use -ldebug for most detail." ) do |level|
|
78
63
|
options[:level] = level
|
79
64
|
end
|
80
65
|
|
66
|
+
opts.separator " "
|
81
67
|
opts.separator "Common Options:"
|
82
|
-
|
68
|
+
opts.separator "--------------------------------------------------------------------------"
|
83
69
|
opts.on( "-h", "--help",
|
84
70
|
"Show this message." ) do
|
85
71
|
puts opts
|
@@ -89,31 +75,46 @@ module Scout
|
|
89
75
|
"Turn on logging to STDOUT" ) do |bool|
|
90
76
|
options[:verbose] = bool
|
91
77
|
end
|
92
|
-
|
78
|
+
|
93
79
|
opts.on( "-V", "--version",
|
94
80
|
"Display the current version") do |version|
|
95
81
|
puts Scout::VERSION
|
96
82
|
exit
|
97
83
|
end
|
98
84
|
|
99
|
-
|
100
|
-
|
101
|
-
@usage = opts.to_s
|
102
|
-
rescue
|
103
|
-
puts opts
|
104
|
-
exit
|
85
|
+
opts.on( "-F", "--force", "Force checkin to Scout server regardless of last checkin time") do |bool|
|
86
|
+
options[:force] = bool
|
105
87
|
end
|
88
|
+
|
89
|
+
opts.separator " "
|
90
|
+
opts.separator "Examples: "
|
91
|
+
opts.separator "--------------------------------------------------------------------------"
|
92
|
+
opts.separator "1. Normal run (replace w/your own key):"
|
93
|
+
opts.separator " scout 6ecad322-0d17-4cb8-9b2c-a12c4541853f"
|
94
|
+
opts.separator "2. Normal run with logging to standard out (replace w/your own key):"
|
95
|
+
opts.separator " scout --verbose 6ecad322-0d17-4cb8-9b2c-a12c4541853f"
|
96
|
+
opts.separator "3. Test a plugin:"
|
97
|
+
opts.separator " scout test my_plugin.rb foo=18 bar=42"
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
begin
|
102
|
+
op.parse!(argv)
|
103
|
+
@usage = op.to_s
|
104
|
+
rescue
|
105
|
+
puts op
|
106
|
+
exit
|
106
107
|
end
|
107
|
-
|
108
108
|
options
|
109
109
|
end
|
110
110
|
private_class_method :parse_options
|
111
|
-
|
111
|
+
|
112
112
|
def self.dispatch(argv)
|
113
|
+
# capture help command
|
114
|
+
argv.push("--help") if argv.first == 'help'
|
113
115
|
options = parse_options(argv)
|
114
116
|
command = if name_or_key = argv.shift
|
115
|
-
if cls = Scout::Command.const_get(name_or_key.capitalize)
|
116
|
-
rescue nil
|
117
|
+
if cls = (Scout::Command.const_get(name_or_key.capitalize) rescue nil)
|
117
118
|
cls.new(options, argv)
|
118
119
|
else
|
119
120
|
Run.new(options, [name_or_key] + argv)
|
@@ -121,9 +122,9 @@ module Scout
|
|
121
122
|
else
|
122
123
|
Install.new(options, argv)
|
123
124
|
end
|
124
|
-
command.
|
125
|
+
command.run
|
125
126
|
end
|
126
|
-
|
127
|
+
|
127
128
|
def initialize(options, args)
|
128
129
|
@server = options[:server] || "https://scoutapp.com/"
|
129
130
|
@history = options[:history] ||
|
@@ -132,23 +133,24 @@ module Scout
|
|
132
133
|
"client_history.yaml" )
|
133
134
|
@verbose = options[:verbose] || false
|
134
135
|
@level = options[:level] || "info"
|
135
|
-
|
136
|
+
@force = options[:force] || false
|
137
|
+
|
136
138
|
@args = args
|
137
139
|
end
|
138
|
-
|
140
|
+
|
139
141
|
attr_reader :server, :history
|
140
|
-
|
142
|
+
|
141
143
|
def config_dir
|
142
144
|
return @config_dir if defined? @config_dir
|
143
145
|
@config_dir = File.dirname(history)
|
144
146
|
FileUtils.mkdir_p(@config_dir) # ensure dir exists
|
145
147
|
@config_dir
|
146
148
|
end
|
147
|
-
|
149
|
+
|
148
150
|
def verbose?
|
149
151
|
@verbose
|
150
152
|
end
|
151
|
-
|
153
|
+
|
152
154
|
def log
|
153
155
|
return @log if defined? @log
|
154
156
|
@log = if verbose?
|
@@ -160,15 +162,15 @@ module Scout
|
|
160
162
|
nil
|
161
163
|
end
|
162
164
|
end
|
163
|
-
|
165
|
+
|
164
166
|
def level
|
165
167
|
Logger.const_get(@level.upcase) rescue Logger::INFO
|
166
168
|
end
|
167
|
-
|
169
|
+
|
168
170
|
def user
|
169
171
|
@user ||= Command.user
|
170
172
|
end
|
171
|
-
|
173
|
+
|
172
174
|
def program_name
|
173
175
|
@program_name ||= Command.program_name
|
174
176
|
end
|
@@ -176,11 +178,11 @@ module Scout
|
|
176
178
|
def program_path
|
177
179
|
@program_path ||= Command.program_path
|
178
180
|
end
|
179
|
-
|
181
|
+
|
180
182
|
def usage
|
181
183
|
@usage ||= Command.usage
|
182
184
|
end
|
183
|
-
|
185
|
+
|
184
186
|
def create_pid_file_or_exit
|
185
187
|
pid_file = File.join(config_dir, "scout_client_pid.txt")
|
186
188
|
begin
|
@@ -228,7 +230,7 @@ module Scout
|
|
228
230
|
retry
|
229
231
|
end
|
230
232
|
end
|
231
|
-
|
233
|
+
|
232
234
|
self
|
233
235
|
end
|
234
236
|
end
|
@@ -4,23 +4,25 @@ module Scout
|
|
4
4
|
class Command
|
5
5
|
class Install < Command
|
6
6
|
def run
|
7
|
+
create_pid_file_or_exit
|
8
|
+
|
7
9
|
abort usage unless $stdin.tty?
|
8
10
|
|
9
11
|
puts <<-END_INTRO.gsub(/^ {8}/, "")
|
10
12
|
=== Scout Installation Wizard ===
|
11
13
|
|
12
|
-
You need the
|
14
|
+
You need the Server Key displayed in the Server Settings tab.
|
13
15
|
It looks like:
|
14
16
|
|
15
17
|
6ecad322-0d17-4cb8-9b2c-a12c4541853f
|
16
18
|
|
17
|
-
Enter the
|
19
|
+
Enter the Server Key:
|
18
20
|
END_INTRO
|
19
21
|
key = gets.to_s.strip
|
20
22
|
|
21
23
|
puts "\nAttempting to contact the server..."
|
22
24
|
begin
|
23
|
-
Scout::Server.new(server, key, history, log) { |scout| scout.
|
25
|
+
Scout::Server.new(server, key, history, log) { |scout| scout.fetch_plan }
|
24
26
|
|
25
27
|
puts <<-END_SUCCESS.gsub(/^ {10}/, "")
|
26
28
|
Success!
|
@@ -31,14 +33,14 @@ module Scout
|
|
31
33
|
(usually located at /etc/crontab):
|
32
34
|
|
33
35
|
****** START CRONTAB SAMPLE ******
|
34
|
-
|
36
|
+
* * * * * #{user} #{program_path} #{key}
|
35
37
|
****** END CRONTAB SAMPLE ******
|
36
38
|
|
37
39
|
If you are using this current user's crontab
|
38
40
|
(using crontab -e to edit):
|
39
41
|
|
40
42
|
****** START CRONTAB SAMPLE ******
|
41
|
-
|
43
|
+
* * * * * #{program_path} #{key}
|
42
44
|
****** END CRONTAB SAMPLE ******
|
43
45
|
|
44
46
|
For help setting up Scout with crontab, please visit:
|
@@ -47,9 +49,10 @@ module Scout
|
|
47
49
|
|
48
50
|
END_SUCCESS
|
49
51
|
rescue SystemExit
|
52
|
+
puts $!.message
|
50
53
|
puts <<-END_ERROR.gsub(/^ {10}/, "")
|
51
54
|
|
52
|
-
|
55
|
+
Failed.
|
53
56
|
For more help, please visit:
|
54
57
|
|
55
58
|
http://scoutapp.com/help
|
data/lib/scout/command/run.rb
CHANGED
@@ -5,8 +5,25 @@ module Scout
|
|
5
5
|
class Run < Command
|
6
6
|
def run
|
7
7
|
key = @args.first
|
8
|
-
|
9
|
-
|
8
|
+
# too much external logic of command doing things to server ... should be moved into server class
|
9
|
+
@scout = Scout::Server.new(server, key, history, log)
|
10
|
+
@scout.load_history
|
11
|
+
@scout.fetch_plan
|
12
|
+
|
13
|
+
|
14
|
+
if @scout.new_plan || @scout.time_to_checkin? || @force
|
15
|
+
if @scout.new_plan
|
16
|
+
log.info("Now checking in with new plugin plan") if log
|
17
|
+
elsif @scout.time_to_checkin?
|
18
|
+
log.info("It is time to checkin") if log
|
19
|
+
elsif @force
|
20
|
+
log.info("overriding checkin schedule with --force and checking in now.") if log
|
21
|
+
end
|
22
|
+
create_pid_file_or_exit
|
23
|
+
@scout.run_plugins_by_plan
|
24
|
+
@scout.save_history
|
25
|
+
else
|
26
|
+
log.info "Not time to checkin yet. Next checkin in #{@scout.next_checkin}. Override by passing --force to the scout command" if log
|
10
27
|
end
|
11
28
|
end
|
12
29
|
end
|
data/lib/scout/command/test.rb
CHANGED
@@ -6,22 +6,43 @@ module Scout
|
|
6
6
|
class Command
|
7
7
|
class Test < Command
|
8
8
|
def run
|
9
|
-
|
10
|
-
|
9
|
+
create_pid_file_or_exit
|
10
|
+
plugin, *provided_options = @args
|
11
11
|
# read the plugin_code from the file specified
|
12
12
|
plugin_code = File.read(plugin)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
13
|
+
|
14
|
+
options_for_run = {}
|
15
|
+
|
16
|
+
# deal with embedded options yaml
|
17
|
+
if options_yaml = Scout::Plugin.extract_options_yaml_from_code(plugin_code)
|
18
|
+
options=Scout::PluginOptions.from_yaml(options_yaml)
|
19
|
+
|
20
|
+
if options.error
|
21
|
+
puts "Problem parsing option definition in the plugin code (ignoring and continuing):"
|
22
|
+
puts options_yaml
|
23
|
+
else
|
24
|
+
puts "== Plugin options: "
|
25
|
+
puts options.to_s
|
26
|
+
options.select{|o|o.has_default?}.each{|o|options_for_run[o.name]=o.default}
|
27
|
+
end
|
28
|
+
else
|
29
|
+
puts "== This plugin doesn't have option metadata."
|
30
|
+
end
|
31
|
+
|
32
|
+
# provided_options are what the user gave us in the command line. Here, we merge them into
|
33
|
+
# the defaults we've already established (if any) for this run.
|
34
|
+
provided_options.each do |e|
|
35
|
+
if e.include?('=')
|
36
|
+
k,v=e.split('=',2)
|
37
|
+
options_for_run[k]=v
|
38
|
+
else
|
39
|
+
puts "ERROR: Option '#{e}' is no good -- provided options should be in the format name=value."
|
40
|
+
end
|
41
|
+
end
|
42
|
+
if options_for_run.any?
|
43
|
+
puts "== Running plugin with: #{options_for_run.to_a.map{|a| "#{a.first}=#{a.last}"}.join('; ') }"
|
23
44
|
else
|
24
|
-
|
45
|
+
puts "== You haven't provided any options for running this plugin."
|
25
46
|
end
|
26
47
|
|
27
48
|
Scout::Server.new(nil, nil, history, log) do |scout|
|
@@ -30,8 +51,9 @@ module Scout
|
|
30
51
|
'plugin_id' => 1,
|
31
52
|
'name' => "Local Plugin",
|
32
53
|
'code' => plugin_code,
|
33
|
-
'options' =>
|
54
|
+
'options' => options_for_run,
|
34
55
|
'path' => plugin )
|
56
|
+
puts "== Output:"
|
35
57
|
scout.show_checkin(:pp)
|
36
58
|
end
|
37
59
|
end
|
data/lib/scout/plugin.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
#!/usr/bin/env ruby -wKU
|
2
2
|
|
3
|
+
|
3
4
|
module Scout
|
5
|
+
|
4
6
|
class Plugin
|
7
|
+
|
8
|
+
EMBEDDED_OPTIONS_REGEX = /OPTIONS ?= ?<<-?([A-Z_]+)(.*)\1/m
|
9
|
+
|
5
10
|
class << self
|
6
11
|
attr_accessor :last_defined
|
7
12
|
|
@@ -32,6 +37,18 @@ module Scout
|
|
32
37
|
needs.push(*libraries.flatten)
|
33
38
|
end
|
34
39
|
end
|
40
|
+
|
41
|
+
# true if the code seems to have embedded options
|
42
|
+
def has_embedded_options?(code)
|
43
|
+
code =~ EMBEDDED_OPTIONS_REGEX
|
44
|
+
end
|
45
|
+
|
46
|
+
# extracts the internal YAML, if any, and returns the YAML string.
|
47
|
+
# returns nil if no embedded options.
|
48
|
+
def extract_options_yaml_from_code(code)
|
49
|
+
code =~ EMBEDDED_OPTIONS_REGEX
|
50
|
+
return $2
|
51
|
+
end
|
35
52
|
end
|
36
53
|
|
37
54
|
# Creates a new Scout Plugin to run.
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#!/usr/bin/env ruby -wKU
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Scout
|
6
|
+
# a data structure of an individual plugin option
|
7
|
+
class PluginOption
|
8
|
+
attr_reader :name, :notes, :default, :advanced, :password, :required
|
9
|
+
def initialize(name, h)
|
10
|
+
@name=name
|
11
|
+
@notes=h['notes'] || ''
|
12
|
+
@default=h['default'] || ''
|
13
|
+
@attributes=h['attributes'] || ''
|
14
|
+
@advanced = @attributes.include?('advanced')
|
15
|
+
@password = @attributes.include?('password')
|
16
|
+
@required = @attributes.include?('required')
|
17
|
+
end
|
18
|
+
|
19
|
+
# convenience -- for nicer syntax
|
20
|
+
def advanced?; @advanced; end
|
21
|
+
def password?; @password; end
|
22
|
+
def required?; @required; end
|
23
|
+
def has_default?; default != '';end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
required_string = required? ? " (required). " : ""
|
27
|
+
default_string = default == '' ? '' : " Default: #{default}. "
|
28
|
+
"'#{name}'#{required_string}#{default_string}#{notes}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# A collection of pluginOption
|
33
|
+
# Create: opts=PluginOptions.from_yaml(yaml_string)
|
34
|
+
# Check if there were any problems -- opts.error -- should be nil.
|
35
|
+
#
|
36
|
+
# A valid options yaml looks like this:
|
37
|
+
# max_swap_used:
|
38
|
+
# notes: If swap is larger than this amount, an alert is generated. Amount should be in MB.
|
39
|
+
# default: 2048 # 2 GB
|
40
|
+
# max_swap_ratio:
|
41
|
+
# notes: If swap used over memory used is larger than this amount, an alert is generated
|
42
|
+
# default: 3
|
43
|
+
# attributes: required advanced
|
44
|
+
class PluginOptions < Array
|
45
|
+
|
46
|
+
attr_accessor :error
|
47
|
+
|
48
|
+
# Should be valid YAML, a hash of hashes ... if not, will be caught in the rescue below
|
49
|
+
def self.from_yaml(string)
|
50
|
+
options_array=[]
|
51
|
+
error=nil
|
52
|
+
|
53
|
+
items=YAML.load(string)
|
54
|
+
items.each_pair {|name, hash| options_array.push(PluginOption.new(name,hash)) }
|
55
|
+
rescue
|
56
|
+
error="Invalid Plugin Options"
|
57
|
+
ensure
|
58
|
+
res=PluginOptions.new(options_array)
|
59
|
+
res.error=error
|
60
|
+
return res
|
61
|
+
end
|
62
|
+
|
63
|
+
def advanced
|
64
|
+
select{|o|o.advanced? }
|
65
|
+
end
|
66
|
+
|
67
|
+
def regular
|
68
|
+
select{|o|!o.advanced? }
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_s
|
72
|
+
res=[]
|
73
|
+
each_with_index do |opt,i|
|
74
|
+
res.push "#{i+1}. #{opt.to_s}"
|
75
|
+
end
|
76
|
+
res.join("\n")
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
data/lib/scout/server.rb
CHANGED
@@ -34,7 +34,10 @@ module Scout
|
|
34
34
|
# We consider the interval close enough at this point.
|
35
35
|
#
|
36
36
|
RUN_DELTA = 30
|
37
|
-
|
37
|
+
|
38
|
+
attr_reader :new_plan
|
39
|
+
attr_reader :directives
|
40
|
+
|
38
41
|
# Creates a new Scout Server connection.
|
39
42
|
def initialize(server, client_key, history_file, logger = nil)
|
40
43
|
@server = server
|
@@ -42,56 +45,116 @@ module Scout
|
|
42
45
|
@history_file = history_file
|
43
46
|
@history = Hash.new
|
44
47
|
@logger = logger
|
45
|
-
|
48
|
+
@plugin_plan = []
|
49
|
+
@directives = {} # take_snapshots
|
50
|
+
@new_plan = false
|
51
|
+
|
52
|
+
# the block is only passed for install and test, since we split plan retrieval outside the lockfile for run
|
46
53
|
if block_given?
|
47
54
|
load_history
|
48
55
|
yield self
|
49
56
|
save_history
|
50
57
|
end
|
51
58
|
end
|
52
|
-
|
53
|
-
# Prepares a check-in data structure to hold Plugin generated data.
|
54
|
-
def prepare_checkin
|
55
|
-
@checkin = { :reports => Array.new,
|
56
|
-
:alerts => Array.new,
|
57
|
-
:errors => Array.new,
|
58
|
-
:summaries => Array.new }
|
59
|
-
end
|
60
|
-
|
61
|
-
def show_checkin(printer = :p)
|
62
|
-
send(printer, @checkin)
|
63
|
-
end
|
64
|
-
|
65
|
-
#
|
66
|
-
# Loads the history file from disk. If the file does not exist,
|
67
|
-
# it creates one.
|
59
|
+
|
68
60
|
#
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
61
|
+
# Retrieves the Plugin Plan from the server. This is the list of plugins
|
62
|
+
# to execute, along with all options.
|
63
|
+
#
|
64
|
+
# This method has a couple of side effects:
|
65
|
+
# 1) it sets the @plugin plan with either A) whatever is in history, B) the results of the /plan retrieval
|
66
|
+
# 2) it sets @checkin_to = true IF so directed by the scout server
|
67
|
+
def fetch_plan
|
68
|
+
url = urlify(:plan)
|
69
|
+
info "Pinging server at #{url}..."
|
70
|
+
headers = Hash.new
|
71
|
+
if @history["plan_last_modified"] and @history["old_plugins"]
|
72
|
+
headers["If-Modified-Since"] = @history["plan_last_modified"]
|
73
|
+
end
|
74
|
+
get(url, "Could not retrieve plan from server.", headers) do |res|
|
75
|
+
if res.is_a? Net::HTTPNotModified
|
76
|
+
info "Plan not modified. Will reuse saved plan."
|
77
|
+
@plugin_plan = Array(@history["old_plugins"])
|
78
|
+
@directives = @history["directives"] || Hash.new
|
79
|
+
else
|
80
|
+
info "plan has been modified. Will run the new plan now."
|
81
|
+
begin
|
82
|
+
body = res.body
|
83
|
+
if res["Content-Encoding"] == "gzip" and body and not body.empty?
|
84
|
+
body = Zlib::GzipReader.new(StringIO.new(body)).read
|
85
|
+
end
|
86
|
+
|
87
|
+
body_as_hash = JSON.parse(body)
|
88
|
+
@plugin_plan = Array(body_as_hash["plugins"])
|
89
|
+
@directives = body_as_hash["directives"].is_a?(Hash) ? body_as_hash["directives"] : Hash.new
|
90
|
+
|
91
|
+
@history["plan_last_modified"] = res["last-modified"]
|
92
|
+
@history["old_plugins"] = @plugin_plan
|
93
|
+
@history["directives"] = @directives
|
94
|
+
|
95
|
+
info "Plan loaded. (#{@plugin_plan.size} plugins: " +
|
96
|
+
"#{@plugin_plan.map { |p| p['name'] }.join(', ')})" +
|
97
|
+
". Directives: #{@directives.to_a.map{|a| "#{a.first}:#{a.last}"}.join(", ")}"
|
98
|
+
|
99
|
+
@new_plan = true # used in determination if we should checkin this time or not
|
100
|
+
rescue Exception
|
101
|
+
fatal "Plan from server was malformed."
|
102
|
+
exit
|
103
|
+
end
|
74
104
|
end
|
75
|
-
info "History file created."
|
76
105
|
end
|
77
|
-
debug "Loading history file..."
|
78
|
-
@history = File.open(@history_file) { |file| YAML.load(file) }
|
79
|
-
info "History file loaded."
|
80
106
|
end
|
81
|
-
|
82
|
-
#
|
83
|
-
def
|
84
|
-
|
85
|
-
|
86
|
-
|
107
|
+
|
108
|
+
# uses values from history and current time to determine if we should checkin at this time
|
109
|
+
def time_to_checkin?
|
110
|
+
@history['last_checkin'] == nil ||
|
111
|
+
@directives['interval'] == nil ||
|
112
|
+
(Time.now.to_i - Time.at(@history['last_checkin']).to_i).abs+15 > @directives['interval'].to_i*60
|
113
|
+
rescue
|
114
|
+
debug "Failed to calculate time_to_checkin. @history['last_checkin']=#{@history['last_checkin']}. "+
|
115
|
+
"@directives['interval']=#{@directives['interval']}. Time.now.to_i=#{Time.now.to_i}"
|
116
|
+
return true
|
87
117
|
end
|
88
|
-
|
89
|
-
#
|
118
|
+
|
119
|
+
# uses values from history and current time to determine if we should ping the server at this time
|
120
|
+
def time_to_ping?
|
121
|
+
return true if
|
122
|
+
@history['last_ping'] == nil ||
|
123
|
+
@directives['ping_interval'] == nil ||
|
124
|
+
(Time.now.to_i - Time.at(@history['last_ping']).to_i).abs+15 > @directives['ping_interval'].to_i*60
|
125
|
+
rescue
|
126
|
+
debug "Failed to calculate time_to_ping. @history['last_ping']=#{@history['last_ping']}. "+
|
127
|
+
"@directives['ping_interval']=#{@directives['ping_interval']}. Time.now.to_i=#{Time.now.to_i}"
|
128
|
+
return true
|
129
|
+
end
|
130
|
+
|
131
|
+
# returns a human-readable representation of the next checkin, i.e., 5min 30sec
|
132
|
+
def next_checkin
|
133
|
+
secs= @directives['interval'].to_i*60 - (Time.now.to_i - Time.at(@history['last_checkin']).to_i).abs
|
134
|
+
minutes=(secs.to_f/60).floor
|
135
|
+
secs=secs%60
|
136
|
+
"#{minutes}min #{secs} sec"
|
137
|
+
rescue
|
138
|
+
"[next scout invocation]"
|
139
|
+
end
|
140
|
+
|
141
|
+
# Runs all plugins from the given plan. Calls process_plugin on each plugin.
|
142
|
+
# @plugin_execution_plan is populated by calling fetch_plan
|
90
143
|
def run_plugins_by_plan
|
91
144
|
prepare_checkin
|
92
|
-
|
93
|
-
|
145
|
+
@plugin_plan.each do |plugin|
|
146
|
+
begin
|
147
|
+
process_plugin(plugin)
|
148
|
+
rescue Exception
|
149
|
+
@checkin[:errors] << build_report(
|
150
|
+
plugin['id'],
|
151
|
+
:subject => "Exception: #{$!.message}.",
|
152
|
+
:body => $!.backtrace
|
153
|
+
)
|
154
|
+
error("Encountered an error: #{$!.message}")
|
155
|
+
end
|
94
156
|
end
|
157
|
+
take_snapshot if @directives['take_snapshots']
|
95
158
|
checkin
|
96
159
|
end
|
97
160
|
|
@@ -105,7 +168,7 @@ module Scout
|
|
105
168
|
# set memory and last_run information in the history file.
|
106
169
|
#
|
107
170
|
def process_plugin(plugin)
|
108
|
-
info "Processing the #{plugin['name']} plugin:"
|
171
|
+
info "Processing the '#{plugin['name']}' plugin:"
|
109
172
|
id_and_name = "#{plugin['id']}-#{plugin['name']}".sub(/\A-/, "")
|
110
173
|
last_run = @history["last_runs"][id_and_name] ||
|
111
174
|
@history["last_runs"][plugin['name']]
|
@@ -126,6 +189,7 @@ module Scout
|
|
126
189
|
rescue Exception
|
127
190
|
raise if $!.is_a? SystemExit
|
128
191
|
error "Plugin would not compile: #{$!.message}"
|
192
|
+
@checkin[:errors] << build_report(plugin['id'],:subject => "Plugin would not compile", :body=>$!.message)
|
129
193
|
return
|
130
194
|
end
|
131
195
|
debug "Loading plugin..."
|
@@ -140,13 +204,19 @@ module Scout
|
|
140
204
|
Timeout.timeout(timeout, PluginTimeoutError) do
|
141
205
|
data = job.run
|
142
206
|
end
|
143
|
-
rescue Timeout::Error
|
207
|
+
rescue Timeout::Error, PluginTimeoutError
|
144
208
|
error "Plugin took too long to run."
|
209
|
+
@checkin[:errors] << build_report(plugin['id'],
|
210
|
+
:subject => "Plugin took too long to run",
|
211
|
+
:body=>"Execution timed out.")
|
145
212
|
return
|
146
213
|
rescue Exception
|
147
214
|
raise if $!.is_a? SystemExit
|
148
215
|
error "Plugin failed to run: #{$!.class}: #{$!.message}\n" +
|
149
216
|
"#{$!.backtrace.join("\n")}"
|
217
|
+
@checkin[:errors] << build_report(plugin['id'],
|
218
|
+
:subject => "Plugin failed to run",
|
219
|
+
:body=>"#{$!.class}: #{$!.message}\n#{$!.backtrace.join("\n")}")
|
150
220
|
end
|
151
221
|
info "Plugin completed its run."
|
152
222
|
|
@@ -168,7 +238,7 @@ module Scout
|
|
168
238
|
@history["memory"][id_and_name] = data[:memory]
|
169
239
|
else
|
170
240
|
@checkin[:errors] << build_report(
|
171
|
-
|
241
|
+
plugin['id'],
|
172
242
|
:subject => "Plugin would not load."
|
173
243
|
)
|
174
244
|
end
|
@@ -189,57 +259,62 @@ module Scout
|
|
189
259
|
error "Unable to remove plugin."
|
190
260
|
end
|
191
261
|
end
|
192
|
-
info "Plugin #{plugin['name']} processing complete."
|
262
|
+
info "Plugin '#{plugin['name']}' processing complete."
|
193
263
|
end
|
194
|
-
|
195
|
-
|
196
|
-
#
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
264
|
+
|
265
|
+
|
266
|
+
# captures a list of processes running at this moment
|
267
|
+
def take_snapshot
|
268
|
+
info "Taking a process snapshot"
|
269
|
+
ps=%x(ps aux).split("\n")[1..-1].join("\n") # get rid of the header line
|
270
|
+
@checkin[:snapshot]=ps
|
271
|
+
rescue Exception
|
272
|
+
error "unable to capture processes on this server. #{$!.message}"
|
273
|
+
return nil
|
274
|
+
end
|
275
|
+
|
276
|
+
# Prepares a check-in data structure to hold Plugin generated data.
|
277
|
+
def prepare_checkin
|
278
|
+
@checkin = { :reports => Array.new,
|
279
|
+
:alerts => Array.new,
|
280
|
+
:errors => Array.new,
|
281
|
+
:summaries => Array.new,
|
282
|
+
:snapshot => '' }
|
283
|
+
end
|
284
|
+
|
285
|
+
def show_checkin(printer = :p)
|
286
|
+
send(printer, @checkin)
|
287
|
+
end
|
288
|
+
|
289
|
+
#
|
290
|
+
# Loads the history file from disk. If the file does not exist,
|
291
|
+
# it creates one.
|
292
|
+
#
|
293
|
+
def load_history
|
294
|
+
unless File.exist? @history_file
|
295
|
+
create_blank_history
|
205
296
|
end
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
plugin_execution_plan = Array(JSON.parse(body)["plugins"])
|
217
|
-
if res["Last-Modified"]
|
218
|
-
@history["last_modified_for_plugins"] = res["last-modified"]
|
219
|
-
@history["old_plugins"] = plugin_execution_plan
|
220
|
-
end
|
221
|
-
info "Plan loaded. (#{plugin_execution_plan.size} plugins: " +
|
222
|
-
"#{plugin_execution_plan.map { |p| p['name'] }.join(', ')})"
|
223
|
-
rescue Exception
|
224
|
-
fatal "Plan from server was malformed."
|
225
|
-
exit
|
226
|
-
end
|
227
|
-
end
|
228
|
-
plugin_execution_plan.each do |plugin|
|
229
|
-
begin
|
230
|
-
yield plugin if block_given?
|
231
|
-
rescue RuntimeError
|
232
|
-
@checkin[:errors] << build_report(
|
233
|
-
plugin_id['id'],
|
234
|
-
:subject => "Exception: #{$!.message}.",
|
235
|
-
:body => $!.backtrace
|
236
|
-
)
|
237
|
-
end
|
238
|
-
end
|
297
|
+
debug "Loading history file..."
|
298
|
+
@history = File.open(@history_file) { |file| YAML.load(file) }
|
299
|
+
info "History file loaded."
|
300
|
+
end
|
301
|
+
|
302
|
+
# creates a blank history file
|
303
|
+
def create_blank_history
|
304
|
+
debug "Creating empty history file..."
|
305
|
+
File.open(@history_file, "w") do |file|
|
306
|
+
YAML.dump({"last_runs" => Hash.new, "memory" => Hash.new}, file)
|
239
307
|
end
|
308
|
+
info "History file created."
|
309
|
+
end
|
310
|
+
|
311
|
+
# Saves the history file to disk.
|
312
|
+
def save_history
|
313
|
+
debug "Saving history file..."
|
314
|
+
File.open(@history_file, "w") { |file| YAML.dump(@history, file) }
|
315
|
+
info "History file saved."
|
240
316
|
end
|
241
|
-
|
242
|
-
|
317
|
+
|
243
318
|
private
|
244
319
|
|
245
320
|
def build_report(plugin_id, fields)
|
@@ -293,8 +368,9 @@ module Scout
|
|
293
368
|
when Net::HTTPSuccess, Net::HTTPNotModified
|
294
369
|
response_handler[response] unless response_handler.nil?
|
295
370
|
else
|
371
|
+
error = "Server says: #{response['x-scout-msg']}" if response['x-scout-msg']
|
296
372
|
fatal error
|
297
|
-
|
373
|
+
raise SystemExit.new(error)
|
298
374
|
end
|
299
375
|
rescue Timeout::Error
|
300
376
|
fatal "Request timed out."
|
@@ -306,6 +382,7 @@ module Scout
|
|
306
382
|
end
|
307
383
|
|
308
384
|
def checkin
|
385
|
+
@history['last_checkin'] = Time.now.to_i # might have to save the time of invocation and use here to prevent drift
|
309
386
|
io = StringIO.new
|
310
387
|
gzip = Zlib::GzipWriter.new(io)
|
311
388
|
gzip << @checkin.to_json
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scout
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 5.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Highgroove Studios
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-12-23 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -46,18 +46,12 @@ files:
|
|
46
46
|
- lib/scout/command/test.rb
|
47
47
|
- lib/scout/command.rb
|
48
48
|
- lib/scout/plugin.rb
|
49
|
+
- lib/scout/plugin_options.rb
|
49
50
|
- lib/scout/server.rb
|
50
51
|
- lib/scout.rb
|
51
52
|
- data/cacert.pem
|
52
53
|
- data/gpl-2.0.txt
|
53
54
|
- data/lgpl-2.1.txt
|
54
|
-
- vendor/json_pure/CHANGES
|
55
|
-
- vendor/json_pure/GPL
|
56
|
-
- vendor/json_pure/README
|
57
|
-
- vendor/json_pure/RUBY
|
58
|
-
- vendor/json_pure/Rakefile
|
59
|
-
- vendor/json_pure/TODO
|
60
|
-
- vendor/json_pure/VERSION
|
61
55
|
- vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkComparison.log
|
62
56
|
- vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast-autocorrelation.dat
|
63
57
|
- vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast.dat
|
@@ -93,6 +87,7 @@ files:
|
|
93
87
|
- vendor/json_pure/benchmarks/parser_benchmark.rb
|
94
88
|
- vendor/json_pure/bin/edit_json.rb
|
95
89
|
- vendor/json_pure/bin/prettify_json.rb
|
90
|
+
- vendor/json_pure/CHANGES
|
96
91
|
- vendor/json_pure/data/example.json
|
97
92
|
- vendor/json_pure/data/index.html
|
98
93
|
- vendor/json_pure/data/prototype.js
|
@@ -106,26 +101,30 @@ files:
|
|
106
101
|
- vendor/json_pure/ext/json/ext/parser/parser.rl
|
107
102
|
- vendor/json_pure/ext/json/ext/parser/unicode.c
|
108
103
|
- vendor/json_pure/ext/json/ext/parser/unicode.h
|
104
|
+
- vendor/json_pure/GPL
|
109
105
|
- vendor/json_pure/install.rb
|
110
|
-
- vendor/json_pure/lib/json/Array.xpm
|
111
|
-
- vendor/json_pure/lib/json/FalseClass.xpm
|
112
|
-
- vendor/json_pure/lib/json/Hash.xpm
|
113
|
-
- vendor/json_pure/lib/json/Key.xpm
|
114
|
-
- vendor/json_pure/lib/json/NilClass.xpm
|
115
|
-
- vendor/json_pure/lib/json/Numeric.xpm
|
116
|
-
- vendor/json_pure/lib/json/String.xpm
|
117
|
-
- vendor/json_pure/lib/json/TrueClass.xpm
|
118
106
|
- vendor/json_pure/lib/json/add/core.rb
|
119
107
|
- vendor/json_pure/lib/json/add/rails.rb
|
108
|
+
- vendor/json_pure/lib/json/Array.xpm
|
120
109
|
- vendor/json_pure/lib/json/common.rb
|
121
110
|
- vendor/json_pure/lib/json/editor.rb
|
122
111
|
- vendor/json_pure/lib/json/ext.rb
|
112
|
+
- vendor/json_pure/lib/json/FalseClass.xpm
|
113
|
+
- vendor/json_pure/lib/json/Hash.xpm
|
123
114
|
- vendor/json_pure/lib/json/json.xpm
|
115
|
+
- vendor/json_pure/lib/json/Key.xpm
|
116
|
+
- vendor/json_pure/lib/json/NilClass.xpm
|
117
|
+
- vendor/json_pure/lib/json/Numeric.xpm
|
124
118
|
- vendor/json_pure/lib/json/pure/generator.rb
|
125
119
|
- vendor/json_pure/lib/json/pure/parser.rb
|
126
120
|
- vendor/json_pure/lib/json/pure.rb
|
121
|
+
- vendor/json_pure/lib/json/String.xpm
|
122
|
+
- vendor/json_pure/lib/json/TrueClass.xpm
|
127
123
|
- vendor/json_pure/lib/json/version.rb
|
128
124
|
- vendor/json_pure/lib/json.rb
|
125
|
+
- vendor/json_pure/Rakefile
|
126
|
+
- vendor/json_pure/README
|
127
|
+
- vendor/json_pure/RUBY
|
129
128
|
- vendor/json_pure/tests/fixtures/fail1.json
|
130
129
|
- vendor/json_pure/tests/fixtures/fail10.json
|
131
130
|
- vendor/json_pure/tests/fixtures/fail11.json
|
@@ -163,8 +162,10 @@ files:
|
|
163
162
|
- vendor/json_pure/tests/test_json_generate.rb
|
164
163
|
- vendor/json_pure/tests/test_json_rails.rb
|
165
164
|
- vendor/json_pure/tests/test_json_unicode.rb
|
165
|
+
- vendor/json_pure/TODO
|
166
166
|
- vendor/json_pure/tools/fuzz.rb
|
167
167
|
- vendor/json_pure/tools/server.rb
|
168
|
+
- vendor/json_pure/VERSION
|
168
169
|
- Rakefile
|
169
170
|
- AUTHORS
|
170
171
|
- COPYING
|