scout_agent 3.0.0
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.
- 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
|