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 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. The basic strategy is similar to Passenger.
18
- Touch a magic file in the application root and Kirk will reload the
19
- application without dropping a single request... It's just magic.
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
@@ -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
- @info = {}
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, pull off messages
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.application_path}`")
89
- redeploy(app, new_last_modified)
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, mtime)
95
- if key = app.key
173
+ def redeploy(app)
174
+ ret = app.deploy(get_standby_deploy(app))
175
+ warmup_standby_deploy(app)
176
+ ret
177
+ end
96
178
 
97
- queue = @info[app][:standby]
98
- deploy = queue.poll
179
+ def get_standby_deploy(app)
180
+ queue = @info[app][:standby]
181
+ key = app.key
99
182
 
100
- if deploy && deploy.key == key
101
- app.deploy(deploy)
102
- else
103
- deploy.terminate if deploy
104
- app.redeploy
105
- end
183
+ return unless key
106
184
 
107
- warmup_standby_deploy(app)
108
- else
109
- app.redeploy
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
- background { queue.put(app.build_deploy) }
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
- redeploy
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 redeploy
41
- deploy(build_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 deploy(d)
45
- d.prepare
46
- super(d)
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
@@ -12,18 +12,24 @@ module Kirk
12
12
  @root = root || Dir.pwd
13
13
  @current = nil
14
14
  @configs = []
15
- @options = { :watcher => Applications::DeployWatcher.new }
15
+ @options = {
16
+ :watcher => Applications::DeployWatcher.new("/tmp/kirk.sock")
17
+ }
16
18
  end
17
19
 
18
- def load(path)
19
- path = expand_path(path)
20
+ def load(glob)
21
+ glob = expand_path(glob)
20
22
 
21
- with_root File.dirname(path) do
22
- unless File.exist?(path)
23
- raise MissingConfigFile, "config file `#{path}` does not exist"
24
- end
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
- instance_eval(File.read(path), path)
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 = [ 'REVISION' ]
134
+ config.watch = [ ]
129
135
  config.bootstrap_path = File.expand_path('../bootstrap.rb', __FILE__)
130
136
  end
131
137
  end
@@ -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", "Load options from a config file") do |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
@@ -36,6 +36,8 @@ module Kirk
36
36
  end
37
37
 
38
38
  def start
39
+ watcher.start if watcher
40
+
39
41
  @server = Jetty::Server.new.tap do |server|
40
42
  connectors.each do |conn|
41
43
  server.add_connector(conn)
@@ -1,4 +1,4 @@
1
1
  module Kirk
2
2
  NAME = "kirk".freeze
3
- VERSION = "0.1.0".freeze
3
+ VERSION = "0.1.5".freeze
4
4
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: kirk
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.1.0
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-02 00:00:00 -08:00
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