tyrantmanager 1.0.9
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/HISTORY +28 -0
- data/LICENSE +13 -0
- data/README +273 -0
- data/bin/tyrantmanager +10 -0
- data/data/config.rb +94 -0
- data/data/default_instance_config.rb +244 -0
- data/gemspec.rb +51 -0
- data/lib/tyrant_manager/cli.rb +145 -0
- data/lib/tyrant_manager/command.rb +118 -0
- data/lib/tyrant_manager/commands/create_instance.rb +27 -0
- data/lib/tyrant_manager/commands/list.rb +28 -0
- data/lib/tyrant_manager/commands/start.rb +32 -0
- data/lib/tyrant_manager/commands/stats.rb +25 -0
- data/lib/tyrant_manager/commands/status.rb +22 -0
- data/lib/tyrant_manager/commands/stop.rb +23 -0
- data/lib/tyrant_manager/log.rb +91 -0
- data/lib/tyrant_manager/paths.rb +77 -0
- data/lib/tyrant_manager/runner.rb +33 -0
- data/lib/tyrant_manager/tyrant_instance.rb +340 -0
- data/lib/tyrant_manager/version.rb +27 -0
- data/lib/tyrant_manager.rb +237 -0
- data/lib/tyrantmanager.rb +2 -0
- data/spec/command_spec.rb +37 -0
- data/spec/paths_spec.rb +57 -0
- data/spec/spec_helper.rb +58 -0
- data/spec/tyrant_instance_spec.rb +83 -0
- data/spec/tyrant_manager_spec.rb +69 -0
- data/spec/version_spec.rb +16 -0
- data/tasks/announce.rake +43 -0
- data/tasks/config.rb +99 -0
- data/tasks/distribution.rake +38 -0
- data/tasks/documentation.rake +32 -0
- data/tasks/rspec.rake +29 -0
- data/tasks/rubyforge.rake +51 -0
- data/tasks/utils.rb +80 -0
- metadata +144 -0
@@ -0,0 +1,340 @@
|
|
1
|
+
require 'tyrant_manager'
|
2
|
+
require 'rufus/tokyo/tyrant'
|
3
|
+
require 'erb'
|
4
|
+
|
5
|
+
class TyrantManager
|
6
|
+
class TyrantInstance
|
7
|
+
class << TyrantInstance
|
8
|
+
def logger
|
9
|
+
Logging::Logger[self]
|
10
|
+
end
|
11
|
+
#
|
12
|
+
# Create all the directories needed for a tyrant
|
13
|
+
#
|
14
|
+
def setup( dir )
|
15
|
+
unless File.directory?( dir )
|
16
|
+
logger.info "Creating directory #{dir}"
|
17
|
+
FileUtils.mkdir_p( dir )
|
18
|
+
end
|
19
|
+
|
20
|
+
cfg = File.join( dir, TyrantManager.config_file_basename )
|
21
|
+
instance_name = File.basename( dir )
|
22
|
+
|
23
|
+
unless File.exist?( cfg )
|
24
|
+
template = TyrantManager::Paths.data_path( "default_instance_config.rb" )
|
25
|
+
logger.info "Creating default config file #{cfg}"
|
26
|
+
File.open( cfg, "w+" ) do |f|
|
27
|
+
f.write ERB.new( IO.read( template ) ).result( binding )
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
%w[ ulog data lua log ].each do |subdir|
|
32
|
+
subdir = File.join( dir, subdir )
|
33
|
+
unless File.directory?( subdir ) then
|
34
|
+
logger.info "Creating directory #{subdir}"
|
35
|
+
FileUtils.mkdir subdir
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
return TyrantInstance.new( dir )
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# the full path to the instance home directory
|
44
|
+
attr_reader :home_dir
|
45
|
+
|
46
|
+
# the name of this instance
|
47
|
+
attr_reader :name
|
48
|
+
|
49
|
+
# the manager that is associated with this instance
|
50
|
+
attr_accessor :manager
|
51
|
+
|
52
|
+
#
|
53
|
+
# Create an instance connected to the tyrant located in the given directory
|
54
|
+
#
|
55
|
+
def initialize( dir )
|
56
|
+
@home_dir = File.expand_path( dir )
|
57
|
+
@name = File.basename( @home_dir )
|
58
|
+
if File.exist?( self.config_file ) then
|
59
|
+
configuration # force a load
|
60
|
+
else
|
61
|
+
raise Error, "#{home_dir} is not a valid archive. #{self.config_file} does not exist"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def logger
|
66
|
+
Logging::Logger[self]
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# The configuration file for the instance
|
71
|
+
#
|
72
|
+
def config_file
|
73
|
+
@config_file ||= File.join( home_dir, TyrantManager.config_file_basename )
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# load the configuration
|
78
|
+
#
|
79
|
+
def configuration
|
80
|
+
unless @configuration then
|
81
|
+
eval( IO.read( self.config_file ) )
|
82
|
+
@configuration = Loquacious::Configuration.for( name )
|
83
|
+
end
|
84
|
+
return @configuration
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
# The pid file
|
89
|
+
#
|
90
|
+
def pid_file
|
91
|
+
@pid_file ||= append_to_home_if_not_absolute( configuration.pid_file )
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# The pid of the service
|
96
|
+
#
|
97
|
+
def pid
|
98
|
+
Float( IO.read( pid_file ).strip ).to_i
|
99
|
+
end
|
100
|
+
|
101
|
+
#
|
102
|
+
# The log file
|
103
|
+
#
|
104
|
+
def log_file
|
105
|
+
@log_file ||= append_to_home_if_not_absolute( configuration.log_file )
|
106
|
+
end
|
107
|
+
|
108
|
+
#
|
109
|
+
# The lua extension file to load on start
|
110
|
+
#
|
111
|
+
def lua_extension_file
|
112
|
+
@lua_extension_file ||= append_to_home_if_not_absolute( configuration.lua_extension_file )
|
113
|
+
end
|
114
|
+
|
115
|
+
#
|
116
|
+
# The lua extension file to load on start
|
117
|
+
#
|
118
|
+
def replication_timestamp_file
|
119
|
+
@replication_timestamp_file ||= append_to_home_if_not_absolute( configuration.replication_timestamp_file )
|
120
|
+
end
|
121
|
+
|
122
|
+
#
|
123
|
+
# The directory housing the database file
|
124
|
+
#
|
125
|
+
def data_dir
|
126
|
+
@data_dir ||= append_to_home_if_not_absolute( configuration.data_dir )
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# The full path to the database file.
|
131
|
+
#
|
132
|
+
def db_file( type = configuration.type )
|
133
|
+
unless @db_file then
|
134
|
+
@db_file = case type
|
135
|
+
when "memory-hash" then "*"
|
136
|
+
when "memory-tree" then "+"
|
137
|
+
when "hash" then File.join( data_dir, "#{name}.tch" )
|
138
|
+
when "tree" then File.join( data_dir, "#{name}.tcb" )
|
139
|
+
when "fixed" then File.join( data_dir, "#{name}.tcf" )
|
140
|
+
when "table" then File.join( data_dir, "#{name}.tct" )
|
141
|
+
else
|
142
|
+
raise Error, "Unknown configuration type [#{configuration.type}]"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
return @db_file
|
146
|
+
end
|
147
|
+
|
148
|
+
#
|
149
|
+
# The directory housing the database file
|
150
|
+
#
|
151
|
+
def ulog_dir
|
152
|
+
@ulog_dir ||= append_to_home_if_not_absolute( configuration.ulog_dir )
|
153
|
+
end
|
154
|
+
|
155
|
+
#
|
156
|
+
# The lua extension file
|
157
|
+
#
|
158
|
+
def lua_extension_file
|
159
|
+
@lua_extension_file ||= append_to_home_if_not_absolute( configuration.lua_extension_file )
|
160
|
+
end
|
161
|
+
|
162
|
+
#
|
163
|
+
# The replication timestamp file
|
164
|
+
#
|
165
|
+
def replication_timestamp_file
|
166
|
+
@replication_timestamp_file ||= append_to_home_if_not_absolute( configuration.replication_timestamp_file )
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
#
|
171
|
+
# Start command.
|
172
|
+
#
|
173
|
+
# This is a bit convoluted to bring together all the options and put them
|
174
|
+
# into one big commandline item.
|
175
|
+
#
|
176
|
+
def start_command
|
177
|
+
|
178
|
+
##-- ttserver executable
|
179
|
+
parts = [ manager.configuration.ttserver ]
|
180
|
+
|
181
|
+
##-- host and port
|
182
|
+
parts << "-host #{configuration.host}" if configuration.host
|
183
|
+
parts << "-port #{configuration.port}" if configuration.port
|
184
|
+
|
185
|
+
##-- thread options
|
186
|
+
if thnum = cascading_config( 'thread_count' ) then
|
187
|
+
parts << "-thnum #{thnum}"
|
188
|
+
end
|
189
|
+
if tout = cascading_config( 'session_timeout' ) then
|
190
|
+
parts << "-tout #{tout}"
|
191
|
+
end
|
192
|
+
|
193
|
+
##-- daemoization and pid
|
194
|
+
parts << "-dmn" if cascading_config( 'daemonize' )
|
195
|
+
parts << "-pid #{pid_file}"
|
196
|
+
|
197
|
+
|
198
|
+
##-- logging
|
199
|
+
parts << "-log #{log_file}"
|
200
|
+
if log_level = cascading_config( 'log_level' ) then
|
201
|
+
if log_level == "error" then
|
202
|
+
parts << "-le"
|
203
|
+
elsif log_level == "debug" then
|
204
|
+
parts << "-ld"
|
205
|
+
elsif log_level == "info" then
|
206
|
+
# leave it at info
|
207
|
+
else
|
208
|
+
raise Error, "Invalid log level setting [#{log_level}]"
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
##-- update logs
|
213
|
+
parts << "-ulog #{ulog_dir}"
|
214
|
+
if ulim = cascading_config( 'update_log_size' )then
|
215
|
+
parts << "-ulim #{ulim}"
|
216
|
+
end
|
217
|
+
parts << "-uas" if cascading_config( 'update_log_async' )
|
218
|
+
|
219
|
+
##-- replication items, server id, master, replication timestamp file
|
220
|
+
parts << "-sid #{configuration.server_id}" if configuration.server_id
|
221
|
+
parts << "-mhost #{configuration.master_server}" if configuration.master_server
|
222
|
+
parts << "-mport #{configuration.master_port}" if configuration.master_port
|
223
|
+
parts << "-rts #{replication_timestamp_file}" if configuration.replication_timestamp_file
|
224
|
+
|
225
|
+
##-- lua extension
|
226
|
+
if configuration.lua_extension_file then
|
227
|
+
if File.exist?( lua_extension_file ) then
|
228
|
+
parts << "-ext #{lua_extension_file}"
|
229
|
+
if pc = configuration.periodic_command then
|
230
|
+
if pc.name and pc.period then
|
231
|
+
parts << "-extpc #{pc.name} #{pc.period}"
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
##-- command permissiosn
|
238
|
+
if deny = cascading_config( "deny_commands" ) then
|
239
|
+
parts << "-mask #{deny.join(",")}"
|
240
|
+
end
|
241
|
+
|
242
|
+
if allow = cascading_config( "allow_commands" ) then
|
243
|
+
parts << "-unmask #{allow.join(",")}"
|
244
|
+
end
|
245
|
+
|
246
|
+
##-- now for the filename. The format is
|
247
|
+
# filename.ext#opts=ld#mode=wc#tuning_param=value#tuning_param=value...
|
248
|
+
#
|
249
|
+
file_pairs = []
|
250
|
+
file_pairs << "opts=#{configuration.opts}"
|
251
|
+
file_pairs << "mode=#{configuration.mode}"
|
252
|
+
Loquacious::Configuration::Iterator.new( configuration.tuning_params ).each do |node|
|
253
|
+
file_pairs << "#{node.name}=#{node.obj}" if node.obj
|
254
|
+
end
|
255
|
+
|
256
|
+
file_name_and_params = "#{db_file}##{file_pairs.join("#")}"
|
257
|
+
|
258
|
+
parts << file_name_and_params
|
259
|
+
|
260
|
+
return parts.join( " " )
|
261
|
+
end
|
262
|
+
|
263
|
+
#
|
264
|
+
# Start the tyrant
|
265
|
+
#
|
266
|
+
def start
|
267
|
+
o = %x[ #{start_command} ]
|
268
|
+
logger.info o
|
269
|
+
end
|
270
|
+
|
271
|
+
#
|
272
|
+
# kill the proc
|
273
|
+
#
|
274
|
+
def stop
|
275
|
+
begin
|
276
|
+
_pid = self.pid
|
277
|
+
Process.kill( "TERM" , _pid )
|
278
|
+
logger.info "Sent signal TERM to #{_pid}"
|
279
|
+
rescue Errno::EPERM
|
280
|
+
logger.info "Process #{_pid} is beyond my control"
|
281
|
+
rescue Errno::ESRCH
|
282
|
+
logger.info "Process #{_pid} is dead"
|
283
|
+
rescue => e
|
284
|
+
logger.error "Problem sending kill(TERM, #{_pid}) : #{e}"
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
|
289
|
+
#
|
290
|
+
# check if process is alive
|
291
|
+
#
|
292
|
+
def running?
|
293
|
+
begin
|
294
|
+
if File.exist?( self.pid_file ) then
|
295
|
+
_pid = self.pid
|
296
|
+
Process.kill( 0, _pid )
|
297
|
+
return true
|
298
|
+
else
|
299
|
+
return false
|
300
|
+
end
|
301
|
+
rescue Errno::EPERM
|
302
|
+
logger.info "Process #{_pid} is beyond my control"
|
303
|
+
rescue Errno::ESRCH
|
304
|
+
logger.info "Process #{_pid} is dead"
|
305
|
+
return false
|
306
|
+
rescue => e
|
307
|
+
logger.error "Problem sending kill(0, #{_pid}) : #{e}"
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
#
|
312
|
+
# return a network connection to this instance
|
313
|
+
#
|
314
|
+
def connection
|
315
|
+
Rufus::Tokyo::Tyrant.new( configuration.host, configuration.port.to_i )
|
316
|
+
end
|
317
|
+
|
318
|
+
|
319
|
+
private
|
320
|
+
|
321
|
+
#
|
322
|
+
# take the given path, and if it is not an absolute path append it
|
323
|
+
# to the home directory of the instance.
|
324
|
+
def append_to_home_if_not_absolute( p )
|
325
|
+
path = Pathname.new( p )
|
326
|
+
unless path.absolute? then
|
327
|
+
path = Pathname.new( home_dir ) + path
|
328
|
+
end
|
329
|
+
return path.to_s
|
330
|
+
end
|
331
|
+
|
332
|
+
#
|
333
|
+
# retrieve the cascading option from our config or the managers config if we
|
334
|
+
# don't have it.
|
335
|
+
#
|
336
|
+
def cascading_config( name )
|
337
|
+
configuration[name] || manager.configuration.instance_defaults[name]
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009 Jeremy Hinegardner
|
3
|
+
# All rights reserved. See LICENSE and/or COPYING for details
|
4
|
+
#++
|
5
|
+
|
6
|
+
class TyrantManager
|
7
|
+
module Version
|
8
|
+
MAJOR = 1
|
9
|
+
MINOR = 0
|
10
|
+
BUILD = 9
|
11
|
+
|
12
|
+
def to_a
|
13
|
+
[MAJOR, MINOR, BUILD]
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
to_a.join(".")
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_hash
|
21
|
+
{ :major => MAJOR, :minor => MINOR, :build => BUILD }
|
22
|
+
end
|
23
|
+
|
24
|
+
extend self
|
25
|
+
end
|
26
|
+
VERSION = Version.to_s
|
27
|
+
end
|
@@ -0,0 +1,237 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2009 Jeremy Hinegardner
|
3
|
+
# All rights reserved. See LICENSE and/or COPYING for details.
|
4
|
+
#++
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'loquacious'
|
8
|
+
require 'tyrant_manager/version'
|
9
|
+
require 'tyrant_manager/paths'
|
10
|
+
require 'tyrant_manager/log'
|
11
|
+
|
12
|
+
class TyrantManager
|
13
|
+
include TyrantManager::Paths
|
14
|
+
|
15
|
+
class Error < StandardError; end
|
16
|
+
|
17
|
+
class << TyrantManager
|
18
|
+
#
|
19
|
+
# The basename of the default config file
|
20
|
+
#
|
21
|
+
def config_file_basename
|
22
|
+
"config.rb"
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# The basename of the directory that holds the tyrant manager system
|
27
|
+
#
|
28
|
+
def basedir
|
29
|
+
"tyrant"
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# is the given directory a tyrant root directory. A tyrant root has a
|
34
|
+
# +config_file_basename+ file in the top level
|
35
|
+
#
|
36
|
+
def is_tyrant_root?( dir )
|
37
|
+
cfg = File.join( dir, config_file_basename )
|
38
|
+
return true if File.directory?( dir ) and File.exist?( cfg )
|
39
|
+
return false
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Return the path of the tyrant dir if there is one relative to the current
|
44
|
+
# working directory. This means that there is a +config_file_basename+ in
|
45
|
+
# the current working directory.
|
46
|
+
#
|
47
|
+
# returns Dir.pwd if this is the case, nil otherwise
|
48
|
+
#
|
49
|
+
def cwd_default_directory
|
50
|
+
default_dir = Dir.pwd
|
51
|
+
return default_dir if is_tyrant_root?( default_dir )
|
52
|
+
return nil
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Return the path of the tyrant dir as it pertains to the
|
57
|
+
# TYRANT_MANAGER_HOME environment variable. If the directory is
|
58
|
+
# a tyrant root, then return that directory, otherwise return nil
|
59
|
+
#
|
60
|
+
def env_default_directory
|
61
|
+
default_dir = ENV['TYRANT_MANAGER_HOME']
|
62
|
+
return default_dir if default_dir and is_tyrant_root?( default_dir )
|
63
|
+
return nil
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Return the path of the tyrant dir as it pertains to the default global
|
68
|
+
# setting of 'lcoalstatedir' which is /opt/local/var, /var, or similar
|
69
|
+
#
|
70
|
+
def localstate_default_directory
|
71
|
+
default_dir = File.join( Config::CONFIG['localstatedir'], basedir )
|
72
|
+
return default_dir if is_tyrant_root?( default_dir )
|
73
|
+
return nil
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# The default tyrant directory. It is the first of these that matches:
|
78
|
+
#
|
79
|
+
# * current directory if there is a +config_file_basename+ file in the
|
80
|
+
# current dirctory
|
81
|
+
# * the value of the TYRANT_MANAGER_HOME environment variable
|
82
|
+
# * File.join( Config::CONFIG['localstatedir'], basedir )
|
83
|
+
#
|
84
|
+
def default_directory
|
85
|
+
defaults = [ self.cwd_default_directory,
|
86
|
+
self.env_default_directory,
|
87
|
+
self.localstate_default_directory ]
|
88
|
+
dd = nil
|
89
|
+
loop do
|
90
|
+
dd = defaults.shift
|
91
|
+
break if dd or defaults.empty?
|
92
|
+
end
|
93
|
+
raise Error, "No default_directory found" unless dd
|
94
|
+
return dd
|
95
|
+
end
|
96
|
+
|
97
|
+
#
|
98
|
+
# Return the default directory if it exists, otherwise fallback to .home_dir
|
99
|
+
#
|
100
|
+
def default_or_home_directory
|
101
|
+
hd = TyrantManager.home_dir
|
102
|
+
begin
|
103
|
+
hd = TyrantManager.default_directory
|
104
|
+
rescue => e
|
105
|
+
# yup, using home
|
106
|
+
end
|
107
|
+
return hd
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
#
|
112
|
+
# Setup the tyrant manager in the given directory. This means creating it
|
113
|
+
# if it does not exist.
|
114
|
+
#
|
115
|
+
def setup( dir = default_directory )
|
116
|
+
unless File.directory?( dir )
|
117
|
+
logger.info "Creating directory #{dir}"
|
118
|
+
FileUtils.mkdir_p( dir )
|
119
|
+
end
|
120
|
+
|
121
|
+
cfg = File.join( dir, config_file_basename )
|
122
|
+
|
123
|
+
unless File.exist?( cfg )
|
124
|
+
template = TyrantManager::Paths.data_path( config_file_basename )
|
125
|
+
logger.info "Creating default config file #{cfg}"
|
126
|
+
FileUtils.cp( template, dir )
|
127
|
+
end
|
128
|
+
|
129
|
+
%w[ instances log tmp ].each do |subdir|
|
130
|
+
subdir = File.join( dir, subdir )
|
131
|
+
unless File.directory?( subdir ) then
|
132
|
+
logger.info "Creating directory #{subdir}"
|
133
|
+
FileUtils.mkdir subdir
|
134
|
+
end
|
135
|
+
end
|
136
|
+
return TyrantManager.new( dir )
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
#
|
141
|
+
# Initialize the manager, which is nothing more than creating the instance and
|
142
|
+
# setting the home directory.
|
143
|
+
#
|
144
|
+
def initialize( directory = TyrantManager.default_directory )
|
145
|
+
self.home_dir = File.expand_path( directory )
|
146
|
+
if File.exist?( self.config_file ) then
|
147
|
+
configuration # force a load
|
148
|
+
else
|
149
|
+
raise Error, "#{home_dir} is not a valid archive. #{self.config_file} does not exist"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def logger
|
154
|
+
Logging::Logger[self]
|
155
|
+
end
|
156
|
+
|
157
|
+
#
|
158
|
+
# The configuration file for the manager
|
159
|
+
#
|
160
|
+
def config_file
|
161
|
+
@config_file ||= File.join( home_dir, TyrantManager.config_file_basename )
|
162
|
+
end
|
163
|
+
|
164
|
+
#
|
165
|
+
# load the configuration
|
166
|
+
#
|
167
|
+
def configuration
|
168
|
+
unless @configuration
|
169
|
+
eval( IO.read( self.config_file ) )
|
170
|
+
@configuration = Loquacious::Configuration.for("manager")
|
171
|
+
end
|
172
|
+
return @configuration
|
173
|
+
end
|
174
|
+
|
175
|
+
#
|
176
|
+
# Create a runner instance with the given options
|
177
|
+
#
|
178
|
+
def runner_for( options )
|
179
|
+
Runner.new( self, options )
|
180
|
+
end
|
181
|
+
|
182
|
+
#
|
183
|
+
# Return the list of instances that the manager knows about
|
184
|
+
#
|
185
|
+
def instances
|
186
|
+
unless @instances then
|
187
|
+
candidates = [ self.instances_path ]
|
188
|
+
if configuration.instances then
|
189
|
+
candidates = configuration.instances
|
190
|
+
end
|
191
|
+
|
192
|
+
@instances = {}
|
193
|
+
while not candidates.empty? do
|
194
|
+
candidate = candidates.pop
|
195
|
+
cpath = append_to_home_if_not_absolute( candidate )
|
196
|
+
begin
|
197
|
+
t = TyrantInstance.new( cpath )
|
198
|
+
t.manager = self
|
199
|
+
@instances[t.name] = t
|
200
|
+
rescue TyrantManager::Error => e
|
201
|
+
if File.directory?( cpath ) then
|
202
|
+
Dir.glob( "#{cpath}/*" ).each do |epath|
|
203
|
+
if File.directory?( epath ) then
|
204
|
+
candidates.push epath
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end #while
|
210
|
+
end
|
211
|
+
return @instances
|
212
|
+
end
|
213
|
+
|
214
|
+
def each_instance
|
215
|
+
instances.keys.sort.each do |name|
|
216
|
+
yield instances[name]
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
private
|
221
|
+
#
|
222
|
+
# take the given path, and if it is not an absolute path append it
|
223
|
+
# to the home directory of the instance.
|
224
|
+
def append_to_home_if_not_absolute( p )
|
225
|
+
path = Pathname.new( p )
|
226
|
+
unless path.absolute? then
|
227
|
+
path = Pathname.new( home_dir ) + path
|
228
|
+
end
|
229
|
+
return path.to_s
|
230
|
+
end
|
231
|
+
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
require 'tyrant_manager/cli'
|
236
|
+
require 'tyrant_manager/runner'
|
237
|
+
require 'tyrant_manager/tyrant_instance'
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.expand_path( File.join( File.dirname( __FILE__ ),"spec_helper.rb"))
|
2
|
+
|
3
|
+
require 'tyrant_manager/command'
|
4
|
+
|
5
|
+
class Junk < TyrantManager::Command; end
|
6
|
+
|
7
|
+
describe TyrantManager::Command do
|
8
|
+
before( :each ) do
|
9
|
+
@cmd = TyrantManager::Command.new( nil )
|
10
|
+
end
|
11
|
+
|
12
|
+
it "has a command name" do
|
13
|
+
@cmd.command_name.should == "command"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "can log" do
|
17
|
+
@cmd.logger.info "this is a log statement"
|
18
|
+
spec_log.should =~ /this is a log statement/
|
19
|
+
end
|
20
|
+
|
21
|
+
it "raises an error if it cannot find the class required" do
|
22
|
+
lambda { TyrantManager::Command.find( "foo" ) }.should raise_error( TyrantManager::Command::CommandNotFoundError,
|
23
|
+
/No command for 'foo' was found./ )
|
24
|
+
end
|
25
|
+
|
26
|
+
it "registers inherited classes" do
|
27
|
+
TyrantManager::Command.list.should be_include( Junk )
|
28
|
+
TyrantManager::Command.list.delete( Junk )
|
29
|
+
TyrantManager::Command.list.should_not be_include(Junk)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "classes cannot be run without implementing 'run'" do
|
33
|
+
j = Junk.new( nil )
|
34
|
+
j.respond_to?(:run).should == true
|
35
|
+
lambda { j.run }.should raise_error( NotImplementedError, /The #run method must be implemented/)
|
36
|
+
end
|
37
|
+
end
|
data/spec/paths_spec.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__),"spec_helper.rb"))
|
2
|
+
|
3
|
+
require 'tyrant_manager/paths'
|
4
|
+
|
5
|
+
describe TyrantManager::Paths do
|
6
|
+
before(:each) do
|
7
|
+
@install_dir = File.expand_path(File.join(File.dirname(__FILE__), ".."))
|
8
|
+
@install_dir += "/"
|
9
|
+
end
|
10
|
+
|
11
|
+
it "root dir should be correct" do
|
12
|
+
TyrantManager::Paths.install_dir.should == @install_dir
|
13
|
+
end
|
14
|
+
|
15
|
+
it "home dir should be correct" do
|
16
|
+
TyrantManager::Paths.home_dir.should == @install_dir
|
17
|
+
end
|
18
|
+
|
19
|
+
%w[ bin lib spec data log tmp ].each do |d|
|
20
|
+
it "#{d} path should be correct" do
|
21
|
+
TyrantManager::Paths.send( "#{d}_path" ).should == File.join(@install_dir, "#{d}/" )
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "setting home_dir" do
|
26
|
+
before( :each ) do
|
27
|
+
@tmp_dir = "/some/location/#{Process.pid}"
|
28
|
+
TyrantManager::Paths.home_dir = @tmp_dir
|
29
|
+
end
|
30
|
+
|
31
|
+
%w[ log tmp instances ].each do |d|
|
32
|
+
check_path = "#{d}_path"
|
33
|
+
it "affects the location of #{check_path}" do
|
34
|
+
p = TyrantManager::Paths.send( check_path )
|
35
|
+
p.should == ( File.join( @tmp_dir, d ) + File::SEPARATOR )
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
%w[ bin lib spec data ].each do |d|
|
40
|
+
check_path = "#{d}_path"
|
41
|
+
it "does not affect the location of #{check_path}" do
|
42
|
+
p = TyrantManager::Paths.send( check_path )
|
43
|
+
p.should == ( File.join( @install_dir, d ) + File::SEPARATOR )
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "Default TyrantManager home_dir" do
|
49
|
+
it 'has a different default home_dir for the top level TyrantManager' do
|
50
|
+
TyrantManager.home_dir.should == File.join( Config::CONFIG['localstatedir'], 'lib', 'tyrant' )
|
51
|
+
end
|
52
|
+
|
53
|
+
it "has an instances dir" do
|
54
|
+
TyrantManager.default_instances_dir.should == File.join( Config::CONFIG['localstatedir'], 'lib', 'tyrant', 'instances' ) + "/"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|