kirk 0.1.0-java → 0.1.5-java

Sign up to get free protection for your applications and to get access to all the features.
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