jackowayed-tyrantmanager 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY.rdoc +34 -0
- data/LICENSE +13 -0
- data/README.rdoc +277 -0
- data/bin/tyrantmanager +10 -0
- data/data/config.rb +94 -0
- data/data/default_instance_config.rb +244 -0
- data/gemspec.rb +53 -0
- data/lib/tyrant_manager.rb +237 -0
- data/lib/tyrant_manager/cli.rb +152 -0
- data/lib/tyrant_manager/command.rb +119 -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/replication_status.rb +92 -0
- data/lib/tyrant_manager/commands/start.rb +32 -0
- data/lib/tyrant_manager/commands/stats.rb +24 -0
- data/lib/tyrant_manager/commands/status.rb +26 -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 +380 -0
- data/lib/tyrant_manager/version.rb +27 -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 +106 -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 +172 -0
data/gemspec.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'tyrant_manager/version'
|
3
|
+
require 'tasks/config'
|
4
|
+
|
5
|
+
TyrantManager::GEM_SPEC = Gem::Specification.new do |spec|
|
6
|
+
proj = Configuration.for('project')
|
7
|
+
spec.name = proj.name
|
8
|
+
spec.version = TyrantManager::VERSION
|
9
|
+
|
10
|
+
spec.author = proj.author
|
11
|
+
spec.email = proj.email
|
12
|
+
spec.homepage = proj.homepage
|
13
|
+
spec.summary = proj.summary
|
14
|
+
spec.description = proj.description
|
15
|
+
spec.platform = Gem::Platform::RUBY
|
16
|
+
|
17
|
+
|
18
|
+
pkg = Configuration.for('packaging')
|
19
|
+
spec.files = pkg.files.all
|
20
|
+
spec.executables = pkg.files.bin.collect { |b| File.basename(b) }
|
21
|
+
|
22
|
+
# add dependencies here
|
23
|
+
spec.add_dependency( "loquacious", "~> 1.3.0")
|
24
|
+
spec.add_dependency( "rufus-tokyo", "~> 1.0.0")
|
25
|
+
spec.add_dependency( "logging", "~> 1.1.4" )
|
26
|
+
spec.add_dependency( "main", "~> 2.8.4" )
|
27
|
+
|
28
|
+
# development dependencies
|
29
|
+
spec.add_development_dependency("configuration", ">= 0.0.5")
|
30
|
+
spec.add_development_dependency( "rake", "~> 0.8.3")
|
31
|
+
|
32
|
+
if ext_conf = Configuration.for_if_exist?("extension") then
|
33
|
+
spec.extensions << ext_conf.configs
|
34
|
+
spec.extensions.flatten!
|
35
|
+
end
|
36
|
+
|
37
|
+
if rdoc = Configuration.for_if_exist?('rdoc') then
|
38
|
+
spec.has_rdoc = true
|
39
|
+
spec.extra_rdoc_files = pkg.files.rdoc
|
40
|
+
spec.rdoc_options = rdoc.options + [ "--main" , rdoc.main_page ]
|
41
|
+
else
|
42
|
+
spec.has_rdoc = false
|
43
|
+
end
|
44
|
+
|
45
|
+
if test = Configuration.for_if_exist?('testing') then
|
46
|
+
spec.test_files = test.files
|
47
|
+
end
|
48
|
+
|
49
|
+
if rf = Configuration.for_if_exist?('rubyforge') then
|
50
|
+
spec.rubyforge_project = rf.project
|
51
|
+
end
|
52
|
+
|
53
|
+
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,152 @@
|
|
1
|
+
require 'main'
|
2
|
+
require 'tyrant_manager'
|
3
|
+
|
4
|
+
class TyrantManager
|
5
|
+
Cli = Main.create {
|
6
|
+
author "Copyright 2009 (c) Jeremy Hinegardner"
|
7
|
+
version ::TyrantManager::VERSION
|
8
|
+
|
9
|
+
description <<-txt
|
10
|
+
The command line tool for managing tyrant instances.
|
11
|
+
|
12
|
+
Run 'tyrantmanager help modename' for more information
|
13
|
+
txt
|
14
|
+
|
15
|
+
run { help! }
|
16
|
+
|
17
|
+
mode( :setup ) {
|
18
|
+
description "Setup an tyrant manager location"
|
19
|
+
argument( :home ) {
|
20
|
+
description "The home directory of the tyrant manager"
|
21
|
+
required
|
22
|
+
default TyrantManager.default_or_home_directory
|
23
|
+
}
|
24
|
+
|
25
|
+
run {
|
26
|
+
TyrantManager::Log.init
|
27
|
+
TyrantManager.setup( params['home'].value )
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
mode( 'create-instance' ) {
|
32
|
+
description <<-txt
|
33
|
+
Create a new tyrant instance in the specified directory
|
34
|
+
txt
|
35
|
+
|
36
|
+
argument( 'instance-home' ) do
|
37
|
+
description <<-txt
|
38
|
+
The home directory of the tyrant instance. If this is a full path it
|
39
|
+
will be used. If it is a relative path, it will be relative to the
|
40
|
+
manager's 'instances' configuration parameter
|
41
|
+
txt
|
42
|
+
end
|
43
|
+
|
44
|
+
mixin :option_home
|
45
|
+
mixin :option_log_level
|
46
|
+
|
47
|
+
run { Cli.run_command_with_params( "create-instance", params ) }
|
48
|
+
}
|
49
|
+
|
50
|
+
mode( 'start' ) {
|
51
|
+
description "Start all the tyrants listed"
|
52
|
+
mixin :option_home
|
53
|
+
mixin :option_log_level
|
54
|
+
mixin :argument_instances
|
55
|
+
option( 'dry-run' ) {
|
56
|
+
description "Do not start, just show the commands"
|
57
|
+
default false
|
58
|
+
}
|
59
|
+
|
60
|
+
run { Cli.run_command_with_params( 'start', params ) }
|
61
|
+
}
|
62
|
+
|
63
|
+
|
64
|
+
mode( 'stop' ) {
|
65
|
+
description "Stop all the tyrants listed"
|
66
|
+
mixin :option_home
|
67
|
+
mixin :option_log_level
|
68
|
+
mixin :argument_instances
|
69
|
+
|
70
|
+
run { Cli.run_command_with_params( 'stop', params ) }
|
71
|
+
}
|
72
|
+
|
73
|
+
mode('replication-status') {
|
74
|
+
description "Describe the replication status of those servers using replication"
|
75
|
+
mixin :option_home
|
76
|
+
mixin :option_log_level
|
77
|
+
mixin :argument_instances
|
78
|
+
run { Cli.run_command_with_params( 'replication-status', params ) }
|
79
|
+
}
|
80
|
+
|
81
|
+
mode('process-status') {
|
82
|
+
description "Check the running status of all the tyrants listed"
|
83
|
+
mixin :option_home
|
84
|
+
mixin :option_log_level
|
85
|
+
|
86
|
+
mixin :argument_instances
|
87
|
+
|
88
|
+
run { Cli.run_command_with_params( 'process-status', params ) }
|
89
|
+
}
|
90
|
+
|
91
|
+
mode( 'stats' ) {
|
92
|
+
description "Dump the database statistics of each of the tyrants listed"
|
93
|
+
mixin :option_home
|
94
|
+
mixin :option_log_level
|
95
|
+
|
96
|
+
mixin :argument_instances
|
97
|
+
|
98
|
+
run { Cli.run_command_with_params( 'stats', params ) }
|
99
|
+
}
|
100
|
+
|
101
|
+
|
102
|
+
mode('list') {
|
103
|
+
description "list the instances and their home directories"
|
104
|
+
mixin :option_home
|
105
|
+
mixin :option_log_level
|
106
|
+
mixin :argument_instances
|
107
|
+
run { Cli.run_command_with_params( 'list', params ) }
|
108
|
+
}
|
109
|
+
|
110
|
+
#--- Mixins ---
|
111
|
+
mixin :option_home do
|
112
|
+
option( :home ) do
|
113
|
+
description "The home directory of the tyrant manager"
|
114
|
+
argument :required
|
115
|
+
validate { |v| ::File.directory?( v ) }
|
116
|
+
default TyrantManager.default_or_home_directory
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
mixin :option_log_level do
|
121
|
+
option('log-level') do
|
122
|
+
description "The verbosity of logging, one of [ #{::Logging::LNAMES.map {|l| l.downcase}.join(", ")} ]"
|
123
|
+
argument :required
|
124
|
+
validate { |l| %w[ debug info warn error fatal off ].include?( l.downcase ) }
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
mixin :argument_instances do
|
129
|
+
argument('instances') do
|
130
|
+
description "A comman separated list of instance names the tyrant manager knows about"
|
131
|
+
argument :required
|
132
|
+
cast :list
|
133
|
+
default 'all'
|
134
|
+
end
|
135
|
+
end
|
136
|
+
}
|
137
|
+
|
138
|
+
#
|
139
|
+
# Convert the Parameters::List that exists as the parameters from Main
|
140
|
+
#
|
141
|
+
def Cli.params_to_hash( params )
|
142
|
+
(hash = params.to_hash ).keys.each { |key| hash[key] = hash[key].value }
|
143
|
+
return hash
|
144
|
+
end
|
145
|
+
|
146
|
+
def Cli.run_command_with_params( command, params )
|
147
|
+
phash = Cli.params_to_hash( params )
|
148
|
+
TyrantManager::Log.init( phash )
|
149
|
+
tm = TyrantManager.new( phash.delete('home') )
|
150
|
+
tm.runner_for( phash ).run( command )
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'tyrant_manager'
|
2
|
+
class TyrantManager
|
3
|
+
#
|
4
|
+
# The Command is the base class for any class to be used as a command
|
5
|
+
#
|
6
|
+
# All commands run within the context of an existing TyrantManager location.
|
7
|
+
# Before the command starts the current working directory will be inside the
|
8
|
+
# tyrant manager's home directory.
|
9
|
+
#
|
10
|
+
# A command has a lifecyle of:
|
11
|
+
#
|
12
|
+
# * instantiation with a TyrantManager instance and an options hash
|
13
|
+
# * one call to before
|
14
|
+
# * one call to run
|
15
|
+
# * one call to after
|
16
|
+
#
|
17
|
+
# In case of an exception raised during the lifecycle, the +error+ method will
|
18
|
+
# be called. The +after+ method is called no matter what at the end of the
|
19
|
+
# lifecycle, even if an error has occurred.
|
20
|
+
#
|
21
|
+
class Command
|
22
|
+
def self.command_name
|
23
|
+
name.split("::").last.downcase
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_reader :options
|
27
|
+
attr_reader :manager
|
28
|
+
|
29
|
+
#
|
30
|
+
# Instantiated and given the tyrant manager instance it is to operate
|
31
|
+
# through and a hash of options
|
32
|
+
#
|
33
|
+
def initialize( manager, opts = {} )
|
34
|
+
@manager = manager
|
35
|
+
@options = opts
|
36
|
+
end
|
37
|
+
|
38
|
+
def command_name
|
39
|
+
self.class.command_name
|
40
|
+
end
|
41
|
+
|
42
|
+
def logger
|
43
|
+
Logging::Logger[self]
|
44
|
+
end
|
45
|
+
|
46
|
+
#------------------------------------------------------------------
|
47
|
+
# lifecycle calls
|
48
|
+
#------------------------------------------------------------------
|
49
|
+
|
50
|
+
#
|
51
|
+
# call-seq:
|
52
|
+
# cmd.before
|
53
|
+
#
|
54
|
+
# called to allow the command to setup anything post initialization it
|
55
|
+
# needs to be able to +run+.
|
56
|
+
#
|
57
|
+
def before() nil ; end
|
58
|
+
|
59
|
+
#
|
60
|
+
# call-seq:
|
61
|
+
# cmd.run
|
62
|
+
#
|
63
|
+
# Yeah, this is where the work should be done.
|
64
|
+
#
|
65
|
+
def run()
|
66
|
+
raise NotImplementedError, "The #run method must be implemented"
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# call-seq:
|
71
|
+
# cmd.after
|
72
|
+
#
|
73
|
+
# called no matter what, after the execution of run() or error() if there was
|
74
|
+
# an exception. It is here to allow the cmd to do cleanup work.
|
75
|
+
#
|
76
|
+
def after() nil ; end
|
77
|
+
|
78
|
+
#
|
79
|
+
# call-seq:
|
80
|
+
# cmd.error( exception )
|
81
|
+
#
|
82
|
+
# called if there is an exception during the before() or after() calls. It
|
83
|
+
# is passed in the exception that was raised.
|
84
|
+
#
|
85
|
+
def error(exception) nil ; end
|
86
|
+
|
87
|
+
#------------------------------------------------------------------
|
88
|
+
# registration
|
89
|
+
#------------------------------------------------------------------
|
90
|
+
class CommandNotFoundError < ::TyrantManager::Error ; end
|
91
|
+
class << Command
|
92
|
+
def inherited( klass )
|
93
|
+
return unless klass.instance_of? Class
|
94
|
+
self.list << klass
|
95
|
+
end
|
96
|
+
|
97
|
+
def list
|
98
|
+
unless defined? @list
|
99
|
+
@list = Set.new
|
100
|
+
end
|
101
|
+
return @list
|
102
|
+
end
|
103
|
+
|
104
|
+
def find( command )
|
105
|
+
klass = list.find { |klass| klass.command_name == command }
|
106
|
+
return klass if klass
|
107
|
+
names = list.collect { |c| c.command_name }
|
108
|
+
raise CommandNotFoundError, "No command for '#{command}' was found. Known commands : #{names.join(",")}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
require 'tyrant_manager/commands/create_instance'
|
114
|
+
require 'tyrant_manager/commands/start'
|
115
|
+
require 'tyrant_manager/commands/stop'
|
116
|
+
require 'tyrant_manager/commands/status'
|
117
|
+
require 'tyrant_manager/commands/stats'
|
118
|
+
require 'tyrant_manager/commands/list'
|
119
|
+
require 'tyrant_manager/commands/replication_status'
|