uninterruptible 2.3.1 → 2.4.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 658c5837ae17777ac2f5d53223b61941444c26ca
4
- data.tar.gz: db14a041c27690a14565f5938255856b1bfde528
3
+ metadata.gz: 6c0bee80d95f929271d75f691c59788a9716e8e3
4
+ data.tar.gz: 877066cf757d14caab769f7585713170f7db08e2
5
5
  SHA512:
6
- metadata.gz: 5421a7627be91b402d0189ae4791dbcce3c5a2d259ffbcaaa3177a4e577d18d1b7a384a86d700dcdf8ae493e872516593f25b4f83daea6dc020a5ea24b847417
7
- data.tar.gz: 214ded520ee70b7c87fbf8ed9efaf139803830db02d294189a56d9c9f0ecc76277159599ac950abb1338642e300d38bcf9c624327563364e7ff4c9106fa9dd7e
6
+ metadata.gz: 2f2f1edad24e0bad293acb8f70a3171388c98d73f266f025c570ed58fda332d47cf0f95c00cfd1f8b293b142b4d8882b496201daa5da30beada562363b8999df
7
+ data.tar.gz: 775f0729644939e948a87adb085105651a00500793d6db9b19b5965faf008f5bea4dbab21fb279bd030604f134c177530c65db64ea37530c4bf3f9f23ecfa543
data/CHANGELOG.md CHANGED
@@ -1,22 +1,25 @@
1
1
  # Changelog
2
2
 
3
- # 2.3.0
3
+ ## 2.4.0
4
+ * When restarting a server, the socket is passed to the new server via a UNIX socket instead of inheriting open file descriptors from the parent.
5
+
6
+ ## 2.3.0
4
7
  * Incoming connections can be restricted to certain networks by setting `allowed_networks` in the configuration.
5
8
 
6
- # 2.2.1
9
+ ## 2.2.1
7
10
  * Allow multiple certificates to be used in one build file
8
11
 
9
- # 2.2.0
12
+ ## 2.2.0
10
13
  * Verify client TLS certificates
11
14
  * Allow trusted client CA to be set
12
15
 
13
- # 2.1.1
16
+ ## 2.1.1
14
17
  * Prevent bad SSL handshakes from crashing server
15
18
 
16
- # 2.1.0
19
+ ## 2.1.0
17
20
  * Add TLS support for TCP connections
18
21
 
19
- # 2.0.0
22
+ ## 2.0.0
20
23
  * Use an internal pipe for delivering signals to the main thread.
21
24
  * `accept_connections` retired in favour of a select loop and `accept_client_connection` being called for each waiting connection
22
25
  * Logging when shutting down or restarting
@@ -34,8 +34,8 @@ module Uninterruptible
34
34
  #
35
35
  # @return [TCPServer] Socket server for the configured address and port
36
36
  def bind_to_tcp_socket
37
- if ENV[SERVER_FD_VAR]
38
- TCPServer.for_fd(ENV[SERVER_FD_VAR].to_i)
37
+ if ENV[FILE_DESCRIPTOR_SERVER_VAR]
38
+ TCPServer.for_fd(read_file_descriptor_from_fds)
39
39
  else
40
40
  TCPServer.new(bind_uri.host, bind_uri.port)
41
41
  end
@@ -45,14 +45,16 @@ module Uninterruptible
45
45
  #
46
46
  # @return [UNIXServer] Socket server for the configured path
47
47
  def bind_to_unix_socket
48
- if ENV[SERVER_FD_VAR]
49
- UNIXServer.for_fd(ENV[SERVER_FD_VAR].to_i)
48
+ if ENV[FILE_DESCRIPTOR_SERVER_VAR]
49
+ UNIXServer.for_fd(read_file_descriptor_from_fds)
50
50
  else
51
51
  File.delete(bind_uri.path) if File.exist?(bind_uri.path)
52
52
  UNIXServer.new(bind_uri.path)
53
53
  end
54
54
  end
55
55
 
56
+ private
57
+
56
58
  # Parse the bind address in the configuration
57
59
  #
58
60
  # @param [String] bind_address The config for a server we're returning the socket for
@@ -65,5 +67,14 @@ module Uninterruptible
65
67
  rescue URI::Error
66
68
  raise Uninterruptible::ConfigurationError, "Couldn't parse the bind address: \"#{bind_address}\""
67
69
  end
70
+
71
+ # Open a connection to a running FileDesciptorServer on the parent of this server and obtain a file descriptor
72
+ # for the running socket server on there.
73
+ def read_file_descriptor_from_fds
74
+ fds_socket = UNIXSocket.new(ENV[FILE_DESCRIPTOR_SERVER_VAR])
75
+ socket_file_descriptor = fds_socket.recv_io
76
+ fds_socket.close
77
+ socket_file_descriptor.to_i
78
+ end
68
79
  end
69
80
  end
@@ -0,0 +1,51 @@
1
+ require 'tmpdir'
2
+ require 'socket'
3
+
4
+ module Uninterruptible
5
+ class FileDescriptorServer
6
+ attr_reader :io_object, :socket_server
7
+
8
+ # Creates a new FileDescriptorServer and starts a listenting socket server
9
+ #
10
+ # @param [IO] Any IO object that will be shared by this server
11
+ def initialize(io_object)
12
+ @io_object = io_object
13
+
14
+ start_socket_server
15
+ end
16
+
17
+ # @return [String] Location on disk where socket server is listening
18
+ def socket_path
19
+ @socket_path ||= File.join(socket_directory, 'file_descriptor_server.sock')
20
+ end
21
+
22
+ # Accept the next client connection and send it the file descriptor
23
+ #
24
+ # @raise [RuntimeError] Raises a runtime error if the socket server is closed
25
+ def serve_file_descriptor
26
+ raise "File descriptor server has been closed" if socket_server.closed?
27
+
28
+ client = socket_server.accept
29
+ client.send_io(io_object)
30
+ client.close
31
+ end
32
+
33
+ # Close the socket server and tidy up any created files
34
+ def close
35
+ socket_server.close
36
+
37
+ File.delete(socket_path)
38
+ Dir.rmdir(socket_directory)
39
+ end
40
+
41
+ private
42
+
43
+ def socket_directory
44
+ @socket_directory ||= Dir.mktmpdir('uninterruptible-')
45
+ end
46
+
47
+ def start_socket_server
48
+ @socket_server = UNIXServer.new(socket_path)
49
+ end
50
+ end
51
+ end
@@ -113,12 +113,12 @@ module Uninterruptible
113
113
  end
114
114
  end
115
115
 
116
- # Listen (or reconnect) to the bind address and port specified in the config. If SERVER_FD_VAR is set in the env,
117
- # reconnect to that file descriptor. Once @socket_server is set, write the file descriptor ID to the env.
116
+ # Listen (or reconnect) to the bind address and port specified in the config. If FILE_DESCRIPTOR_SERVER_PATH is set
117
+ # in the env, reconnect to that file descriptor.
118
118
  def establish_socket_server
119
119
  @socket_server = Uninterruptible::Binder.new(server_configuration.bind).bind_to_socket
120
120
  # If there's a file descriptor present, take over from a previous instance of this server and kill it off
121
- kill_parent if ENV[SERVER_FD_VAR]
121
+ kill_parent if ENV[FILE_DESCRIPTOR_SERVER_VAR]
122
122
 
123
123
  @socket_server.autoclose = false
124
124
  @socket_server.close_on_exec = false
@@ -126,8 +126,6 @@ module Uninterruptible
126
126
  if server_configuration.tls_enabled?
127
127
  @socket_server = Uninterruptible::TLSServerFactory.new(server_configuration).wrap_with_tls(@socket_server)
128
128
  end
129
-
130
- ENV[SERVER_FD_VAR] = @socket_server.to_i.to_s
131
129
  end
132
130
 
133
131
  # Send a TERM signal to the parent process. This will be called by a newly spawned server if it has been started
@@ -194,11 +192,22 @@ module Uninterruptible
194
192
 
195
193
  # Start a new copy of this server, maintaining all current file descriptors and env.
196
194
  def hot_restart
195
+ # Start a FileDescriptorServer running on a unix socket
196
+ file_descriptor_server = FileDescriptorServer.new(socket_server)
197
+
197
198
  fork do
199
+ # Let the new server know where to find the file descriptor server
200
+ ENV[FILE_DESCRIPTOR_SERVER_VAR] = file_descriptor_server.socket_path
201
+
198
202
  Dir.chdir(ENV['APP_ROOT']) if ENV['APP_ROOT']
199
203
  ENV.delete('BUNDLE_GEMFILE') # Ensure a fresh bundle is used
200
- exec("bundle exec --keep-file-descriptors #{server_configuration.start_command}", :close_others => false)
204
+
205
+ exec("bundle exec #{server_configuration.start_command}")
201
206
  end
207
+
208
+ # Provide the new server with the file descriptor for @socket_server
209
+ file_descriptor_server.serve_file_descriptor
210
+ file_descriptor_server.close
202
211
  end
203
212
 
204
213
  def network_restrictions
@@ -1,3 +1,3 @@
1
1
  module Uninterruptible
2
- VERSION = "2.3.1".freeze
2
+ VERSION = "2.4.0".freeze
3
3
  end
@@ -5,6 +5,7 @@ require "uninterruptible/version"
5
5
  require 'uninterruptible/ssl_extensions'
6
6
  require 'uninterruptible/configuration'
7
7
  require 'uninterruptible/binder'
8
+ require 'uninterruptible/file_descriptor_server'
8
9
  require 'uninterruptible/network_restrictions'
9
10
  require 'uninterruptible/tls_server_factory'
10
11
  require 'uninterruptible/server'
@@ -13,5 +14,5 @@ require 'uninterruptible/server'
13
14
  module Uninterruptible
14
15
  class ConfigurationError < StandardError; end
15
16
 
16
- SERVER_FD_VAR = 'UNINTERRUPTIBLE_SERVER_FD'.freeze
17
+ FILE_DESCRIPTOR_SERVER_VAR = 'FILE_DESCRIPTOR_SERVER_PATH'.freeze
17
18
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uninterruptible
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.1
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Wentworth
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-03-19 00:00:00.000000000 Z
11
+ date: 2019-05-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -75,6 +75,7 @@ files:
75
75
  - lib/uninterruptible.rb
76
76
  - lib/uninterruptible/binder.rb
77
77
  - lib/uninterruptible/configuration.rb
78
+ - lib/uninterruptible/file_descriptor_server.rb
78
79
  - lib/uninterruptible/network_restrictions.rb
79
80
  - lib/uninterruptible/server.rb
80
81
  - lib/uninterruptible/ssl_extensions.rb
@@ -101,7 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
101
102
  version: '0'
102
103
  requirements: []
103
104
  rubyforge_project:
104
- rubygems_version: 2.5.2.1
105
+ rubygems_version: 2.6.14.3
105
106
  signing_key:
106
107
  specification_version: 4
107
108
  summary: Zero-downtime restarts for your trivial socket servers