hotcell-client-legacy 1.1.0 → 1.2.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 +4 -4
- data/README.md +1 -1
- data/lib/hot_cell/client/legacy.rb +71 -26
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c69cef47499d5d65909fa3be45dbcc447b56d9f9fec0c5f2bd1fbb65b9b63e88
|
|
4
|
+
data.tar.gz: 4c251a71745d240adc39fb144fe0695baca23a2b65c4a0b005ea1e6209c44182
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b1efde0d7580dcf53b06db03a8256216de8b5c20085239d5eb2873cfb8668a20e289f21c6892d0cea56986d2c6448ea7552def34896f25c1418896115c71678f
|
|
7
|
+
data.tar.gz: b5aff56e7f0c394496865d9e9700fef4753e13cd69f3a29b8a3fc688e6b16cb43cad76308d73d1b818a53fd92d019c3a8ab6eac1c1b169b5d3789a72a7fd8ccd
|
data/README.md
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
# hotcell-client-legacy
|
|
2
2
|
|
|
3
|
-
Part of [
|
|
3
|
+
Part of [Hot Cell](https://github.com/basecamp/hotcell). See [docs/client-legacy.md](https://github.com/basecamp/hotcell/blob/master/docs/client-legacy.md).
|
|
@@ -14,7 +14,7 @@ module HotCell
|
|
|
14
14
|
#
|
|
15
15
|
# Written for Ruby 1.9.3, so it uses no keyword arguments, `&.`, `String#b` or `IO#wait_readable`.
|
|
16
16
|
class Legacy
|
|
17
|
-
VERSION = "1.
|
|
17
|
+
VERSION = "1.2.0"
|
|
18
18
|
|
|
19
19
|
PROTOCOL_VERSION = 1
|
|
20
20
|
MAX_RESPONSE_BYTES = 65_536
|
|
@@ -29,7 +29,7 @@ module HotCell
|
|
|
29
29
|
INPUT_MODE = 0o640
|
|
30
30
|
OUTPUT_MODE = 0o620
|
|
31
31
|
|
|
32
|
-
# A cell's
|
|
32
|
+
# A cell's answer to a request that did not succeed, with the readers of hotcell-core's HotCell::Failure.
|
|
33
33
|
class Failure
|
|
34
34
|
attr_reader :code, :cause, :signal, :error_class, :message, :stderr
|
|
35
35
|
|
|
@@ -56,27 +56,50 @@ module HotCell
|
|
|
56
56
|
def to_s
|
|
57
57
|
[ (code unless code.empty?), cause, error_class, message ].compact.join(": ")
|
|
58
58
|
end
|
|
59
|
+
|
|
60
|
+
# Included in both failure classes, so `rescue Failure::Exception` catches either and
|
|
61
|
+
# `hot_cell_failure` reads the failure, as with hotcell-client's HotCell::Failure::Exception. The
|
|
62
|
+
# classes share no superclass, so rescuing one by name never catches the other: a permanent failure
|
|
63
|
+
# may be written down against a file forever, and a transient one must be retried.
|
|
64
|
+
module Exception
|
|
65
|
+
attr_reader :hot_cell_response
|
|
66
|
+
|
|
67
|
+
def initialize(response)
|
|
68
|
+
@hot_cell_response = response
|
|
69
|
+
super(response.failure.to_s)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def hot_cell_failure
|
|
73
|
+
hot_cell_response.failure
|
|
74
|
+
end
|
|
75
|
+
end
|
|
59
76
|
end
|
|
60
77
|
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
|
|
66
|
-
attr_reader :
|
|
78
|
+
# The outcome of one call, success or failure. `timing` is the cell's own open map, such as
|
|
79
|
+
# `queued_ms` and `perform_ms`, and is empty when the answer carried none. `duration_ms` is the time this
|
|
80
|
+
# client measures across the connect, the send and the read, so `duration_ms` minus `perform_ms` is
|
|
81
|
+
# transport plus queueing.
|
|
82
|
+
class Response
|
|
83
|
+
attr_reader :result, :failure, :timing, :duration_ms
|
|
84
|
+
|
|
85
|
+
def initialize(result, failure, timing, duration_ms)
|
|
86
|
+
@result = result
|
|
87
|
+
@failure = failure
|
|
88
|
+
@timing = timing
|
|
89
|
+
@duration_ms = duration_ms
|
|
90
|
+
end
|
|
67
91
|
|
|
68
|
-
def
|
|
69
|
-
|
|
70
|
-
super(failure.to_s)
|
|
92
|
+
def ok?
|
|
93
|
+
failure.nil?
|
|
71
94
|
end
|
|
72
95
|
end
|
|
73
96
|
|
|
74
97
|
class PermanentFailure < StandardError
|
|
75
|
-
include
|
|
98
|
+
include Failure::Exception
|
|
76
99
|
end
|
|
77
100
|
|
|
78
101
|
class TransientFailure < StandardError
|
|
79
|
-
include
|
|
102
|
+
include Failure::Exception
|
|
80
103
|
end
|
|
81
104
|
|
|
82
105
|
class Timeout < StandardError; end
|
|
@@ -93,7 +116,7 @@ module HotCell
|
|
|
93
116
|
@group = options[:group]
|
|
94
117
|
end
|
|
95
118
|
|
|
96
|
-
# Returns
|
|
119
|
+
# Returns a Response whose result is a Hash with String keys. Raises PermanentFailure or TransientFailure
|
|
97
120
|
# according to the `permanent` flag on the cell's answer; a failure with no answer at all is transient.
|
|
98
121
|
def perform(operation, inputs, outputs, payload = {})
|
|
99
122
|
inputs = [ inputs ] unless inputs.is_a?(Array)
|
|
@@ -104,19 +127,41 @@ module HotCell
|
|
|
104
127
|
share inputs, INPUT_MODE
|
|
105
128
|
share outputs, OUTPUT_MODE
|
|
106
129
|
|
|
107
|
-
|
|
108
|
-
|
|
130
|
+
call socket_path, operation.to_s, inputs, outputs, payload
|
|
131
|
+
end
|
|
109
132
|
|
|
110
|
-
|
|
111
|
-
|
|
133
|
+
# The cell's static description, from the control socket beside the work socket. Raises as `perform` does.
|
|
134
|
+
def describe
|
|
135
|
+
call control_socket_path, "hotcell.describe", [], [], {}
|
|
136
|
+
end
|
|
112
137
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
raise (failure.permanent? ? PermanentFailure : TransientFailure).new(failure)
|
|
138
|
+
# The cell's counters, from the control socket beside the work socket. Raises as `perform` does.
|
|
139
|
+
def metrics
|
|
140
|
+
call control_socket_path, "hotcell.metrics", [], [], {}
|
|
117
141
|
end
|
|
118
142
|
|
|
119
143
|
private
|
|
144
|
+
def control_socket_path
|
|
145
|
+
File.join File.dirname(socket_path), "control.sock"
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def call(path, operation, inputs, outputs, payload)
|
|
149
|
+
line = JSON.generate("v" => PROTOCOL_VERSION, "op" => operation, "inputs" => inputs.size,
|
|
150
|
+
"outputs" => outputs.size, "payload" => payload) + "\n"
|
|
151
|
+
|
|
152
|
+
started = now
|
|
153
|
+
answer = exchange(path, line, inputs + outputs)
|
|
154
|
+
duration_ms = (now - started) * 1000
|
|
155
|
+
timing = answer["timing"].is_a?(Hash) ? answer["timing"] : {}
|
|
156
|
+
return Response.new(answer["result"], nil, timing, duration_ms) if answer["ok"]
|
|
157
|
+
|
|
158
|
+
# Built rather than passed to raise, because Ruby 2.x reads a trailing Hash given to raise as its options
|
|
159
|
+
# and takes `cause` out of it.
|
|
160
|
+
failure = Failure.new(sanitize(answer["error"]))
|
|
161
|
+
response = Response.new(nil, failure, timing, duration_ms)
|
|
162
|
+
raise (failure.permanent? ? PermanentFailure : TransientFailure).new(response)
|
|
163
|
+
end
|
|
164
|
+
|
|
120
165
|
def share(ios, mode)
|
|
121
166
|
return if group.nil?
|
|
122
167
|
|
|
@@ -126,10 +171,10 @@ module HotCell
|
|
|
126
171
|
end
|
|
127
172
|
end
|
|
128
173
|
|
|
129
|
-
def exchange(line, descriptors)
|
|
174
|
+
def exchange(path, line, descriptors)
|
|
130
175
|
deadline = now + timeout
|
|
131
176
|
socket = Socket.new(:UNIX, :STREAM)
|
|
132
|
-
connect socket, deadline
|
|
177
|
+
connect socket, path, deadline
|
|
133
178
|
deliver socket, line, descriptors, deadline
|
|
134
179
|
response = receive(socket, deadline)
|
|
135
180
|
return unavailable("the connection closed with no response, so the cell's supervisor is gone") if response.nil?
|
|
@@ -147,8 +192,8 @@ module HotCell
|
|
|
147
192
|
# with no deadline, and a supervisor that has stopped calling `accept` leaves it full. Linux answers a
|
|
148
193
|
# non-blocking connect to that backlog with EAGAIN and leaves the socket unconnected, so it is retried
|
|
149
194
|
# rather than waited on: an unconnected Unix socket is always writable, and `select` would not wait.
|
|
150
|
-
def connect(socket, deadline)
|
|
151
|
-
socket.connect_nonblock Socket.sockaddr_un(
|
|
195
|
+
def connect(socket, path, deadline)
|
|
196
|
+
socket.connect_nonblock Socket.sockaddr_un(path)
|
|
152
197
|
rescue Errno::EAGAIN
|
|
153
198
|
remaining = deadline - now
|
|
154
199
|
raise Timeout unless remaining > 0
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hotcell-client-legacy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mike Dalessio
|
|
@@ -9,7 +9,7 @@ bindir: bin
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
|
-
description: Call an operation in a
|
|
12
|
+
description: Call an operation in a Hot Cell container from an application that cannot
|
|
13
13
|
run hotcell-client. One file, the standard library only, and legacy Ruby versions.
|
|
14
14
|
email:
|
|
15
15
|
- mike@37signals.com
|
|
@@ -26,8 +26,8 @@ licenses:
|
|
|
26
26
|
- MIT
|
|
27
27
|
metadata:
|
|
28
28
|
homepage_uri: https://github.com/basecamp/hotcell
|
|
29
|
-
source_code_uri: https://github.com/basecamp/hotcell/tree/v1.
|
|
30
|
-
changelog_uri: https://github.com/basecamp/hotcell/blob/v1.
|
|
29
|
+
source_code_uri: https://github.com/basecamp/hotcell/tree/v1.2.0/hotcell-client-legacy
|
|
30
|
+
changelog_uri: https://github.com/basecamp/hotcell/blob/v1.2.0/CHANGELOG.md
|
|
31
31
|
documentation_uri: https://basecamp.github.io/hotcell/
|
|
32
32
|
bug_tracker_uri: https://github.com/basecamp/hotcell/issues
|
|
33
33
|
rubygems_mfa_required: 'true'
|
|
@@ -45,7 +45,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
46
|
version: '0'
|
|
47
47
|
requirements: []
|
|
48
|
-
rubygems_version: 4.0.
|
|
48
|
+
rubygems_version: 4.0.6
|
|
49
49
|
specification_version: 4
|
|
50
|
-
summary: Call a
|
|
50
|
+
summary: Call a Hot Cell from a legacy Ruby version, with no dependencies.
|
|
51
51
|
test_files: []
|