kirk 0.1.0-java → 0.1.5-java
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/README.md +18 -3
- data/lib/kirk.rb +1 -0
- data/lib/kirk/applications/deploy_watcher.rb +104 -21
- data/lib/kirk/applications/hot_deployable.rb +21 -6
- data/lib/kirk/applications/redeploy_client.rb +45 -0
- data/lib/kirk/builder.rb +15 -9
- data/lib/kirk/cli.rb +45 -5
- data/lib/kirk/server.rb +2 -0
- data/lib/kirk/version.rb +1 -1
- metadata +3 -2
data/README.md
CHANGED
@@ -14,9 +14,9 @@ retarded ruby rack server out there.
|
|
14
14
|
Here is a brief highlight of some of the features available.
|
15
15
|
|
16
16
|
* 0 Downtime deploys: Deploy new versions of your rack application without
|
17
|
-
losing a single request.
|
18
|
-
|
19
|
-
|
17
|
+
losing a single request. This happens atomically so it will work even under
|
18
|
+
heavy load. Also, if the redeploy fails for some reason (the application fails
|
19
|
+
to boot), then the previous application remains live.
|
20
20
|
|
21
21
|
* Request body streaming: Have a large request body to handle? Why wait until
|
22
22
|
receiving the entire thing before starting the work?
|
@@ -67,6 +67,21 @@ Once you have Kirk configured, start it up with the following command:
|
|
67
67
|
|
68
68
|
... and you're off.
|
69
69
|
|
70
|
+
### Redeploying
|
71
|
+
|
72
|
+
As showed above, one way to trigger a redeploy is to specify a magic file to
|
73
|
+
watch and then touch it. While this is simple, it doesn't give you any insight
|
74
|
+
into the process of the redeploy. If the redeploy fails, there is no feedback.
|
75
|
+
|
76
|
+
Another way to deploy is by running `kirk redeploy -R /path/to/app/config.ru`
|
77
|
+
|
78
|
+
$ kirk redeploy -R /path/to/app/config.ru
|
79
|
+
Waiting for response...
|
80
|
+
Redeploying application...
|
81
|
+
Redeploy complete.
|
82
|
+
|
83
|
+
... that was easy.
|
84
|
+
|
70
85
|
### Daemonizing Kirk
|
71
86
|
|
72
87
|
Use your OS features. For example, write an upstart script or use
|
data/lib/kirk.rb
CHANGED
@@ -29,6 +29,7 @@ module Kirk
|
|
29
29
|
autoload :DeployWatcher, 'kirk/applications/deploy_watcher'
|
30
30
|
autoload :HotDeployable, 'kirk/applications/hot_deployable'
|
31
31
|
autoload :Rack, 'kirk/applications/rack'
|
32
|
+
autoload :RedeployClient, 'kirk/applications/redeploy_client'
|
32
33
|
end
|
33
34
|
|
34
35
|
require 'kirk/builder'
|
@@ -1,17 +1,31 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
1
3
|
module Kirk
|
2
4
|
class Applications::DeployWatcher
|
3
5
|
include Jetty::LifeCycle::Listener
|
4
6
|
|
5
|
-
def initialize
|
7
|
+
def initialize(unix_socket_path = nil)
|
6
8
|
@apps, @magic_telephone, @workers = [], LinkedBlockingQueue.new, 0
|
7
|
-
@
|
9
|
+
@unix_socket, @unix_socket_path = nil, unix_socket_path
|
10
|
+
@info = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def start
|
14
|
+
raise "Watcher already started" if @thread
|
15
|
+
connect_unix_socket_server
|
8
16
|
@thread = Thread.new { run_loop }
|
9
17
|
@thread.abort_on_exception = true
|
18
|
+
true
|
10
19
|
end
|
11
20
|
|
12
21
|
def stop
|
13
22
|
@magic_telephone.put :halt
|
23
|
+
|
24
|
+
disconnect_unix_socket_server
|
25
|
+
|
14
26
|
@thread.join
|
27
|
+
@thread = nil
|
28
|
+
true
|
15
29
|
end
|
16
30
|
|
17
31
|
def life_cycle_started(app)
|
@@ -29,9 +43,32 @@ module Kirk
|
|
29
43
|
|
30
44
|
private
|
31
45
|
|
46
|
+
def connect_unix_socket_server
|
47
|
+
return unless @unix_socket_path
|
48
|
+
|
49
|
+
umask = File.umask
|
50
|
+
File.umask(0137)
|
51
|
+
|
52
|
+
if File.exist?(@unix_socket_path)
|
53
|
+
File.delete(@unix_socket_path)
|
54
|
+
end
|
55
|
+
|
56
|
+
@unix_socket = UNIXServer.new(@unix_socket_path)
|
57
|
+
ensure
|
58
|
+
File.umask(umask)
|
59
|
+
end
|
60
|
+
|
61
|
+
def disconnect_unix_socket_server
|
62
|
+
@unix_socket.close if @unix_socket
|
63
|
+
File.delete(@unix_socket_path) if @unix_socket_path
|
64
|
+
end
|
65
|
+
|
32
66
|
def run_loop
|
33
67
|
while true
|
34
|
-
# First,
|
68
|
+
# First, check if there are any pending connections
|
69
|
+
handle_redeploys_on_socket
|
70
|
+
|
71
|
+
# Then, pull off messages
|
35
72
|
while msg = @magic_telephone.poll(50, TimeUnit::MILLISECONDS)
|
36
73
|
return if msg == :halt
|
37
74
|
handle_message(*msg)
|
@@ -48,6 +85,41 @@ module Kirk
|
|
48
85
|
cleanup
|
49
86
|
end
|
50
87
|
|
88
|
+
def handle_redeploys_on_socket
|
89
|
+
return unless @unix_socket
|
90
|
+
|
91
|
+
conn = @unix_socket.accept_nonblock
|
92
|
+
line = conn.gets.chomp
|
93
|
+
|
94
|
+
if line =~ /^REDEPLOY (.*)$/
|
95
|
+
rackup_path = $1
|
96
|
+
app = @apps.find { |a| a.rackup_path == rackup_path }
|
97
|
+
|
98
|
+
unless app
|
99
|
+
conn.write "ERROR No application racked up at `#{rackup_path}`\n"
|
100
|
+
return
|
101
|
+
end
|
102
|
+
|
103
|
+
conn.write "INFO Redeploying application...\n"
|
104
|
+
|
105
|
+
if redeploy(app)
|
106
|
+
conn.write "INFO Redeploy complete.\n"
|
107
|
+
else
|
108
|
+
conn.write "ERROR Something went wrong\n"
|
109
|
+
end
|
110
|
+
else
|
111
|
+
conn.write "ERROR unknown command\n"
|
112
|
+
end
|
113
|
+
rescue Errno::EAGAIN,
|
114
|
+
Errno::EWOULDBLOCK,
|
115
|
+
Errno::ECONNABORTED,
|
116
|
+
Errno::EPROTO,
|
117
|
+
Errno::EINTR
|
118
|
+
# Nothing
|
119
|
+
ensure
|
120
|
+
conn.close if conn
|
121
|
+
end
|
122
|
+
|
51
123
|
def handle_message(action, *args)
|
52
124
|
case action
|
53
125
|
when :watch
|
@@ -84,37 +156,48 @@ module Kirk
|
|
84
156
|
new_last_modified = app.last_modified
|
85
157
|
info = @info[app]
|
86
158
|
|
159
|
+
next unless new_last_modified
|
160
|
+
|
87
161
|
if new_last_modified > info[:last_modified]
|
88
|
-
Kirk.logger.info("Reloading `#{app.
|
89
|
-
|
162
|
+
Kirk.logger.info("Reloading `#{app.rackup_path}`")
|
163
|
+
|
164
|
+
# Redeploy the application
|
165
|
+
redeploy(app)
|
166
|
+
|
167
|
+
# Update the last modified time
|
168
|
+
info[:last_modified] = new_last_modified
|
90
169
|
end
|
91
170
|
end
|
92
171
|
end
|
93
172
|
|
94
|
-
def redeploy(app
|
95
|
-
|
173
|
+
def redeploy(app)
|
174
|
+
ret = app.deploy(get_standby_deploy(app))
|
175
|
+
warmup_standby_deploy(app)
|
176
|
+
ret
|
177
|
+
end
|
96
178
|
|
97
|
-
|
98
|
-
|
179
|
+
def get_standby_deploy(app)
|
180
|
+
queue = @info[app][:standby]
|
181
|
+
key = app.key
|
99
182
|
|
100
|
-
|
101
|
-
app.deploy(deploy)
|
102
|
-
else
|
103
|
-
deploy.terminate if deploy
|
104
|
-
app.redeploy
|
105
|
-
end
|
183
|
+
return unless key
|
106
184
|
|
107
|
-
|
108
|
-
|
109
|
-
|
185
|
+
while deploy = queue.poll
|
186
|
+
return deploy if deploy.key == key
|
187
|
+
# Otherwise, we don't need it anymore
|
188
|
+
background { deploy.terminate }
|
110
189
|
end
|
111
|
-
|
112
|
-
@info[app][:last_modified] = mtime
|
113
190
|
end
|
114
191
|
|
115
192
|
def warmup_standby_deploy(app)
|
116
193
|
queue = @info[app][:standby]
|
117
|
-
|
194
|
+
|
195
|
+
return unless queue.size == 0
|
196
|
+
|
197
|
+
background do
|
198
|
+
deploy = app.build_deploy
|
199
|
+
queue.put deploy if deploy
|
200
|
+
end
|
118
201
|
end
|
119
202
|
|
120
203
|
def cleanup
|
@@ -7,7 +7,7 @@ module Kirk
|
|
7
7
|
|
8
8
|
def initialize(config)
|
9
9
|
super(config)
|
10
|
-
|
10
|
+
deploy
|
11
11
|
end
|
12
12
|
|
13
13
|
def key
|
@@ -28,6 +28,10 @@ module Kirk
|
|
28
28
|
config.application_path
|
29
29
|
end
|
30
30
|
|
31
|
+
def rackup_path
|
32
|
+
config.rackup
|
33
|
+
end
|
34
|
+
|
31
35
|
def last_modified
|
32
36
|
mtimes = config.watch.map do |path|
|
33
37
|
path = File.expand_path(path, application_path)
|
@@ -37,13 +41,24 @@ module Kirk
|
|
37
41
|
mtimes.max
|
38
42
|
end
|
39
43
|
|
40
|
-
def
|
41
|
-
deploy
|
44
|
+
def deploy(deploy = nil)
|
45
|
+
deploy ||= build_deploy
|
46
|
+
|
47
|
+
if deploy
|
48
|
+
deploy.prepare
|
49
|
+
super(deploy)
|
50
|
+
true
|
51
|
+
end
|
52
|
+
rescue Exception => e
|
53
|
+
Kirk.logger.warning "Deploying `#{application_path}` failed: #{e.message}"
|
54
|
+
nil
|
42
55
|
end
|
43
56
|
|
44
|
-
def
|
45
|
-
|
46
|
-
|
57
|
+
def build_deploy
|
58
|
+
super
|
59
|
+
rescue Exception => e
|
60
|
+
Kirk.logger.warning "Warming up `#{application_path}` failed: #{e.message}"
|
61
|
+
nil
|
47
62
|
end
|
48
63
|
end
|
49
64
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module Kirk
|
4
|
+
class Applications::RedeployClient
|
5
|
+
def self.redeploy(socket, path, &blk)
|
6
|
+
new(socket).redeploy(path, &blk)
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(unix_socket_path)
|
10
|
+
@unix_socket, @unix_socket_path = nil, unix_socket_path
|
11
|
+
end
|
12
|
+
|
13
|
+
def redeploy(path, &blk)
|
14
|
+
connect
|
15
|
+
@unix_socket.write "REDEPLOY #{path}\n"
|
16
|
+
handle_response(&blk)
|
17
|
+
ensure
|
18
|
+
disconnect
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def connect
|
24
|
+
@unix_socket = UNIXSocket.new(@unix_socket_path)
|
25
|
+
end
|
26
|
+
|
27
|
+
def disconnect
|
28
|
+
@unix_socket.close
|
29
|
+
end
|
30
|
+
|
31
|
+
def handle_response(&blk)
|
32
|
+
yield "Waiting for response..." if block_given?
|
33
|
+
|
34
|
+
while line = @unix_socket.gets
|
35
|
+
msg = case line
|
36
|
+
when /^INFO (.*)$/ then $1
|
37
|
+
when /^ERROR (.*)$/ then "[ERROR] #{$1}"
|
38
|
+
else "[ERROR] Received unknown message: `#{line}`"
|
39
|
+
end
|
40
|
+
|
41
|
+
yield msg if block_given?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/kirk/builder.rb
CHANGED
@@ -12,18 +12,24 @@ module Kirk
|
|
12
12
|
@root = root || Dir.pwd
|
13
13
|
@current = nil
|
14
14
|
@configs = []
|
15
|
-
@options = {
|
15
|
+
@options = {
|
16
|
+
:watcher => Applications::DeployWatcher.new("/tmp/kirk.sock")
|
17
|
+
}
|
16
18
|
end
|
17
19
|
|
18
|
-
def load(
|
19
|
-
|
20
|
+
def load(glob)
|
21
|
+
glob = expand_path(glob)
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
23
|
+
files = Dir[glob].select { |f| File.file?(f) }
|
24
|
+
|
25
|
+
if files.empty?
|
26
|
+
raise MissingConfigFile, "glob `#{glob}` did not match any files"
|
27
|
+
end
|
25
28
|
|
26
|
-
|
29
|
+
files.each do |file|
|
30
|
+
with_root File.dirname(file) do
|
31
|
+
instance_eval(File.read(file), file)
|
32
|
+
end
|
27
33
|
end
|
28
34
|
end
|
29
35
|
|
@@ -125,7 +131,7 @@ module Kirk
|
|
125
131
|
def new_config
|
126
132
|
Applications::Config.new.tap do |config|
|
127
133
|
config.listen = '0.0.0.0:9090'
|
128
|
-
config.watch = [
|
134
|
+
config.watch = [ ]
|
129
135
|
config.bootstrap_path = File.expand_path('../bootstrap.rb', __FILE__)
|
130
136
|
end
|
131
137
|
end
|
data/lib/kirk/cli.rb
CHANGED
@@ -9,15 +9,13 @@ module Kirk
|
|
9
9
|
|
10
10
|
def initialize(argv)
|
11
11
|
@argv = argv.dup
|
12
|
+
@command = nil
|
12
13
|
@options = default_options
|
13
14
|
end
|
14
15
|
|
15
16
|
def run
|
16
17
|
parse!
|
17
|
-
|
18
|
-
server = Kirk::Server.build(config)
|
19
|
-
server.start
|
20
|
-
server.join
|
18
|
+
send(command_handler)
|
21
19
|
rescue Exception => e
|
22
20
|
abort "[ERROR] #{e.message}"
|
23
21
|
end
|
@@ -28,6 +26,33 @@ module Kirk
|
|
28
26
|
@options[:config]
|
29
27
|
end
|
30
28
|
|
29
|
+
def commands
|
30
|
+
[ 'start', 'redeploy' ]
|
31
|
+
end
|
32
|
+
|
33
|
+
def command_handler
|
34
|
+
"handle_#{@command}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def handle_start
|
38
|
+
server = Kirk::Server.build(config)
|
39
|
+
server.start
|
40
|
+
server.join
|
41
|
+
end
|
42
|
+
|
43
|
+
def handle_redeploy
|
44
|
+
rackup = File.expand_path(@options[:rackup] || "#{Dir.pwd}/config.ru")
|
45
|
+
client = Applications::RedeployClient.new('/tmp/kirk.sock')
|
46
|
+
|
47
|
+
unless File.exist?(rackup)
|
48
|
+
raise MissingRackupFile, "rackup file `#{rackup}` does not exist"
|
49
|
+
end
|
50
|
+
|
51
|
+
client.redeploy(rackup) do |log|
|
52
|
+
puts log
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
31
56
|
def default_options
|
32
57
|
{ :config => "#{Dir.pwd}/Kirkfile" }
|
33
58
|
end
|
@@ -36,17 +61,32 @@ module Kirk
|
|
36
61
|
@parser ||= OptionParser.new do |opts|
|
37
62
|
opts.banner = "Usage: kirk [options] <command> [<args>]"
|
38
63
|
|
64
|
+
opts.separator ""
|
65
|
+
opts.separator "The available Kirk commands are:"
|
66
|
+
opts.separator " start Start up Kirk"
|
67
|
+
opts.separator " redeploy Redeploy a specific application"
|
68
|
+
|
39
69
|
opts.separator ""
|
40
70
|
opts.separator "Server options:"
|
41
71
|
|
42
|
-
opts.on("-c", "--config FILE",
|
72
|
+
opts.on("-c", "--config FILE", "Load options from a config file") do |file|
|
43
73
|
@options[:config] = file
|
44
74
|
end
|
75
|
+
|
76
|
+
opts.on("-R", "--rackup FILE", "Specify a rackup file") do |file|
|
77
|
+
@options[:rackup] = file
|
78
|
+
end
|
79
|
+
|
80
|
+
opts.on("-h", "--help", "Show this message") do
|
81
|
+
puts opts
|
82
|
+
exit!
|
83
|
+
end
|
45
84
|
end
|
46
85
|
end
|
47
86
|
|
48
87
|
def parse!
|
49
88
|
parser.parse! @argv
|
89
|
+
@command = @argv.shift || "start"
|
50
90
|
end
|
51
91
|
end
|
52
92
|
end
|
data/lib/kirk/server.rb
CHANGED
data/lib/kirk/version.rb
CHANGED
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: kirk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.1.
|
5
|
+
version: 0.1.5
|
6
6
|
platform: java
|
7
7
|
authors:
|
8
8
|
- Carl Lerche
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-02-
|
13
|
+
date: 2011-02-07 00:00:00 -08:00
|
14
14
|
default_executable: kirk
|
15
15
|
dependencies: []
|
16
16
|
|
@@ -41,6 +41,7 @@ files:
|
|
41
41
|
- lib/kirk/applications/deploy_watcher.rb
|
42
42
|
- lib/kirk/applications/hot_deployable.rb
|
43
43
|
- lib/kirk/applications/rack.rb
|
44
|
+
- lib/kirk/applications/redeploy_client.rb
|
44
45
|
- lib/rack/handler/kirk.rb
|
45
46
|
- lib/kirk/native.jar
|
46
47
|
- lib/kirk/jetty/jetty-server-7.2.2.v20101205.jar
|