scout_agent 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +4 -0
- data/CHANGELOG +3 -0
- data/COPYING +340 -0
- data/INSTALL +17 -0
- data/LICENSE +6 -0
- data/README +3 -0
- data/Rakefile +123 -0
- data/TODO +3 -0
- data/bin/scout_agent +11 -0
- data/lib/scout_agent.rb +73 -0
- data/lib/scout_agent/agent.rb +42 -0
- data/lib/scout_agent/agent/communication_agent.rb +85 -0
- data/lib/scout_agent/agent/master_agent.rb +301 -0
- data/lib/scout_agent/api.rb +241 -0
- data/lib/scout_agent/assignment.rb +105 -0
- data/lib/scout_agent/assignment/configuration.rb +30 -0
- data/lib/scout_agent/assignment/identify.rb +110 -0
- data/lib/scout_agent/assignment/queue.rb +95 -0
- data/lib/scout_agent/assignment/reset.rb +91 -0
- data/lib/scout_agent/assignment/snapshot.rb +92 -0
- data/lib/scout_agent/assignment/start.rb +149 -0
- data/lib/scout_agent/assignment/status.rb +44 -0
- data/lib/scout_agent/assignment/stop.rb +60 -0
- data/lib/scout_agent/assignment/upload_log.rb +61 -0
- data/lib/scout_agent/core_extensions.rb +260 -0
- data/lib/scout_agent/database.rb +386 -0
- data/lib/scout_agent/database/mission_log.rb +282 -0
- data/lib/scout_agent/database/queue.rb +126 -0
- data/lib/scout_agent/database/snapshots.rb +187 -0
- data/lib/scout_agent/database/statuses.rb +65 -0
- data/lib/scout_agent/dispatcher.rb +157 -0
- data/lib/scout_agent/id_card.rb +143 -0
- data/lib/scout_agent/lifeline.rb +243 -0
- data/lib/scout_agent/mission.rb +212 -0
- data/lib/scout_agent/order.rb +58 -0
- data/lib/scout_agent/order/check_in_order.rb +32 -0
- data/lib/scout_agent/order/snapshot_order.rb +33 -0
- data/lib/scout_agent/plan.rb +306 -0
- data/lib/scout_agent/server.rb +123 -0
- data/lib/scout_agent/tracked.rb +59 -0
- data/lib/scout_agent/wire_tap.rb +513 -0
- data/setup.rb +1360 -0
- data/test/tc_core_extensions.rb +89 -0
- data/test/tc_id_card.rb +115 -0
- data/test/tc_plan.rb +285 -0
- data/test/test_helper.rb +22 -0
- data/test/ts_all.rb +7 -0
- metadata +171 -0
@@ -0,0 +1,212 @@
|
|
1
|
+
#!/usr/bin/env ruby -wKU
|
2
|
+
|
3
|
+
module ScoutAgent
|
4
|
+
class Mission
|
5
|
+
class << self
|
6
|
+
attr_reader :prepared
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.inherited(new_plugin)
|
10
|
+
@prepared = new_plugin
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
# You can use this method to indicate one or more libraries your plugin
|
15
|
+
# requires:
|
16
|
+
#
|
17
|
+
# class MyNeedyPlugin < Scout::Plugin
|
18
|
+
# needs "faster_csv", "elif"
|
19
|
+
# # ...
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# Your build_report() method will not be called if all libraries cannot
|
23
|
+
# be loaded. RubyGems will be loaded if needed to find the libraries.
|
24
|
+
#
|
25
|
+
def self.needs(*libraries)
|
26
|
+
if libraries.empty?
|
27
|
+
@needs ||= [ ]
|
28
|
+
else
|
29
|
+
needs.push(*libraries.flatten)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Creates a new Scout Plugin to run.
|
34
|
+
def initialize(id, name, last_run, memory, options)
|
35
|
+
@id = id
|
36
|
+
@name = name
|
37
|
+
@last_run = last_run
|
38
|
+
@memory = memory
|
39
|
+
@options = options
|
40
|
+
@mission_log_db = Database.load(:mission_log)
|
41
|
+
@queue_db = Database.load(:queue)
|
42
|
+
end
|
43
|
+
|
44
|
+
attr_reader :last_run
|
45
|
+
|
46
|
+
def option(name)
|
47
|
+
@options[name] ||
|
48
|
+
@options[name.is_a?(String) ? name.to_sym : String(name)]
|
49
|
+
end
|
50
|
+
|
51
|
+
# Builds the data to send to the server.
|
52
|
+
#
|
53
|
+
# We programatically define several helper methods for creating this data.
|
54
|
+
#
|
55
|
+
# Usage:
|
56
|
+
#
|
57
|
+
# reports << {:data => "here"}
|
58
|
+
# report(:data => "here")
|
59
|
+
# add_report(:data => "here")
|
60
|
+
#
|
61
|
+
# alerts << {:subject => "subject", :body => "body"}
|
62
|
+
# alert("subject", "body")
|
63
|
+
# alert(:subject => "subject", :body => "body")
|
64
|
+
# add_alert("subject", "body")
|
65
|
+
# add_alert(:subject => "subject", :body => "body")
|
66
|
+
#
|
67
|
+
# errors << {:subject => "subject", :body => "body"}
|
68
|
+
# error("subject", "body")
|
69
|
+
# error(:subject => "subject", :body => "body")
|
70
|
+
# add_error("subject", "body")
|
71
|
+
# add_error(:subject => "subject", :body => "body")
|
72
|
+
#
|
73
|
+
def data_for_server
|
74
|
+
@data_for_server ||= { :reports => [ ],
|
75
|
+
:hints => [ ],
|
76
|
+
:alerts => [ ],
|
77
|
+
:errors => [ ],
|
78
|
+
:memory => { } }
|
79
|
+
end
|
80
|
+
|
81
|
+
%w[report hint alert error].each do |kind|
|
82
|
+
class_eval <<-END
|
83
|
+
def #{kind}s
|
84
|
+
data_for_server[:#{kind}s]
|
85
|
+
end
|
86
|
+
|
87
|
+
if %w[report hint].include? "#{kind}"
|
88
|
+
def #{kind}(new_entry)
|
89
|
+
#{kind}s << new_entry
|
90
|
+
end
|
91
|
+
else
|
92
|
+
def #{kind}(*fields)
|
93
|
+
#{kind}s << ( fields.first.is_a?(Hash) ?
|
94
|
+
fields.first :
|
95
|
+
{:subject => fields.first, :body => fields.last} )
|
96
|
+
end
|
97
|
+
end
|
98
|
+
alias_method :add_#{kind}, :#{kind}
|
99
|
+
END
|
100
|
+
end
|
101
|
+
|
102
|
+
#
|
103
|
+
# Usage:
|
104
|
+
#
|
105
|
+
# memory(:no_track)
|
106
|
+
# memory.delete(:no_track)
|
107
|
+
# memory.clear
|
108
|
+
#
|
109
|
+
def memory(name = nil)
|
110
|
+
if name.nil?
|
111
|
+
data_for_server[:memory]
|
112
|
+
else
|
113
|
+
@memory[name] ||
|
114
|
+
@memory[name.is_a?(String) ? name.to_sym : String(name)]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# Usage:
|
120
|
+
#
|
121
|
+
# remember(:name, value)
|
122
|
+
# remember(:name1, value1, :name2, value2)
|
123
|
+
# remember(:name => value)
|
124
|
+
# remember(:name1 => value1, :name2 => value2)
|
125
|
+
# remember(:name1, value1, :name2 => value2)
|
126
|
+
#
|
127
|
+
def remember(*args)
|
128
|
+
hashes, other = args.partition { |value| value.is_a? Hash }
|
129
|
+
hashes.each { |hash| memory.merge!(hash) }
|
130
|
+
(0...other.size).step(2) { |i| memory.merge!(other[i] => other[i + 1]) }
|
131
|
+
end
|
132
|
+
|
133
|
+
def each_queued_message(&block)
|
134
|
+
while message = @queue_db.peek(@id)
|
135
|
+
if block.arity == 1
|
136
|
+
block[message[:fields]]
|
137
|
+
else
|
138
|
+
block[message[:fields], message[:created_at]]
|
139
|
+
end
|
140
|
+
@queue_db.dequeue(message[:id]) or return # prevent infinite loop
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
#
|
145
|
+
# Old plugins will work because they override this method. New plugins can
|
146
|
+
# now leave this method in place, add a build_report() method instead, and
|
147
|
+
# use the new helper methods to build up content inside which will
|
148
|
+
# automatically be returned as the end result of the run.
|
149
|
+
#
|
150
|
+
def run
|
151
|
+
#
|
152
|
+
# run the plugin in a new Thread so signals will be handled in the main
|
153
|
+
# Thread and not accidentally caught by rescue clauses in the body of the
|
154
|
+
# mission
|
155
|
+
#
|
156
|
+
Thread.new do
|
157
|
+
Thread.current.abort_on_exception = true
|
158
|
+
if self.class.needs.all? { |l| library_available?(l) }
|
159
|
+
begin
|
160
|
+
build_report
|
161
|
+
rescue Exception
|
162
|
+
raise if $!.is_a? SystemExit # don't catch exit() calls
|
163
|
+
error "#{@name} run failed with an error", <<-END_ERROR.trim
|
164
|
+
Error:
|
165
|
+
#{$!.class}
|
166
|
+
Message:
|
167
|
+
#{$!.message}
|
168
|
+
Backtrace:
|
169
|
+
#{$!.backtrace.map { |line| " #{line}\n" }.join}
|
170
|
+
END_ERROR
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end.join # wait for completion so we have reports to write
|
174
|
+
write_reports_and_update_memory
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
#
|
180
|
+
# Returns true is a library can be loaded. A bare require is made as the
|
181
|
+
# first attempt. If that fails, RubyGems is loaded and another attempt is
|
182
|
+
# made. If the library cannot be loaded either way, an error() is generated
|
183
|
+
# and build_report() will not be called.
|
184
|
+
#
|
185
|
+
def library_available?(library)
|
186
|
+
begin
|
187
|
+
require library
|
188
|
+
rescue LoadError
|
189
|
+
begin
|
190
|
+
require "rubygems"
|
191
|
+
require library
|
192
|
+
rescue LoadError
|
193
|
+
error("Failed to load library", "Could not load #{library}")
|
194
|
+
return false
|
195
|
+
end
|
196
|
+
end
|
197
|
+
true
|
198
|
+
end
|
199
|
+
|
200
|
+
def write_reports_and_update_memory
|
201
|
+
%w[report hint alert error].each do |type|
|
202
|
+
Array(send("#{type}s")).each do |fields|
|
203
|
+
@mission_log_db.write_report(@id, type, fields)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
@mission_log_db.update_mission_memory(@id, data_for_server[:memory])
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# An external alias.
|
211
|
+
Plugin = Mission
|
212
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/bin/env ruby -wKU
|
2
|
+
|
3
|
+
module ScoutAgent
|
4
|
+
class Order
|
5
|
+
ORDERS_DIR = LIB_DIR + "order"
|
6
|
+
|
7
|
+
def self.log=(log)
|
8
|
+
@log = log
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.log
|
12
|
+
@log ||= WireTap.new(nil)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.subclasses
|
16
|
+
@subclasses ||= Array.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.inherited(order)
|
20
|
+
subclasses << order
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.load_all
|
24
|
+
ORDERS_DIR.each_entry do |order|
|
25
|
+
require ORDERS_DIR + order unless order.basename.to_s[0] == ?.
|
26
|
+
end
|
27
|
+
subclasses
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.can_handle?(message)
|
31
|
+
subclasses.each do |order|
|
32
|
+
if match_details = order.match?(message)
|
33
|
+
return order.new(message, match_details)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.match?(message)
|
39
|
+
const_defined?(:MATCH_RE) and const_get(:MATCH_RE).match(message.body)
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize(message, match_details)
|
43
|
+
@message = message
|
44
|
+
@match_details = match_details
|
45
|
+
end
|
46
|
+
|
47
|
+
attr_reader :message, :match_details
|
48
|
+
|
49
|
+
def log
|
50
|
+
Order.log
|
51
|
+
end
|
52
|
+
|
53
|
+
def execute
|
54
|
+
raise NotImplementedError,
|
55
|
+
"Subclasses must override ScoutAgent::Order#execute()."
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby -wKU
|
2
|
+
|
3
|
+
module ScoutAgent
|
4
|
+
class Order
|
5
|
+
class CheckInOrder < Order
|
6
|
+
MATCH_RE = /\A\s*check[-_]?in(?:\s+(.+?))?\s*\z/
|
7
|
+
|
8
|
+
def self.mission_log
|
9
|
+
return @mission_log if defined? @mission_log
|
10
|
+
unless db = Database.load(:mission_log, log)
|
11
|
+
log.fatal("Could not load mission log database.")
|
12
|
+
exit
|
13
|
+
end
|
14
|
+
@mission_log = db
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.master_agent
|
18
|
+
@master_agent ||= IDCard.new(:master)
|
19
|
+
end
|
20
|
+
|
21
|
+
def execute
|
22
|
+
if id_str = match_details.captures.first
|
23
|
+
ids = id_str.scan(/\d+/).map { |n| n.to_i }
|
24
|
+
unless ids.empty?
|
25
|
+
self.class.mission_log.reset_missions(ids)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
self.class.master_agent.signal("ALRM")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby -wKU
|
2
|
+
|
3
|
+
module ScoutAgent
|
4
|
+
class Order
|
5
|
+
class SnapshotOrder < Order
|
6
|
+
MATCH_RE = /\A\s*snap[-_]?shot(?:\s+(.+?))?\s*\z/
|
7
|
+
|
8
|
+
def self.snapshots
|
9
|
+
return @snapshots if defined? @snapshots
|
10
|
+
unless db = Database.load(:snapshots, log)
|
11
|
+
log.fatal("Could not load snapshots database.")
|
12
|
+
exit
|
13
|
+
end
|
14
|
+
@snapshots = db
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.master_agent
|
18
|
+
@master_agent ||= IDCard.new(:master)
|
19
|
+
end
|
20
|
+
|
21
|
+
def execute
|
22
|
+
# if id_str = match_details.captures.first
|
23
|
+
# ids = id_str.scan(/\d+/).map { |n| n.to_i }
|
24
|
+
# unless ids.empty?
|
25
|
+
# self.class.mission_log.reset_missions(ids)
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
API.take_snapshot
|
29
|
+
self.class.master_agent.signal("ALRM")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,306 @@
|
|
1
|
+
#!/usr/bin/env ruby -wKU
|
2
|
+
|
3
|
+
module ScoutAgent
|
4
|
+
#
|
5
|
+
# This constant holds all global configuration for the agent. There's only
|
6
|
+
# this one instance of this data and all systems share it. This data is
|
7
|
+
# configured at startup and should never again change without a restart.
|
8
|
+
#
|
9
|
+
# Users are free to add to this configuration as need (via their configuration
|
10
|
+
# file) and make use of those new values in their plugins.
|
11
|
+
#
|
12
|
+
class << Plan = OpenStruct.new
|
13
|
+
################
|
14
|
+
### Defaults ###
|
15
|
+
################
|
16
|
+
|
17
|
+
# The default configuration for this agent.
|
18
|
+
def defaults
|
19
|
+
[ [:server_url, "https://scoutapp.com"],
|
20
|
+
[:run_as_daemon, true],
|
21
|
+
[:logging_level, "INFO"],
|
22
|
+
[:test_mode, false],
|
23
|
+
|
24
|
+
[:user_choices, %w[daemon nobody]],
|
25
|
+
[:group_choices, %w[daemon nogroup]],
|
26
|
+
[:prefix_path, "/"],
|
27
|
+
[:os_config_path, "etc"],
|
28
|
+
[:os_db_path, "var/db"],
|
29
|
+
[:os_pid_path, "var/run"],
|
30
|
+
[:os_log_path, "var/log"],
|
31
|
+
[:config_file, nil],
|
32
|
+
[:db_dir, nil],
|
33
|
+
[:pid_dir, nil],
|
34
|
+
[:log_dir, nil] ]
|
35
|
+
end
|
36
|
+
|
37
|
+
# This method is used to set or reset the default configuration.
|
38
|
+
def set_defaults
|
39
|
+
defaults.each { |name, value| send("#{name}=", value) }
|
40
|
+
end
|
41
|
+
alias_method :reset_defaults, :set_defaults
|
42
|
+
|
43
|
+
# Provide a more natural interface for a boolean flag.
|
44
|
+
def run_as_daemon? # can't use alias_method() because it's not defined yet
|
45
|
+
run_as_daemon
|
46
|
+
end
|
47
|
+
|
48
|
+
# Provide a more natural interface for a boolean flag.
|
49
|
+
def test_mode? # can't use alias_method() because it's not defined yet
|
50
|
+
test_mode
|
51
|
+
end
|
52
|
+
|
53
|
+
###########################
|
54
|
+
### Configuration Files ###
|
55
|
+
###########################
|
56
|
+
|
57
|
+
# This method loads configuration settings from a plain Ruby file.
|
58
|
+
def update_from_config_file(path = config_file)
|
59
|
+
eval <<-END_UPDATE
|
60
|
+
config = self
|
61
|
+
#{File.read(path)}
|
62
|
+
config
|
63
|
+
END_UPDATE
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# This method places the current configuration into a standard configuration
|
68
|
+
# file. This makes it easy for users to edit this file and modify all
|
69
|
+
# future runs of the agent.
|
70
|
+
#
|
71
|
+
def write_default_config_file
|
72
|
+
config_file.dirname.mkpath
|
73
|
+
config_file.open(File::CREAT|File::EXCL|File::WRONLY) do |pid|
|
74
|
+
pid.puts <<-END_DEFAULT_CONFIG.trim
|
75
|
+
#!/usr/bin/env ruby -wKU
|
76
|
+
|
77
|
+
#
|
78
|
+
# This file configures #{ScoutAgent.proper_agent_name}. The settings in
|
79
|
+
# here should get you started. You can tweak these as the need arises.
|
80
|
+
#
|
81
|
+
# Any Ruby code you would like run at start-up is a valid addition
|
82
|
+
# to this file.
|
83
|
+
#
|
84
|
+
|
85
|
+
####################
|
86
|
+
### Basic Config ###
|
87
|
+
####################
|
88
|
+
|
89
|
+
# The key below is how the server identifies this machine:
|
90
|
+
config.agent_key = #{agent_key.inspect}
|
91
|
+
|
92
|
+
# The following is the URL used to reach the server:
|
93
|
+
config.server_url = #{server_url.inspect}
|
94
|
+
|
95
|
+
#
|
96
|
+
# When the following is true, the agent will disconnect from the
|
97
|
+
# terminal that launches it and run in the background. You can
|
98
|
+
# switch this to false to have the program stay connected and log
|
99
|
+
# to STDOUT. This can be handy when debugging issues.
|
100
|
+
#
|
101
|
+
# You can set this option to false for a single run with the
|
102
|
+
# command-line switch --no-daemon.
|
103
|
+
#
|
104
|
+
config.run_as_daemon = #{run_as_daemon.inspect}
|
105
|
+
|
106
|
+
#
|
107
|
+
# The following sets your logging level for the agent. This setting can
|
108
|
+
# be one of "DEBUG", "INFO", "WARN", "ERROR", or "FATAL". Messages
|
109
|
+
# below the seleted level will not be written to the logs. "DEBUG" is
|
110
|
+
# intended for the developers as it includes some very low level data,
|
111
|
+
# "INFO" (the default) is good general purpose logging that will what
|
112
|
+
# the agent does as it works, and "WARN" is a good choice if you want
|
113
|
+
# to minimize log space taken but still see problems. Logs are rotated
|
114
|
+
# daily and deleted after seven days though, so you shouldn't need to
|
115
|
+
# worry about the agent filling up your hard drive.
|
116
|
+
#
|
117
|
+
# This agent includes a command to upload logs to our server. This is
|
118
|
+
# never done automatically or without your permission, but it can help
|
119
|
+
# us troubleshoot issues on your box. We will give you the proper
|
120
|
+
# command for this during a support request if we think it would help.
|
121
|
+
# Rest assured that agent logs do not contain sensative data.
|
122
|
+
#
|
123
|
+
config.logging_level = #{logging_level.inspect}
|
124
|
+
|
125
|
+
#####################
|
126
|
+
### Expert Config ###
|
127
|
+
#####################
|
128
|
+
|
129
|
+
#
|
130
|
+
# The agent will try to use standard Unix locations to store its
|
131
|
+
# data, but you are free to adjust these paths as needed.
|
132
|
+
#
|
133
|
+
# The prefix is prepended to all other paths, so you can change
|
134
|
+
# just that to move all agent related storage. The other three
|
135
|
+
# paths are specific locations where resources will be stored,
|
136
|
+
# so you can adjust those separately if needed.
|
137
|
+
#
|
138
|
+
# Note: to have the prefix effect the location of this file or
|
139
|
+
# to set your OS's configuration path, you will need to use
|
140
|
+
# command-line switches (--prefix and --os-config-path respectively).
|
141
|
+
# It wouldn't help to have those settings in this file, for obvious
|
142
|
+
# reasons.
|
143
|
+
#
|
144
|
+
config.prefix_path = #{prefix_path.to_s.inspect}
|
145
|
+
|
146
|
+
config.os_db_path = #{os_db_path.
|
147
|
+
relative_path_from(prefix_path).to_s.inspect}
|
148
|
+
config.os_pid_path = #{os_pid_path.
|
149
|
+
relative_path_from(prefix_path).to_s.inspect}
|
150
|
+
config.os_log_path = #{os_log_path.
|
151
|
+
relative_path_from(prefix_path).to_s.inspect}
|
152
|
+
|
153
|
+
#
|
154
|
+
# The agent must be started with super user privileges so it can
|
155
|
+
# prepare the environment for a long run. However, it will abandon
|
156
|
+
# these privileges once setup is complete to better protect your
|
157
|
+
# security.
|
158
|
+
#
|
159
|
+
# The following are the list of users and groups the agent will
|
160
|
+
# try to switch to. Choices are tried from left to right, so by
|
161
|
+
# default the "daemon" user is used but the agent will try
|
162
|
+
# "nobody" if daemon is not available. These are standard users
|
163
|
+
# and groups for processes like this that run on Unix.
|
164
|
+
#
|
165
|
+
# If you wish to improve security even more, we recommend creating
|
166
|
+
# a user and group called "#{ScoutAgent.agent_name}" and replacing these
|
167
|
+
# defaults with what you created. This puts you full control
|
168
|
+
# of what the agent will have access to on your system and makes
|
169
|
+
# it easier to track what the agent is doing.
|
170
|
+
#
|
171
|
+
config.user_choices = %w[#{user_choices.join(" ")}]
|
172
|
+
config.group_choices = %w[#{group_choices.join(" ")}]
|
173
|
+
|
174
|
+
############################
|
175
|
+
### Your Personal Config ###
|
176
|
+
############################
|
177
|
+
|
178
|
+
# Add any additional Ruby code you need to run at start-up below...
|
179
|
+
END_DEFAULT_CONFIG
|
180
|
+
end
|
181
|
+
config_file.chmod(0755)
|
182
|
+
true
|
183
|
+
rescue Errno::EEXIST # file already exists
|
184
|
+
false
|
185
|
+
rescue Errno::EACCES # don't have permission
|
186
|
+
false
|
187
|
+
end
|
188
|
+
|
189
|
+
#############################
|
190
|
+
### Command-line Switches ###
|
191
|
+
#############################
|
192
|
+
|
193
|
+
# This method allows mass update through a +hash+ of settings.
|
194
|
+
def update_from_switches(hash)
|
195
|
+
hash.each { |name, value| send("#{name}=", value) }
|
196
|
+
end
|
197
|
+
alias_method :update_from_hash, :update_from_switches
|
198
|
+
|
199
|
+
#############
|
200
|
+
### Paths ###
|
201
|
+
#############
|
202
|
+
|
203
|
+
# A prefix used in all configuration paths.
|
204
|
+
def prefix_path
|
205
|
+
Pathname.new(super)
|
206
|
+
end
|
207
|
+
|
208
|
+
#
|
209
|
+
# This code defines OS specific path accessors that honor the +prefix_path+.
|
210
|
+
# These paths prefix all of the specific application paths.
|
211
|
+
#
|
212
|
+
%w[os_config_path os_db_path os_pid_path os_log_path].each do |path|
|
213
|
+
define_method(path) do
|
214
|
+
prefix_path + super
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
#
|
219
|
+
# The full path to the loaded configuration file, prefixed by +prefix_path+
|
220
|
+
# and the +os_config_path+. By default, this file is named after the agent.
|
221
|
+
#
|
222
|
+
def config_file
|
223
|
+
os_config_path + (super || "#{ScoutAgent.agent_name}.rb")
|
224
|
+
end
|
225
|
+
|
226
|
+
#
|
227
|
+
# This code defines the application specific directory paths, prefixed by
|
228
|
+
# +prefix_path+ and the related OS directory. By default, these directories
|
229
|
+
# are named after the agent.
|
230
|
+
#
|
231
|
+
%w[db pid log].each do |path|
|
232
|
+
define_method("#{path}_dir") do
|
233
|
+
send("os_#{path}_path") + (super || ScoutAgent.agent_name)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
#
|
238
|
+
# This code creates builders for the configuration directories. These
|
239
|
+
# methods build the directories and set their group and permissons so
|
240
|
+
# they can be written to after the daemon surrenders super user privileges.
|
241
|
+
#
|
242
|
+
{ :db_dir => 0777,
|
243
|
+
:pid_dir => 0775,
|
244
|
+
:log_dir => 0777 }.each do |path, permissions|
|
245
|
+
define_method("build_#{path}") do |group_id|
|
246
|
+
begin
|
247
|
+
send(path).mkpath
|
248
|
+
send(path).chown(nil, group_id)
|
249
|
+
send(path).chmod(permissions)
|
250
|
+
true
|
251
|
+
rescue Errno::EACCES, Errno::EPERM # don't have permission
|
252
|
+
false
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
#############
|
258
|
+
### URL's ###
|
259
|
+
#############
|
260
|
+
|
261
|
+
def agent_url
|
262
|
+
URI.join(server_url, "clients/#{agent_key}")
|
263
|
+
end
|
264
|
+
|
265
|
+
#################
|
266
|
+
### Databases ###
|
267
|
+
#################
|
268
|
+
|
269
|
+
def prepare_global_database(name)
|
270
|
+
db = Database.load(name) or return false
|
271
|
+
db.path.chmod(0777)
|
272
|
+
true
|
273
|
+
rescue Errno::EPERM # don't have permission
|
274
|
+
false
|
275
|
+
end
|
276
|
+
|
277
|
+
##################
|
278
|
+
### Validation ###
|
279
|
+
##################
|
280
|
+
|
281
|
+
# Returns +true+ if all needed configuration paths exist.
|
282
|
+
def present?
|
283
|
+
%w[config_file db_dir log_dir].all? { |path| send(path).exist? } and
|
284
|
+
%w[queue snapshots].all? { |db| Database.path(db).exist? }
|
285
|
+
end
|
286
|
+
|
287
|
+
#
|
288
|
+
# Returns +true+ if <tt>present?</tt> and all needed configuration paths
|
289
|
+
# have proper permisions as far as this current run is concerned.
|
290
|
+
#
|
291
|
+
def valid?
|
292
|
+
present? and
|
293
|
+
%w[config_file db_dir log_dir].all? { |path| send(path).readable? } and
|
294
|
+
%w[db_dir log_dir].all? { |path| send(path).writable? } and
|
295
|
+
%w[queue snapshots].map { |db| Database.path(db) }.
|
296
|
+
all? { |path| path.readable? and
|
297
|
+
path.writable? }
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
#
|
302
|
+
# Set the defaults after we have overriden some accessors, to keep OpenStruct
|
303
|
+
# from replacing them.
|
304
|
+
#
|
305
|
+
Plan.set_defaults
|
306
|
+
end
|