warden-client 0.1.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.
- data/Gemfile +10 -0
- data/README.md +13 -0
- data/Rakefile +8 -0
- data/lib/warden/client.rb +115 -0
- data/lib/warden/client/v1.rb +286 -0
- data/lib/warden/client/version.rb +5 -0
- data/spec/client/v1_spec.rb +447 -0
- data/spec/client_spec.rb +224 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/support/mock_warden_server.rb +78 -0
- data/warden-client.gemspec +20 -0
- metadata +78 -0
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# warden-client
|
2
|
+
|
3
|
+
> This README describes a **client** component. Please refer to the top
|
4
|
+
> level [README][tlr] for an overview of all components.
|
5
|
+
|
6
|
+
[tlr]: /README.md
|
7
|
+
|
8
|
+
## License
|
9
|
+
|
10
|
+
The project is licensed under the Apache 2.0 license (see the
|
11
|
+
[`LICENSE`][license] file in the root directory of the repository).
|
12
|
+
|
13
|
+
[license]: /LICENSE
|
data/Rakefile
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require "socket"
|
2
|
+
require "warden/protocol"
|
3
|
+
require "warden/client/v1"
|
4
|
+
|
5
|
+
module Warden
|
6
|
+
|
7
|
+
class Client
|
8
|
+
|
9
|
+
class Error < StandardError; end
|
10
|
+
class ServerError < Error; end
|
11
|
+
|
12
|
+
attr_reader :path
|
13
|
+
|
14
|
+
def initialize(path)
|
15
|
+
@path = path
|
16
|
+
@v1mode = false
|
17
|
+
end
|
18
|
+
|
19
|
+
def connected?
|
20
|
+
!@sock.nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
def connect
|
24
|
+
raise "already connected" if connected?
|
25
|
+
@sock = ::UNIXSocket.new(path)
|
26
|
+
end
|
27
|
+
|
28
|
+
def disconnect
|
29
|
+
raise "not connected" unless connected?
|
30
|
+
@sock.close rescue nil
|
31
|
+
@sock = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def reconnect
|
35
|
+
disconnect if connected?
|
36
|
+
connect
|
37
|
+
end
|
38
|
+
|
39
|
+
def io
|
40
|
+
rv = yield
|
41
|
+
if rv.nil?
|
42
|
+
disconnect
|
43
|
+
raise ::EOFError
|
44
|
+
end
|
45
|
+
|
46
|
+
rv
|
47
|
+
end
|
48
|
+
|
49
|
+
def read
|
50
|
+
length = io { @sock.gets }
|
51
|
+
data = io { @sock.read(length.to_i) }
|
52
|
+
|
53
|
+
# Discard \r\n
|
54
|
+
io { @sock.read(2) }
|
55
|
+
|
56
|
+
response = Warden::Protocol::Message.decode(data).response
|
57
|
+
|
58
|
+
# Raise error replies
|
59
|
+
if response.is_a?(Warden::Protocol::ErrorResponse)
|
60
|
+
raise Warden::Client::ServerError.new(response.message)
|
61
|
+
end
|
62
|
+
|
63
|
+
if @v1mode
|
64
|
+
response = V1.response_to_v1(response)
|
65
|
+
end
|
66
|
+
|
67
|
+
response
|
68
|
+
end
|
69
|
+
|
70
|
+
def write(request)
|
71
|
+
if request.kind_of?(Array)
|
72
|
+
@v1mode = true
|
73
|
+
request = V1.request_from_v1(request.dup)
|
74
|
+
end
|
75
|
+
|
76
|
+
unless request.kind_of?(Warden::Protocol::BaseRequest)
|
77
|
+
raise "Expected #kind_of? Warden::Protocol::BaseRequest"
|
78
|
+
end
|
79
|
+
|
80
|
+
data = request.wrap.encode.to_s
|
81
|
+
@sock.write data.length.to_s + "\r\n"
|
82
|
+
@sock.write data + "\r\n"
|
83
|
+
end
|
84
|
+
|
85
|
+
def stream(request, &blk)
|
86
|
+
unless request.is_a?(Warden::Protocol::StreamRequest)
|
87
|
+
msg = "Expected argument to be of type:"
|
88
|
+
msg << "'#{Warden::Protocol::StreamRequest}'"
|
89
|
+
msg << ", but received: '#{request.class}'."
|
90
|
+
raise ArgumentError, msg
|
91
|
+
end
|
92
|
+
|
93
|
+
response = call(request)
|
94
|
+
while response.exit_status.nil?
|
95
|
+
blk.call(response)
|
96
|
+
response = read
|
97
|
+
end
|
98
|
+
|
99
|
+
response
|
100
|
+
end
|
101
|
+
|
102
|
+
def call(request)
|
103
|
+
write(request)
|
104
|
+
read
|
105
|
+
end
|
106
|
+
|
107
|
+
def method_missing(sym, *args, &blk)
|
108
|
+
klass_name = sym.to_s.gsub(/(^|_)([a-z])/) { $2.upcase }
|
109
|
+
klass_name += "Request"
|
110
|
+
klass = Warden::Protocol.const_get(klass_name)
|
111
|
+
|
112
|
+
call(klass.new(*args))
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,286 @@
|
|
1
|
+
require "warden/protocol"
|
2
|
+
|
3
|
+
module Warden
|
4
|
+
class Client
|
5
|
+
class V1
|
6
|
+
def self.request_from_v1(args)
|
7
|
+
command = args.shift
|
8
|
+
|
9
|
+
m = "convert_#{command}_request".downcase
|
10
|
+
if respond_to?(m)
|
11
|
+
send(m, args)
|
12
|
+
else
|
13
|
+
raise "Unknown command: #{command.upcase}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.response_to_v1(response)
|
18
|
+
klass_name = response.class.name.split("::").last
|
19
|
+
klass_name = klass_name.gsub(/Response$/, "")
|
20
|
+
klass_name = klass_name.gsub(/(.)([A-Z])/) { |m| "#{m[0]}_#{m[1]}" }
|
21
|
+
klass_name = klass_name.downcase
|
22
|
+
|
23
|
+
m = "convert_#{klass_name}_response".downcase
|
24
|
+
if respond_to?(m)
|
25
|
+
send(m, response)
|
26
|
+
else
|
27
|
+
raise "Unknown response: #{response.class.name.split("::").last}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def self.convert_create_request(args)
|
34
|
+
request = Protocol::CreateRequest.new
|
35
|
+
config = args.shift || {}
|
36
|
+
|
37
|
+
config.each do |key, value|
|
38
|
+
case key
|
39
|
+
when "bind_mounts"
|
40
|
+
bind_mounts = value.map do |src_path, dst_path, mode|
|
41
|
+
bind_mount = Protocol::CreateRequest::BindMount.new
|
42
|
+
bind_mount.src_path = src_path
|
43
|
+
bind_mount.dst_path = dst_path
|
44
|
+
|
45
|
+
if mode.kind_of?(Hash)
|
46
|
+
mode = mode["mode"]
|
47
|
+
end
|
48
|
+
|
49
|
+
bind_mount.mode = Protocol::CreateRequest::BindMount::Mode.const_get(mode.to_s.upcase)
|
50
|
+
bind_mount
|
51
|
+
end
|
52
|
+
|
53
|
+
request.bind_mounts = bind_mounts
|
54
|
+
when "grace_time"
|
55
|
+
request.grace_time = Integer(value)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
request
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.convert_create_response(response)
|
63
|
+
response.handle
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.convert_stop_request(args)
|
67
|
+
request = Protocol::StopRequest.new
|
68
|
+
request.handle = args.shift
|
69
|
+
request
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.convert_stop_response(response)
|
73
|
+
"ok"
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.convert_destroy_request(args)
|
77
|
+
request = Protocol::DestroyRequest.new
|
78
|
+
request.handle = args.shift
|
79
|
+
request
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.convert_destroy_response(response)
|
83
|
+
"ok"
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.convert_info_request(args)
|
87
|
+
request = Protocol::InfoRequest.new
|
88
|
+
request.handle = args.shift
|
89
|
+
request
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.convert_info_response(response)
|
93
|
+
stringify_hash response.to_hash
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.convert_spawn_request(args)
|
97
|
+
request = Protocol::SpawnRequest.new
|
98
|
+
request.handle = args.shift
|
99
|
+
request.script = args.shift
|
100
|
+
request
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.convert_spawn_response(response)
|
104
|
+
response.job_id
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.convert_link_request(args)
|
108
|
+
request = Protocol::LinkRequest.new
|
109
|
+
request.handle = args.shift
|
110
|
+
request.job_id = Integer(args.shift)
|
111
|
+
request
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.convert_link_response(response)
|
115
|
+
[response.exit_status, response.stdout, response.stderr]
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.convert_stream_request(args)
|
119
|
+
request = Protocol::StreamRequest.new
|
120
|
+
request.handle = args.shift
|
121
|
+
request.job_id = Integer(args.shift)
|
122
|
+
request
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.convert_stream_response(response)
|
126
|
+
[response.name, response.data, response.exit_status]
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.convert_run_request(args)
|
130
|
+
request = Protocol::RunRequest.new
|
131
|
+
request.handle = args.shift
|
132
|
+
request.script = args.shift
|
133
|
+
request
|
134
|
+
end
|
135
|
+
|
136
|
+
def self.convert_run_response(response)
|
137
|
+
[response.exit_status, response.stdout, response.stderr]
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.convert_net_request(args)
|
141
|
+
request = nil
|
142
|
+
handle = args.shift
|
143
|
+
direction = args.shift
|
144
|
+
|
145
|
+
case direction
|
146
|
+
when "in"
|
147
|
+
request = Protocol::NetInRequest.new
|
148
|
+
request.handle = handle
|
149
|
+
when "out"
|
150
|
+
request = Protocol::NetOutRequest.new
|
151
|
+
request.handle = handle
|
152
|
+
|
153
|
+
network, port = args.shift.split(":", 2)
|
154
|
+
request.network = network
|
155
|
+
request.port = Integer(port)
|
156
|
+
else
|
157
|
+
raise "Unknown net direction: #{direction}"
|
158
|
+
end
|
159
|
+
|
160
|
+
request
|
161
|
+
end
|
162
|
+
|
163
|
+
def self.convert_net_in_response(response)
|
164
|
+
stringify_hash response.to_hash
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.convert_net_out_response(response)
|
168
|
+
"ok"
|
169
|
+
end
|
170
|
+
|
171
|
+
def self.convert_copy_request(args)
|
172
|
+
request = nil
|
173
|
+
handle = args.shift
|
174
|
+
direction = args.shift
|
175
|
+
src_path = args.shift
|
176
|
+
dst_path = args.shift
|
177
|
+
owner = args.shift
|
178
|
+
|
179
|
+
attributes = {
|
180
|
+
:handle => handle,
|
181
|
+
:src_path => src_path,
|
182
|
+
:dst_path => dst_path
|
183
|
+
}
|
184
|
+
|
185
|
+
case direction
|
186
|
+
when "in"
|
187
|
+
request = Protocol::CopyInRequest.new(attributes)
|
188
|
+
when "out"
|
189
|
+
request = Protocol::CopyOutRequest.new(attributes)
|
190
|
+
request.owner = owner if owner
|
191
|
+
else
|
192
|
+
raise "Unknown copy direction: #{direction}"
|
193
|
+
end
|
194
|
+
|
195
|
+
request
|
196
|
+
end
|
197
|
+
|
198
|
+
def self.convert_copy_in_response(response)
|
199
|
+
"ok"
|
200
|
+
end
|
201
|
+
|
202
|
+
def self.convert_copy_out_response(response)
|
203
|
+
"ok"
|
204
|
+
end
|
205
|
+
|
206
|
+
def self.convert_limit_request(args)
|
207
|
+
request = nil
|
208
|
+
handle = args.shift
|
209
|
+
limit = args.shift
|
210
|
+
|
211
|
+
attributes = {
|
212
|
+
:handle => handle,
|
213
|
+
}
|
214
|
+
|
215
|
+
case limit
|
216
|
+
when "mem"
|
217
|
+
request = Protocol::LimitMemoryRequest.new(attributes)
|
218
|
+
request.limit_in_bytes = Integer(args.shift) unless args.empty?
|
219
|
+
when "disk"
|
220
|
+
request = Protocol::LimitDiskRequest.new(attributes)
|
221
|
+
request.byte = Integer(args.shift) unless args.empty?
|
222
|
+
when "bandwidth"
|
223
|
+
request = Protocol::LimitBandwidthRequest.new(attributes)
|
224
|
+
request.rate = Integer(args.shift) unless args.empty?
|
225
|
+
request.burst = Integer(args.shift) unless args.empty?
|
226
|
+
else
|
227
|
+
raise "Unknown limit: #{limit}"
|
228
|
+
end
|
229
|
+
|
230
|
+
request
|
231
|
+
end
|
232
|
+
|
233
|
+
def self.convert_limit_memory_response(response)
|
234
|
+
response.limit_in_bytes
|
235
|
+
end
|
236
|
+
|
237
|
+
def self.convert_limit_disk_response(response)
|
238
|
+
response.byte
|
239
|
+
end
|
240
|
+
|
241
|
+
def self.convert_limit_bandwidth_response(response)
|
242
|
+
"rate: #{response.rate} burst: #{response.burst}"
|
243
|
+
end
|
244
|
+
|
245
|
+
def self.convert_ping_request(args)
|
246
|
+
request = Protocol::PingRequest.new
|
247
|
+
request
|
248
|
+
end
|
249
|
+
|
250
|
+
def self.convert_ping_response(response)
|
251
|
+
"pong"
|
252
|
+
end
|
253
|
+
|
254
|
+
def self.convert_list_request(args)
|
255
|
+
request = Protocol::ListRequest.new
|
256
|
+
request
|
257
|
+
end
|
258
|
+
|
259
|
+
def self.convert_list_response(response)
|
260
|
+
response.handles
|
261
|
+
end
|
262
|
+
|
263
|
+
def self.convert_echo_request(args)
|
264
|
+
request = Protocol::EchoRequest.new
|
265
|
+
request.message = args.shift
|
266
|
+
request
|
267
|
+
end
|
268
|
+
|
269
|
+
def self.convert_echo_response(response)
|
270
|
+
response.message
|
271
|
+
end
|
272
|
+
|
273
|
+
private
|
274
|
+
|
275
|
+
def self.stringify_hash(hash)
|
276
|
+
Hash[hash.map do |key, value|
|
277
|
+
if value.kind_of?(Hash)
|
278
|
+
value = stringify_hash(value)
|
279
|
+
end
|
280
|
+
|
281
|
+
[key.to_s, value]
|
282
|
+
end]
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
@@ -0,0 +1,447 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "warden/client/v1"
|
3
|
+
|
4
|
+
describe Warden::Client::V1 do
|
5
|
+
def to_request(args)
|
6
|
+
described_class.request_from_v1(args)
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_response(response)
|
10
|
+
described_class.response_to_v1(response)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "create" do
|
14
|
+
describe "request" do
|
15
|
+
describe "bind_mount parameter" do
|
16
|
+
it "should use src and dst parameters" do
|
17
|
+
request = to_request [
|
18
|
+
"create",
|
19
|
+
{ "bind_mounts" => [["/src", "/dst", "ro"]] },
|
20
|
+
]
|
21
|
+
|
22
|
+
request.bind_mounts.should have(1).bind_mount
|
23
|
+
|
24
|
+
bind_mount = request.bind_mounts.first
|
25
|
+
bind_mount.src_path.should == "/src"
|
26
|
+
bind_mount.dst_path.should == "/dst"
|
27
|
+
end
|
28
|
+
|
29
|
+
["ro", { "mode" => "ro" }].each do |mode|
|
30
|
+
it "should convert ro mode when passed as #{mode.inspect}" do
|
31
|
+
request = to_request [
|
32
|
+
"create",
|
33
|
+
{ "bind_mounts" => [["/src", "/dst", mode]] },
|
34
|
+
]
|
35
|
+
|
36
|
+
request.bind_mounts.should have(1).bind_mount
|
37
|
+
|
38
|
+
bind_mount = request.bind_mounts.first
|
39
|
+
bind_mount.src_path.should == "/src"
|
40
|
+
bind_mount.dst_path.should == "/dst"
|
41
|
+
bind_mount.mode.should == Warden::Protocol::CreateRequest::BindMount::Mode::RO
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
["rw", { "mode" => "rw" }].each do |mode|
|
46
|
+
it "should convert rw mode when passed as #{mode.inspect}" do
|
47
|
+
request = to_request [
|
48
|
+
"create",
|
49
|
+
{ "bind_mounts" => [["/src", "/dst", "rw"]] },
|
50
|
+
]
|
51
|
+
|
52
|
+
request.bind_mounts.first.mode.should ==
|
53
|
+
Warden::Protocol::CreateRequest::BindMount::Mode::RW
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
["rx", { "mode" => "rx" }].each do |mode|
|
58
|
+
it "should raise on an invalid mode when passed as #{mode.inspect}" do
|
59
|
+
expect do
|
60
|
+
to_request [
|
61
|
+
"create",
|
62
|
+
{ "bind_mounts" => [["/src", "/dst", "rx"]] },
|
63
|
+
]
|
64
|
+
end.to raise_error
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "grace_time parameter" do
|
70
|
+
it "should be converted to an integer" do
|
71
|
+
request = to_request [
|
72
|
+
"create",
|
73
|
+
{ "grace_time" => 1.1 },
|
74
|
+
]
|
75
|
+
|
76
|
+
request.grace_time.should == 1
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should raise on an invalid value" do
|
80
|
+
expect do
|
81
|
+
to_request [
|
82
|
+
"create",
|
83
|
+
{ "grace_time" => "invalid" },
|
84
|
+
]
|
85
|
+
end.to raise_error
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "response" do
|
91
|
+
it "should return the handle" do
|
92
|
+
response = to_response \
|
93
|
+
Warden::Protocol::CreateResponse.new(:handle => "handle")
|
94
|
+
response.should == "handle"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "stop" do
|
100
|
+
describe "request" do
|
101
|
+
subject { to_request ["stop", "handle"] }
|
102
|
+
|
103
|
+
its(:class) { should == Warden::Protocol::StopRequest }
|
104
|
+
its(:handle) { should == "handle" }
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "response" do
|
108
|
+
it "should always be ok" do
|
109
|
+
response = to_response \
|
110
|
+
Warden::Protocol::StopResponse.new
|
111
|
+
response.should == "ok"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "destroy" do
|
117
|
+
describe "request" do
|
118
|
+
subject { to_request ["destroy", "handle"] }
|
119
|
+
|
120
|
+
its(:class) { should == Warden::Protocol::DestroyRequest }
|
121
|
+
its(:handle) { should == "handle" }
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "response" do
|
125
|
+
it "should always be ok" do
|
126
|
+
response = to_response \
|
127
|
+
Warden::Protocol::DestroyResponse.new
|
128
|
+
response.should == "ok"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe "info" do
|
134
|
+
describe "request" do
|
135
|
+
subject { to_request ["info", "handle"] }
|
136
|
+
|
137
|
+
its(:class) { should == Warden::Protocol::InfoRequest }
|
138
|
+
its(:handle) { should == "handle" }
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "response" do
|
142
|
+
let(:response) do
|
143
|
+
to_response \
|
144
|
+
Warden::Protocol::InfoResponse.new({
|
145
|
+
:state => "state",
|
146
|
+
:memory_stat => Warden::Protocol::InfoResponse::MemoryStat.new({
|
147
|
+
:cache => 1,
|
148
|
+
:rss => 2,
|
149
|
+
})
|
150
|
+
})
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should return a hash" do
|
154
|
+
response.should be_a(Hash)
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should stringify keys" do
|
158
|
+
response["state"].should == "state"
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should stringify keys of nested hashes" do
|
162
|
+
response["memory_stat"]["cache"].should == 1
|
163
|
+
response["memory_stat"]["rss"].should == 2
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe "spawn" do
|
169
|
+
describe "request" do
|
170
|
+
subject { to_request ["spawn", "handle", "echo foo"] }
|
171
|
+
|
172
|
+
its(:class) { should == Warden::Protocol::SpawnRequest }
|
173
|
+
its(:handle) { should == "handle" }
|
174
|
+
its(:script) { should == "echo foo" }
|
175
|
+
end
|
176
|
+
|
177
|
+
describe "response" do
|
178
|
+
it "should return job_id" do
|
179
|
+
response = to_response \
|
180
|
+
Warden::Protocol::SpawnResponse.new(:job_id => 3)
|
181
|
+
response.should == 3
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe "link" do
|
187
|
+
describe "request" do
|
188
|
+
subject { to_request ["link", "handle", "1"] }
|
189
|
+
|
190
|
+
its(:class) { should == Warden::Protocol::LinkRequest }
|
191
|
+
its(:handle) { should == "handle" }
|
192
|
+
its(:job_id) { should == 1 }
|
193
|
+
end
|
194
|
+
|
195
|
+
describe "response" do
|
196
|
+
it "should return a 3-element tuple" do
|
197
|
+
response = to_response \
|
198
|
+
Warden::Protocol::LinkResponse.new(
|
199
|
+
:exit_status => 255,
|
200
|
+
:stdout => "stdout",
|
201
|
+
:stderr => "stderr"
|
202
|
+
)
|
203
|
+
|
204
|
+
response[0].should == 255
|
205
|
+
response[1].should == "stdout"
|
206
|
+
response[2].should == "stderr"
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
describe "stream" do
|
212
|
+
describe "request" do
|
213
|
+
subject { to_request ["stream", "handle", "1"] }
|
214
|
+
|
215
|
+
its(:class) { should == Warden::Protocol::StreamRequest }
|
216
|
+
its(:handle) { should == "handle" }
|
217
|
+
its(:job_id) { should == 1 }
|
218
|
+
end
|
219
|
+
|
220
|
+
describe "response" do
|
221
|
+
it "should return a 3-element tuple" do
|
222
|
+
response = to_response \
|
223
|
+
Warden::Protocol::StreamResponse.new(
|
224
|
+
:name => "stdout",
|
225
|
+
:data => "data",
|
226
|
+
:exit_status => 25,
|
227
|
+
)
|
228
|
+
|
229
|
+
response[0].should == "stdout"
|
230
|
+
response[1].should == "data"
|
231
|
+
response[2].should == 25
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
describe "run" do
|
237
|
+
describe "request" do
|
238
|
+
subject { to_request ["run", "handle", "echo foo"] }
|
239
|
+
|
240
|
+
its(:class) { should == Warden::Protocol::RunRequest }
|
241
|
+
its(:handle) { should == "handle" }
|
242
|
+
its(:script) { should == "echo foo" }
|
243
|
+
end
|
244
|
+
|
245
|
+
describe "response" do
|
246
|
+
it "should return a 3-element tuple" do
|
247
|
+
response = to_response \
|
248
|
+
Warden::Protocol::RunResponse.new(
|
249
|
+
:exit_status => 255,
|
250
|
+
:stdout => "stdout",
|
251
|
+
:stderr => "stderr"
|
252
|
+
)
|
253
|
+
|
254
|
+
response[0].should == 255
|
255
|
+
response[1].should == "stdout"
|
256
|
+
response[2].should == "stderr"
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
describe "net" do
|
262
|
+
describe "request (in)" do
|
263
|
+
subject { to_request ["net", "handle", "in"] }
|
264
|
+
|
265
|
+
its(:class) { should == Warden::Protocol::NetInRequest }
|
266
|
+
its(:handle) { should == "handle" }
|
267
|
+
end
|
268
|
+
|
269
|
+
describe "request (out)" do
|
270
|
+
subject { to_request ["net", "handle", "out", "network:1234"] }
|
271
|
+
|
272
|
+
its(:class) { should == Warden::Protocol::NetOutRequest }
|
273
|
+
its(:handle) { should == "handle" }
|
274
|
+
its(:network) { should == "network" }
|
275
|
+
its(:port) { should == 1234 }
|
276
|
+
end
|
277
|
+
|
278
|
+
describe "response (in)" do
|
279
|
+
it "should return a hash with both properties" do
|
280
|
+
response = to_response \
|
281
|
+
Warden::Protocol::NetInResponse.new(
|
282
|
+
:host_port => 1234,
|
283
|
+
:container_port => 2345,
|
284
|
+
)
|
285
|
+
|
286
|
+
response["host_port"].should == 1234
|
287
|
+
response["container_port"].should == 2345
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
describe "response (out)" do
|
292
|
+
it "should always be ok" do
|
293
|
+
response = to_response \
|
294
|
+
Warden::Protocol::NetOutResponse.new
|
295
|
+
response.should == "ok"
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
describe "copy" do
|
301
|
+
describe "request (in)" do
|
302
|
+
subject { to_request ["copy", "handle", "in", "/src", "/dst"] }
|
303
|
+
|
304
|
+
its(:class) { should == Warden::Protocol::CopyInRequest }
|
305
|
+
its(:handle) { should == "handle" }
|
306
|
+
its(:src_path) { should == "/src" }
|
307
|
+
its(:dst_path) { should == "/dst" }
|
308
|
+
end
|
309
|
+
|
310
|
+
describe "request (out)" do
|
311
|
+
subject { to_request ["copy", "handle", "out", "/src", "/dst", "owner"] }
|
312
|
+
|
313
|
+
its(:class) { should == Warden::Protocol::CopyOutRequest }
|
314
|
+
its(:handle) { should == "handle" }
|
315
|
+
its(:src_path) { should == "/src" }
|
316
|
+
its(:dst_path) { should == "/dst" }
|
317
|
+
its(:owner) { should == "owner" }
|
318
|
+
end
|
319
|
+
|
320
|
+
describe "response (in)" do
|
321
|
+
it "should always be ok" do
|
322
|
+
response = to_response \
|
323
|
+
Warden::Protocol::CopyInResponse.new
|
324
|
+
response.should == "ok"
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
describe "response (out)" do
|
329
|
+
it "should always be ok" do
|
330
|
+
response = to_response \
|
331
|
+
Warden::Protocol::CopyOutResponse.new
|
332
|
+
response.should == "ok"
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
describe "limit" do
|
338
|
+
describe "request (mem)" do
|
339
|
+
describe "without limit" do
|
340
|
+
subject { to_request ["limit", "handle", "mem"] }
|
341
|
+
|
342
|
+
its(:class) { should == Warden::Protocol::LimitMemoryRequest }
|
343
|
+
its(:handle) { should == "handle" }
|
344
|
+
its(:limit_in_bytes) { should be_nil }
|
345
|
+
end
|
346
|
+
|
347
|
+
describe "with limit" do
|
348
|
+
subject { to_request ["limit", "handle", "mem", "1234"] }
|
349
|
+
|
350
|
+
its(:class) { should == Warden::Protocol::LimitMemoryRequest }
|
351
|
+
its(:handle) { should == "handle" }
|
352
|
+
its(:limit_in_bytes) { should == 1234 }
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
describe "response (mem)" do
|
357
|
+
it "should return #limit_in_bytes" do
|
358
|
+
response = to_response \
|
359
|
+
Warden::Protocol::LimitMemoryResponse.new({
|
360
|
+
:limit_in_bytes => 1234
|
361
|
+
})
|
362
|
+
response.should == 1234
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
describe "request (disk)" do
|
367
|
+
describe "without limit" do
|
368
|
+
subject { to_request ["limit", "handle", "disk"] }
|
369
|
+
|
370
|
+
its(:class) { should == Warden::Protocol::LimitDiskRequest }
|
371
|
+
its(:handle) { should == "handle" }
|
372
|
+
its(:byte) { should be_nil }
|
373
|
+
end
|
374
|
+
|
375
|
+
describe "with limit" do
|
376
|
+
subject { to_request ["limit", "handle", "disk", "1234"] }
|
377
|
+
|
378
|
+
its(:class) { should == Warden::Protocol::LimitDiskRequest }
|
379
|
+
its(:handle) { should == "handle" }
|
380
|
+
its(:byte) { should == 1234 }
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
describe "response (disk)" do
|
385
|
+
it "should return #byte" do
|
386
|
+
response = to_response \
|
387
|
+
Warden::Protocol::LimitDiskResponse.new({
|
388
|
+
:byte => 1234
|
389
|
+
})
|
390
|
+
response.should == 1234
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
describe "ping" do
|
396
|
+
describe "request" do
|
397
|
+
subject { to_request ["ping"] }
|
398
|
+
|
399
|
+
its(:class) { should == Warden::Protocol::PingRequest }
|
400
|
+
end
|
401
|
+
|
402
|
+
describe "response" do
|
403
|
+
it "should return pong" do
|
404
|
+
response = to_response \
|
405
|
+
Warden::Protocol::PingResponse.new
|
406
|
+
response.should == "pong"
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
describe "list" do
|
412
|
+
describe "request" do
|
413
|
+
subject { to_request ["list"] }
|
414
|
+
|
415
|
+
its(:class) { should == Warden::Protocol::ListRequest }
|
416
|
+
end
|
417
|
+
|
418
|
+
describe "response" do
|
419
|
+
it "should return an array with handles" do
|
420
|
+
response = to_response \
|
421
|
+
Warden::Protocol::ListResponse.new({
|
422
|
+
:handles => ["h1", "h2"]
|
423
|
+
})
|
424
|
+
response.should == ["h1", "h2"]
|
425
|
+
end
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
describe "echo" do
|
430
|
+
describe "request" do
|
431
|
+
subject { to_request ["echo", "hello world"] }
|
432
|
+
|
433
|
+
its(:class) { should == Warden::Protocol::EchoRequest }
|
434
|
+
its(:message) { should == "hello world" }
|
435
|
+
end
|
436
|
+
|
437
|
+
describe "response" do
|
438
|
+
it "should return #message" do
|
439
|
+
response = to_response \
|
440
|
+
Warden::Protocol::EchoResponse.new({
|
441
|
+
:message => "hello world"
|
442
|
+
})
|
443
|
+
response.should == "hello world"
|
444
|
+
end
|
445
|
+
end
|
446
|
+
end
|
447
|
+
end
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,224 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "support/mock_warden_server"
|
3
|
+
|
4
|
+
describe Warden::Client do
|
5
|
+
|
6
|
+
include_context :mock_warden_server
|
7
|
+
|
8
|
+
let(:client) do
|
9
|
+
new_client
|
10
|
+
end
|
11
|
+
|
12
|
+
it "shouldn't be able to connect without a server" do
|
13
|
+
expect do
|
14
|
+
client.connect
|
15
|
+
end.to raise_error
|
16
|
+
|
17
|
+
client.should_not be_connected
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should be able to connect with a server" do
|
21
|
+
start_server
|
22
|
+
|
23
|
+
expect do
|
24
|
+
client.connect
|
25
|
+
end.to_not raise_error
|
26
|
+
|
27
|
+
client.should be_connected
|
28
|
+
end
|
29
|
+
|
30
|
+
context "connection management" do
|
31
|
+
|
32
|
+
# This is super-racy: the ivar is updated from the server thread
|
33
|
+
def connection_count
|
34
|
+
sleep 0.001
|
35
|
+
@sessions.size
|
36
|
+
end
|
37
|
+
|
38
|
+
before(:each) do
|
39
|
+
@sessions = {}
|
40
|
+
start_server do |session, _|
|
41
|
+
@sessions[session] = 1
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "when connected" do
|
46
|
+
|
47
|
+
before(:each) do
|
48
|
+
client.connect
|
49
|
+
client.should be_connected
|
50
|
+
connection_count.should == 1
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should not allow connecting" do
|
54
|
+
expect do
|
55
|
+
client.connect
|
56
|
+
end.to raise_error
|
57
|
+
|
58
|
+
# This should not affect the connection
|
59
|
+
client.should be_connected
|
60
|
+
|
61
|
+
# This should not reconnect
|
62
|
+
connection_count.should == 1
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should allow disconnecting" do
|
66
|
+
client.disconnect
|
67
|
+
client.should_not be_connected
|
68
|
+
|
69
|
+
# This should not reconnect
|
70
|
+
connection_count.should == 1
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should allow reconnecting" do
|
74
|
+
client.reconnect
|
75
|
+
client.should be_connected
|
76
|
+
|
77
|
+
# This should have reconnected
|
78
|
+
connection_count.should == 2
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "when disconnected" do
|
83
|
+
|
84
|
+
before(:each) do
|
85
|
+
connection_count.should == 0
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should not allow disconnecting" do
|
89
|
+
expect do
|
90
|
+
client.disconnect
|
91
|
+
end.to raise_error
|
92
|
+
|
93
|
+
# This should not affect the connection
|
94
|
+
client.should_not be_connected
|
95
|
+
|
96
|
+
# This should not reconnect
|
97
|
+
connection_count.should == 0
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should allow connecting" do
|
101
|
+
client.connect
|
102
|
+
client.should be_connected
|
103
|
+
|
104
|
+
# This should have connected
|
105
|
+
connection_count.should == 1
|
106
|
+
end
|
107
|
+
|
108
|
+
# While it is semantically impossible to reconnect when the client was
|
109
|
+
# never connected to begin with, it IS possible.
|
110
|
+
it "should allow reconnecting" do
|
111
|
+
client.reconnect
|
112
|
+
client.should be_connected
|
113
|
+
|
114
|
+
# This should have connected
|
115
|
+
connection_count.should == 1
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "when connected" do
|
121
|
+
|
122
|
+
before(:each) do
|
123
|
+
container = nil
|
124
|
+
job_id = nil
|
125
|
+
|
126
|
+
start_server do |session, request|
|
127
|
+
next if request.nil?
|
128
|
+
|
129
|
+
if request.class == Warden::Protocol::EchoRequest
|
130
|
+
case request.message
|
131
|
+
when "eof"
|
132
|
+
session.close
|
133
|
+
when "error"
|
134
|
+
args = { :message => "error" }
|
135
|
+
session.respond(Warden::Protocol::ErrorResponse.new(args))
|
136
|
+
else
|
137
|
+
args = { :message => request.message }
|
138
|
+
session.respond(request.create_response(args))
|
139
|
+
end
|
140
|
+
elsif request.class == Warden::Protocol::CreateRequest
|
141
|
+
raise 'Cannot create more than one container' unless container.nil?
|
142
|
+
|
143
|
+
container = "test"
|
144
|
+
args = { :handle => container }
|
145
|
+
session.respond(Warden::Protocol::CreateResponse.new(args))
|
146
|
+
elsif request.class == Warden::Protocol::SpawnRequest
|
147
|
+
raise 'Unknown handle' unless request.handle == container
|
148
|
+
raise 'Cannot spawn more than one job' unless job_id.nil?
|
149
|
+
|
150
|
+
job_id = 1
|
151
|
+
args = { :job_id => job_id }
|
152
|
+
session.respond(Warden::Protocol::SpawnResponse.new(args))
|
153
|
+
elsif request.class == Warden::Protocol::StreamRequest
|
154
|
+
raise 'Unknown handle' unless request.handle == container
|
155
|
+
raise 'Unknown job' unless request.job_id == job_id
|
156
|
+
|
157
|
+
args = { :name => "stream", :data => "test" }
|
158
|
+
session.respond(Warden::Protocol::StreamResponse.new(args))
|
159
|
+
args = { :exit_status => 0 }
|
160
|
+
session.respond(Warden::Protocol::StreamResponse.new(args))
|
161
|
+
else
|
162
|
+
raise "Unknown request type: #{request.class}."
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
client.connect
|
167
|
+
client.should be_connected
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should raise EOFError on eof" do
|
171
|
+
expect do
|
172
|
+
client.echo(:message => "eof")
|
173
|
+
end.to raise_error(::EOFError)
|
174
|
+
|
175
|
+
# This should update the connection status
|
176
|
+
client.should_not be_connected
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should raise Warden::Client::ServerError on error payloads" do
|
180
|
+
expect do
|
181
|
+
client.echo(:message => "error")
|
182
|
+
end.to raise_error(Warden::Client::ServerError)
|
183
|
+
|
184
|
+
# This should not affect the connection
|
185
|
+
client.should be_connected
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should return decoded payload for non-error replies" do
|
189
|
+
response = client.echo(:message => "hello")
|
190
|
+
response.message.should == "hello"
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should work when called with the old API" do
|
194
|
+
response = client.call(["echo", "hello"])
|
195
|
+
response.should == "hello"
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should stream data" do
|
199
|
+
handle = client.create.handle
|
200
|
+
response = client.spawn(:handle => handle, :script => "echo test")
|
201
|
+
|
202
|
+
called = false
|
203
|
+
block = lambda do |response|
|
204
|
+
raise "Block should not be called more than once." if called
|
205
|
+
|
206
|
+
response.should be_an_instance_of Warden::Protocol::StreamResponse
|
207
|
+
response.data.should == "test"
|
208
|
+
response.name.should == "stream"
|
209
|
+
response.exit_status.should be_nil
|
210
|
+
|
211
|
+
called = true
|
212
|
+
end
|
213
|
+
|
214
|
+
request = Warden::Protocol::StreamRequest.new(:handle => handle,
|
215
|
+
:job_id => response.job_id)
|
216
|
+
response = client.stream(request, &block)
|
217
|
+
response.data.should be_nil
|
218
|
+
response.name.should be_nil
|
219
|
+
response.exit_status.should == 0
|
220
|
+
|
221
|
+
called.should be_true
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require "socket"
|
2
|
+
require "tempfile"
|
3
|
+
require "warden/protocol"
|
4
|
+
|
5
|
+
class Session
|
6
|
+
|
7
|
+
def initialize(sock, handler = nil)
|
8
|
+
@sock = sock
|
9
|
+
@handler = handler
|
10
|
+
|
11
|
+
# Post-initialization
|
12
|
+
handle(nil)
|
13
|
+
end
|
14
|
+
|
15
|
+
def handle(request)
|
16
|
+
@handler.call(self, request) if @handler
|
17
|
+
end
|
18
|
+
|
19
|
+
def close
|
20
|
+
@sock.close
|
21
|
+
ensure
|
22
|
+
@sock = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def respond(*responses)
|
26
|
+
responses.each do |response|
|
27
|
+
data = response.wrap.encode.to_s
|
28
|
+
@sock.write data.length.to_s + "\r\n"
|
29
|
+
@sock.write data + "\r\n"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def run!
|
34
|
+
while @sock && length = @sock.gets
|
35
|
+
data = @sock.read(length.to_i)
|
36
|
+
|
37
|
+
# Discard \r\n
|
38
|
+
@sock.read(2)
|
39
|
+
|
40
|
+
handle(Warden::Protocol::Message.decode(data).request)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
shared_context :mock_warden_server do
|
46
|
+
|
47
|
+
SERVER_PATH = File.expand_path("../../../tmp/mock_server.sock", __FILE__)
|
48
|
+
|
49
|
+
def new_client
|
50
|
+
Warden::Client.new(SERVER_PATH)
|
51
|
+
end
|
52
|
+
|
53
|
+
def start_server(&blk)
|
54
|
+
# Make sure the path to the unix socket is not used
|
55
|
+
FileUtils.rm_rf(SERVER_PATH)
|
56
|
+
|
57
|
+
# Create unix socket server
|
58
|
+
server = UNIXServer.new(SERVER_PATH)
|
59
|
+
|
60
|
+
# Accept new connections from a thread
|
61
|
+
@server = Thread.new do
|
62
|
+
begin
|
63
|
+
loop do
|
64
|
+
session = Session.new(server.accept, blk)
|
65
|
+
session.run!
|
66
|
+
end
|
67
|
+
rescue => ex
|
68
|
+
STDERR.puts ex.message
|
69
|
+
STDERR.puts ex.backtrace
|
70
|
+
raise
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
after(:each) do
|
76
|
+
@server.kill if @server
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "warden/client/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "warden-client"
|
7
|
+
s.version = Warden::Client::VERSION
|
8
|
+
s.authors = ["Pieter Noordhuis", "Matt Page"]
|
9
|
+
s.email = ["pcnoordhuis@gmail.com", "mpage@vmware.com"]
|
10
|
+
s.homepage = "http://www.cloudfoundry.org/"
|
11
|
+
s.summary = %q{Client driver for warden, the ephemeral container manager.}
|
12
|
+
s.description = %q{Provides a blocking client for interacting with the Warden.}
|
13
|
+
|
14
|
+
s.files = Dir.glob("**/*")
|
15
|
+
s.test_files = Dir.glob("spec/**/*")
|
16
|
+
s.executables = []
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_runtime_dependency "warden-protocol", "~> 0.1.0"
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: warden-client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Pieter Noordhuis
|
9
|
+
- Matt Page
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2013-06-12 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: warden-protocol
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.1.0
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: 0.1.0
|
31
|
+
description: Provides a blocking client for interacting with the Warden.
|
32
|
+
email:
|
33
|
+
- pcnoordhuis@gmail.com
|
34
|
+
- mpage@vmware.com
|
35
|
+
executables: []
|
36
|
+
extensions: []
|
37
|
+
extra_rdoc_files: []
|
38
|
+
files:
|
39
|
+
- Gemfile
|
40
|
+
- lib/warden/client/v1.rb
|
41
|
+
- lib/warden/client/version.rb
|
42
|
+
- lib/warden/client.rb
|
43
|
+
- Rakefile
|
44
|
+
- README.md
|
45
|
+
- spec/client/v1_spec.rb
|
46
|
+
- spec/client_spec.rb
|
47
|
+
- spec/spec_helper.rb
|
48
|
+
- spec/support/mock_warden_server.rb
|
49
|
+
- warden-client.gemspec
|
50
|
+
homepage: http://www.cloudfoundry.org/
|
51
|
+
licenses: []
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options: []
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ! '>='
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
requirements: []
|
69
|
+
rubyforge_project:
|
70
|
+
rubygems_version: 1.8.25
|
71
|
+
signing_key:
|
72
|
+
specification_version: 3
|
73
|
+
summary: Client driver for warden, the ephemeral container manager.
|
74
|
+
test_files:
|
75
|
+
- spec/client/v1_spec.rb
|
76
|
+
- spec/client_spec.rb
|
77
|
+
- spec/spec_helper.rb
|
78
|
+
- spec/support/mock_warden_server.rb
|