golden_brindle 0.2 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +1 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +38 -0
- data/Rakefile +33 -46
- data/bin/golden_brindle +1 -4
- data/golden_brindle.gemspec +33 -15
- data/lib/golden_brindle/actions/cluster.rb +50 -0
- data/lib/golden_brindle/actions/configure.rb +63 -0
- data/lib/golden_brindle/actions/restart.rb +40 -0
- data/lib/golden_brindle/actions/start.rb +131 -0
- data/lib/golden_brindle/actions/stop.rb +45 -0
- data/lib/golden_brindle/base.rb +89 -0
- data/lib/golden_brindle/command.rb +39 -174
- data/lib/golden_brindle/const.rb +5 -5
- data/lib/golden_brindle/hooks.rb +2 -1
- data/lib/golden_brindle/rails_support.rb +2 -2
- data/lib/golden_brindle/validations.rb +56 -0
- data/lib/golden_brindle.rb +6 -7
- data/spec/golden_brindle_spec.rb +23 -0
- data/spec/spec_helper.rb +12 -0
- metadata +70 -18
- data/lib/golden_brindle/cluster.rb +0 -53
- data/lib/golden_brindle/configure.rb +0 -63
- data/lib/golden_brindle/restart.rb +0 -40
- data/lib/golden_brindle/start.rb +0 -130
- data/lib/golden_brindle/stop.rb +0 -45
- data/test/helper.rb +0 -10
- data/test/test_golden_brindle.rb +0 -7
@@ -0,0 +1,45 @@
|
|
1
|
+
module GoldenBrindle
|
2
|
+
module Actions
|
3
|
+
|
4
|
+
class Stop < ::GoldenBrindle::Base
|
5
|
+
|
6
|
+
def configure
|
7
|
+
options [
|
8
|
+
['-c', '--chdir PATH', "Change to dir before starting (will be expanded).", :@cwd, "."],
|
9
|
+
['-C', '--config PATH', "Use a mongrel based config file", :@config_file, nil],
|
10
|
+
['-f', '--force', "Force the shutdown (kill -9).", :@force, false],
|
11
|
+
['-w', '--wait SECONDS', "Wait SECONDS before forcing shutdown", :@wait, "0"],
|
12
|
+
['-P', '--pid FILE', "Where the PID file is located.", :@pid_file, "tmp/pids/unicorn.pid"]
|
13
|
+
]
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate
|
17
|
+
if @config_file
|
18
|
+
valid_exists?(@config_file, "Config file not there: #@config_file")
|
19
|
+
@config_file = File.expand_path(@config_file)
|
20
|
+
load_config
|
21
|
+
return @valid
|
22
|
+
end
|
23
|
+
|
24
|
+
@cwd = File.expand_path(@cwd)
|
25
|
+
valid_dir? @cwd, "Invalid path to change to during daemon mode: #@cwd"
|
26
|
+
valid_exists? File.join(@cwd,@pid_file), "PID file #@pid_file does not exist. Not running?"
|
27
|
+
return @valid
|
28
|
+
end
|
29
|
+
|
30
|
+
def run
|
31
|
+
@pid_file = File.join(@cwd,@pid_file)
|
32
|
+
if @force
|
33
|
+
@wait.to_i.times do |waiting|
|
34
|
+
exit(0) if not File.exist? @pid_file
|
35
|
+
sleep 1
|
36
|
+
end
|
37
|
+
GoldenBrindle::send_signal("KILL", @pid_file) if File.exist? @pid_file
|
38
|
+
else
|
39
|
+
GoldenBrindle::send_signal("TERM", @pid_file)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module GoldenBrindle
|
2
|
+
class Base
|
3
|
+
include Validations
|
4
|
+
attr_reader :valid, :done_validating, :original_args
|
5
|
+
|
6
|
+
# Called by the subclass to setup the command and parse the argv arguments.
|
7
|
+
# The call is destructive on argv since it uses the OptionParser#parse! function.
|
8
|
+
def initialize(argv)
|
9
|
+
@opt = ::OptionParser.new
|
10
|
+
@opt.banner = GoldenBrindle::Const::BANNER
|
11
|
+
@valid = true
|
12
|
+
# this is retarded, but it has to be done this way because -h and -v exit
|
13
|
+
@done_validating = false
|
14
|
+
@original_args = argv.dup
|
15
|
+
configure
|
16
|
+
# I need to add my own -h definition to prevent the -h by default from exiting.
|
17
|
+
@opt.on_tail("-h", "--help", "Show this message") do
|
18
|
+
@done_validating = true
|
19
|
+
puts @opt
|
20
|
+
end
|
21
|
+
# I need to add my own -v definition to prevent the -v from exiting by default as well.
|
22
|
+
@opt.on_tail("--version", "Show version") do
|
23
|
+
@done_validating = true
|
24
|
+
if VERSION
|
25
|
+
puts "Version #{GoldenBrindle::Const::VERSION}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
@opt.parse! argv
|
29
|
+
end
|
30
|
+
|
31
|
+
# Called by the implemented command to set the options for that command.
|
32
|
+
# Every option has a short and long version, a description, a variable to
|
33
|
+
# set, and a default value. No exceptions.
|
34
|
+
def options(opts)
|
35
|
+
# process the given options array
|
36
|
+
opts.each do |short, long, help, variable, default|
|
37
|
+
self.instance_variable_set(variable, default)
|
38
|
+
@opt.on(short, long, help) do |arg|
|
39
|
+
self.instance_variable_set(variable, arg)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def configure
|
45
|
+
options []
|
46
|
+
end
|
47
|
+
|
48
|
+
def config_keys
|
49
|
+
GoldenBrindle::Const::CONFIG_KEYS
|
50
|
+
end
|
51
|
+
|
52
|
+
def load_config
|
53
|
+
settings = {}
|
54
|
+
begin
|
55
|
+
settings = ::YAML.load_file(@config_file)
|
56
|
+
ensure
|
57
|
+
STDERR.puts "** Loading settings from #{@config_file} (they override command line)." unless @daemon || settings[:daemon]
|
58
|
+
end
|
59
|
+
|
60
|
+
# Config file settings will override command line settings
|
61
|
+
settings.each do |key, value|
|
62
|
+
key = key.to_s
|
63
|
+
if config_keys.include?(key)
|
64
|
+
key = 'address' if key == 'host'
|
65
|
+
self.instance_variable_set("@#{key}", value)
|
66
|
+
else
|
67
|
+
failure "Unknown configuration setting: #{key}"
|
68
|
+
@valid = false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns true/false depending on whether the command is configured properly.
|
74
|
+
def validate
|
75
|
+
@valid
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns a help message. Defaults to OptionParser#help which should be good.
|
79
|
+
def help
|
80
|
+
@opt.help
|
81
|
+
end
|
82
|
+
|
83
|
+
# Runs the command doing it's job. You should implement this otherwise it will
|
84
|
+
# throw a NotImplementedError as a reminder.
|
85
|
+
def run
|
86
|
+
raise NotImplementedError
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -1,194 +1,60 @@
|
|
1
1
|
module GoldenBrindle
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
2
|
+
|
3
|
+
class << self
|
4
|
+
def send_signal(signal, pid_file)
|
5
|
+
pid = open(pid_file).read.to_i
|
6
|
+
print "Sending #{signal} to Unicorn at PID #{pid}..."
|
7
|
+
begin
|
8
|
+
Process.kill(signal, pid)
|
9
|
+
rescue Errno::ESRCH
|
10
|
+
puts "Process does not exist. Not running."
|
11
|
+
end
|
12
|
+
puts "Done."
|
11
13
|
end
|
12
|
-
puts "Done."
|
13
14
|
end
|
14
|
-
|
15
|
-
module Command
|
16
|
-
|
17
|
-
module Base
|
18
|
-
|
19
|
-
attr_reader :valid, :done_validating, :original_args
|
20
15
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
def options(opts)
|
25
|
-
# process the given options array
|
26
|
-
opts.each do |short, long, help, variable, default|
|
27
|
-
self.instance_variable_set(variable, default)
|
28
|
-
@opt.on(short, long, help) do |arg|
|
29
|
-
self.instance_variable_set(variable, arg)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
16
|
+
# A Singleton class that manages all of the available commands
|
17
|
+
# and handles running them.
|
18
|
+
class Registry
|
33
19
|
|
34
|
-
|
35
|
-
# The call is destructive on argv since it uses the OptionParser#parse! function.
|
36
|
-
def initialize(options={})
|
37
|
-
argv = options[:argv] || []
|
38
|
-
@opt = OptionParser.new
|
39
|
-
@opt.banner = GoldenBrindle::Const::BANNER
|
40
|
-
@valid = true
|
41
|
-
# this is retarded, but it has to be done this way because -h and -v exit
|
42
|
-
@done_validating = false
|
43
|
-
@original_args = argv.dup
|
44
|
-
configure
|
45
|
-
# I need to add my own -h definition to prevent the -h by default from exiting.
|
46
|
-
@opt.on_tail("-h", "--help", "Show this message") do
|
47
|
-
@done_validating = true
|
48
|
-
puts @opt
|
49
|
-
end
|
20
|
+
class << self
|
50
21
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
22
|
+
def constantize(camel_cased_word)
|
23
|
+
names = camel_cased_word.split('::')
|
24
|
+
names.shift if names.empty? || names.first.empty?
|
25
|
+
constant = Object
|
26
|
+
names.each do |name|
|
27
|
+
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
57
28
|
end
|
58
|
-
|
29
|
+
constant
|
59
30
|
end
|
60
31
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
%w(address host port cwd log_file pid_file environment servers daemon debug config_script workers timeout user group prefix preload listen bundler)
|
68
|
-
end
|
69
|
-
|
70
|
-
def load_config
|
71
|
-
settings = {}
|
72
|
-
begin
|
73
|
-
settings = YAML.load_file(@config_file)
|
74
|
-
ensure
|
75
|
-
STDERR.puts "** Loading settings from #{@config_file} (they override command line)." unless @daemon || settings[:daemon]
|
76
|
-
end
|
77
|
-
|
78
|
-
# Config file settings will override command line settings
|
79
|
-
settings.each do |key, value|
|
80
|
-
key = key.to_s
|
81
|
-
if config_keys.include?(key)
|
82
|
-
key = 'address' if key == 'host'
|
83
|
-
self.instance_variable_set("@#{key}", value)
|
32
|
+
# Builds a list of possible commands from the Command derivates list
|
33
|
+
def commands
|
34
|
+
GoldenBrindle::Actions.constants.inject([]) do |memo, action|
|
35
|
+
constants = constantize("GoldenBrindle::Actions::#{action.to_s}").constants
|
36
|
+
if constants.empty?
|
37
|
+
memo << action.to_s.downcase
|
84
38
|
else
|
85
|
-
|
86
|
-
|
39
|
+
constants.each do |subaction|
|
40
|
+
memo << "#{action.to_s}::#{subaction.to_s}".downcase
|
41
|
+
end
|
87
42
|
end
|
43
|
+
memo
|
88
44
|
end
|
89
45
|
end
|
90
46
|
|
91
|
-
# Returns true/false depending on whether the command is configured properly.
|
92
|
-
def validate
|
93
|
-
@valid
|
94
|
-
end
|
95
|
-
|
96
|
-
# Returns a help message. Defaults to OptionParser#help which should be good.
|
97
|
-
def help
|
98
|
-
@opt.help
|
99
|
-
end
|
100
|
-
|
101
|
-
# Runs the command doing it's job. You should implement this otherwise it will
|
102
|
-
# throw a NotImplementedError as a reminder.
|
103
|
-
def run
|
104
|
-
raise NotImplementedError
|
105
|
-
end
|
106
|
-
|
107
|
-
# Validates the given expression is true and prints the message if not, exiting.
|
108
|
-
def valid?(exp, message)
|
109
|
-
if !@done_validating && !exp
|
110
|
-
failure message
|
111
|
-
@valid = false
|
112
|
-
@done_validating = true
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
# Validates that a file exists and if not displays the message
|
117
|
-
def valid_exists?(file, message)
|
118
|
-
valid?(File.exist?(file), message)
|
119
|
-
end
|
120
|
-
|
121
|
-
# Validates that the file is a file and not a directory or something else.
|
122
|
-
def valid_file?(file, message)
|
123
|
-
valid?(File.file?(file), message)
|
124
|
-
end
|
125
|
-
|
126
|
-
# Validates that the given directory exists
|
127
|
-
def valid_dir?(file, message)
|
128
|
-
valid?(File.directory?(file), message)
|
129
|
-
end
|
130
|
-
|
131
|
-
def can_change_user?
|
132
|
-
valid?(Process.euid.to_i == 0, "if you want to change workers UID/GID you must run script from root")
|
133
|
-
end
|
134
|
-
|
135
|
-
def valid_user?(user)
|
136
|
-
valid?(@group, "You must also specify a group.")
|
137
|
-
can_change_user?
|
138
|
-
begin
|
139
|
-
Etc.getpwnam(user)
|
140
|
-
rescue
|
141
|
-
failure "User does not exist: #{user}"
|
142
|
-
@valid = false
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
def valid_group?(group)
|
147
|
-
valid?(@user, "You must also specify a user.")
|
148
|
-
begin
|
149
|
-
Etc.getgrnam(group)
|
150
|
-
rescue
|
151
|
-
failure "Group does not exist: #{group}"
|
152
|
-
@valid = false
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
# Just a simple method to display failure until something better is developed.
|
157
|
-
def failure(message)
|
158
|
-
STDERR.puts "!!! #{message}"
|
159
|
-
end
|
160
|
-
|
161
|
-
end
|
162
|
-
|
163
|
-
# A Singleton class that manages all of the available commands
|
164
|
-
# and handles running them.
|
165
|
-
class Registry
|
166
|
-
include Singleton
|
167
|
-
|
168
|
-
# Builds a list of possible commands from the Command derivates list
|
169
|
-
def commands
|
170
|
-
pmgr = GemPlugin::Manager.instance
|
171
|
-
list = pmgr.plugins["/commands"].keys
|
172
|
-
list.sort
|
173
|
-
end
|
174
|
-
|
175
47
|
# Prints a list of available commands.
|
176
48
|
def print_command_list
|
177
49
|
puts "#{GoldenBrindle::Const::BANNER}\nAvailable commands are:\n\n"
|
178
|
-
|
179
|
-
|
180
|
-
if /brindle::/ =~ name
|
181
|
-
name = name[9 .. -1]
|
182
|
-
end
|
183
|
-
|
184
|
-
puts " - #{name[1 .. -1]}\n"
|
50
|
+
commands.each do |name|
|
51
|
+
puts " - #{name}\n"
|
185
52
|
end
|
186
53
|
|
187
54
|
puts "\nEach command takes -h as an option to get help."
|
188
55
|
|
189
56
|
end
|
190
57
|
|
191
|
-
|
192
58
|
# Runs the args against the first argument as the command name.
|
193
59
|
# If it has any errors it returns a false, otherwise it return true.
|
194
60
|
def run(args)
|
@@ -204,10 +70,9 @@ module GoldenBrindle
|
|
204
70
|
end
|
205
71
|
|
206
72
|
begin
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
command = GemPlugin::Manager.instance.create("/commands/#{cmd_name}", :argv => args)
|
73
|
+
cmd_name = cmd_name.split("::").map{|x| x.capitalize}.join("::")
|
74
|
+
constant = constantize("GoldenBrindle::Actions::#{cmd_name}")
|
75
|
+
command = constant.new(args)
|
211
76
|
rescue OptionParser::InvalidOption
|
212
77
|
STDERR.puts "#$! for command '#{cmd_name}'"
|
213
78
|
STDERR.puts "Try #{cmd_name} -h to get help."
|
@@ -233,7 +98,7 @@ module GoldenBrindle
|
|
233
98
|
true
|
234
99
|
end
|
235
100
|
end
|
236
|
-
|
101
|
+
|
237
102
|
end
|
238
|
-
|
103
|
+
|
239
104
|
end
|
data/lib/golden_brindle/const.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
module GoldenBrindle
|
2
|
-
|
3
2
|
module Const
|
4
|
-
|
5
3
|
# current version
|
6
|
-
VERSION="0.
|
4
|
+
VERSION="0.3"
|
7
5
|
# main banner
|
8
6
|
BANNER = "Usage: golden_brindle <command> [options]"
|
9
|
-
|
7
|
+
# config options names
|
8
|
+
CONFIG_KEYS = %w(address host port cwd log_file pid_file environment servers daemon debug config_script workers timeout user group prefix preload listen bundler)
|
9
|
+
ANSI_RED = "\033[0;31m"
|
10
|
+
ANSI_RESET = "\033[0m"
|
10
11
|
end
|
11
|
-
|
12
12
|
end
|
data/lib/golden_brindle/hooks.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
module
|
1
|
+
module GoldenBrindle
|
2
2
|
class RailsSupport
|
3
3
|
class << self
|
4
4
|
|
@@ -68,7 +68,7 @@ module Brindle
|
|
68
68
|
unless defined?(ActionDispatch::Static)
|
69
69
|
use Rails::Rack::Static
|
70
70
|
end
|
71
|
-
run
|
71
|
+
run ::GoldenBrindle::RailsSupport.rails_dispatcher
|
72
72
|
end
|
73
73
|
end
|
74
74
|
end.to_app
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module GoldenBrindle
|
2
|
+
module Validations
|
3
|
+
|
4
|
+
# Validates the given expression is true and prints the message if not, exiting.
|
5
|
+
def valid?(exp, message)
|
6
|
+
if !exp
|
7
|
+
failure message
|
8
|
+
@valid = false
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Validates that a file exists and if not displays the message
|
13
|
+
def valid_exists?(file, message)
|
14
|
+
valid?(File.exist?(file), message)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Validates that the file is a file and not a directory or something else.
|
18
|
+
def valid_file?(file, message)
|
19
|
+
valid?(::File.file?(file), message)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Validates that the given directory exists
|
23
|
+
def valid_dir?(file, message)
|
24
|
+
valid?(::File.directory?(file), message)
|
25
|
+
end
|
26
|
+
|
27
|
+
def can_change_user?
|
28
|
+
valid?(::Process.euid.zero?, "if you want to change workers UID/GID you must run programm from root")
|
29
|
+
end
|
30
|
+
|
31
|
+
def valid_user?(user)
|
32
|
+
return unless can_change_user?
|
33
|
+
begin
|
34
|
+
::Etc.getpwnam(user)
|
35
|
+
rescue
|
36
|
+
failure "User does not exist: #{user}"
|
37
|
+
@valid = false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def valid_group?(group)
|
42
|
+
begin
|
43
|
+
::Etc.getgrnam(group)
|
44
|
+
rescue
|
45
|
+
failure "Group does not exist: #{group}"
|
46
|
+
@valid = false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Just a simple method to display failure until something better is developed.
|
51
|
+
def failure(message)
|
52
|
+
STDERR.puts "#{::GoldenBrindle::Const::ANSI_RED}!!! * #{message}#{::GoldenBrindle::Const::ANSI_RESET}"
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|