fleet-ruby 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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.md +67 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +43 -0
- data/LICENSE +201 -0
- data/README.md +151 -0
- data/Rakefile +8 -0
- data/circle.yml +13 -0
- data/fleet-ruby.gemspec +23 -0
- data/lib/fleet.rb +15 -0
- data/lib/fleet/client.rb +119 -0
- data/lib/fleet/client/machines.rb +18 -0
- data/lib/fleet/client/state.rb +18 -0
- data/lib/fleet/client/unit.rb +33 -0
- data/lib/fleet/configuration.rb +39 -0
- data/lib/fleet/connection.rb +33 -0
- data/lib/fleet/error.rb +41 -0
- data/lib/fleet/request.rb +73 -0
- data/lib/fleet/service_definition.rb +39 -0
- data/lib/fleet/version.rb +3 -0
- data/spec/fleet/client/machines_spec.rb +25 -0
- data/spec/fleet/client/state_spec.rb +25 -0
- data/spec/fleet/client/unit_spec.rb +86 -0
- data/spec/fleet/client_spec.rb +296 -0
- data/spec/fleet/configuration_spec.rb +46 -0
- data/spec/fleet/connection_spec.rb +80 -0
- data/spec/fleet/error_spec.rb +23 -0
- data/spec/fleet/request_spec.rb +117 -0
- data/spec/fleet/service_definition_spec.rb +38 -0
- data/spec/fleet_spec.rb +81 -0
- data/spec/spec_helper.rb +9 -0
- metadata +157 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
module Fleet
|
2
|
+
class ServiceDefinition
|
3
|
+
|
4
|
+
def initialize(service_def={})
|
5
|
+
@service_def = service_def
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_unit(name)
|
9
|
+
{
|
10
|
+
'name' => name,
|
11
|
+
'options' => options
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def options
|
18
|
+
@service_def.each_with_object([]) do |(section, options), h|
|
19
|
+
options.each do |name, value|
|
20
|
+
if value.is_a?(Enumerable)
|
21
|
+
value.each do |v|
|
22
|
+
h << {
|
23
|
+
'section' => section,
|
24
|
+
'name' => name,
|
25
|
+
'value' => v
|
26
|
+
}
|
27
|
+
end
|
28
|
+
else
|
29
|
+
h << {
|
30
|
+
'section' => section,
|
31
|
+
'name' => name,
|
32
|
+
'value' => value
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Fleet::Client::Machines do
|
4
|
+
subject { Fleet::Client.new }
|
5
|
+
|
6
|
+
let(:response) { double(:response) }
|
7
|
+
|
8
|
+
describe '#list_machines' do
|
9
|
+
before do
|
10
|
+
allow(subject).to receive(:get).and_return(response)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'GETs the Fleet machines key' do
|
14
|
+
expect(subject).to receive(:get)
|
15
|
+
.with('fleet/v1/machines')
|
16
|
+
.and_return(response)
|
17
|
+
|
18
|
+
subject.list_machines
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'returns the job response' do
|
22
|
+
expect(subject.list_machines).to eql(response)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Fleet::Client::State do
|
4
|
+
subject { Fleet::Client.new }
|
5
|
+
|
6
|
+
let(:response) { double(:response) }
|
7
|
+
|
8
|
+
describe '#list_states' do
|
9
|
+
before do
|
10
|
+
allow(subject).to receive(:get).and_return(response)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'GETs the state resource' do
|
14
|
+
expect(subject).to receive(:get)
|
15
|
+
.with('fleet/v1/state')
|
16
|
+
.and_return(response)
|
17
|
+
|
18
|
+
subject.list_states
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'returns the state response' do
|
22
|
+
expect(subject.list_states).to eql(response)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Fleet::Client::Unit do
|
4
|
+
subject { Fleet::Client.new }
|
5
|
+
|
6
|
+
let(:response) { double(:response) }
|
7
|
+
|
8
|
+
describe '#list_units' do
|
9
|
+
before do
|
10
|
+
allow(subject).to receive(:get).and_return(response)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'GETs all Fleet units' do
|
14
|
+
expect(subject).to receive(:get)
|
15
|
+
.with('fleet/v1/units')
|
16
|
+
.and_return(response)
|
17
|
+
|
18
|
+
subject.list_units
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'returns the unit response' do
|
22
|
+
expect(subject.list_units).to eql(response)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#get_unit' do
|
27
|
+
let(:name) { 'foo.service' }
|
28
|
+
|
29
|
+
before do
|
30
|
+
allow(subject).to receive(:get).and_return(response)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'GETs the Fleet unit' do
|
34
|
+
expect(subject).to receive(:get)
|
35
|
+
.with("fleet/v1/units/#{name}")
|
36
|
+
.and_return(response)
|
37
|
+
|
38
|
+
subject.get_unit(name)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'returns the unit response' do
|
42
|
+
expect(subject.get_unit(name)).to eql(response)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#create_unit' do
|
47
|
+
let(:name) { 'foo.service' }
|
48
|
+
let(:options) { { exec_start: '/bin/bash' } }
|
49
|
+
|
50
|
+
before do
|
51
|
+
allow(subject).to receive(:put).and_return(response)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'PUTs the unit def to the Fleet unit key' do
|
55
|
+
expect(subject).to receive(:put)
|
56
|
+
.with("fleet/v1/units/#{name}", options)
|
57
|
+
.and_return(response)
|
58
|
+
|
59
|
+
subject.create_unit(name, options)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'returns the unit response' do
|
63
|
+
expect(subject.create_unit(name, options)).to eql(response)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '#delete_unit' do
|
68
|
+
let(:name) { 'foo.service' }
|
69
|
+
|
70
|
+
before do
|
71
|
+
allow(subject).to receive(:delete).and_return(response)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'DELETEs the named Fleet unit' do
|
75
|
+
expect(subject).to receive(:delete)
|
76
|
+
.with("fleet/v1/units/#{name}")
|
77
|
+
.and_return(response)
|
78
|
+
|
79
|
+
subject.delete_unit(name)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'returns the job response' do
|
83
|
+
expect(subject.delete_unit(name)).to eql(response)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,296 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'fleet/service_definition'
|
4
|
+
|
5
|
+
describe Fleet::Client do
|
6
|
+
|
7
|
+
describe '#initialize' do
|
8
|
+
|
9
|
+
after do
|
10
|
+
Fleet.reset
|
11
|
+
end
|
12
|
+
|
13
|
+
Fleet::Configuration::VALID_OPTIONS_KEYS.each do |option|
|
14
|
+
it "inherits default #{option} value from Panamax" do
|
15
|
+
client = Fleet::Client.new
|
16
|
+
expect(client.send(option)).to eql(Fleet.send(option))
|
17
|
+
end
|
18
|
+
|
19
|
+
it "overrides default for #{option} when specified" do
|
20
|
+
client = Fleet::Client.new(option => :foo)
|
21
|
+
expect(client.send(option)).to eql(:foo)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#list' do
|
27
|
+
|
28
|
+
let(:machine_list) do
|
29
|
+
{
|
30
|
+
'machines' => [
|
31
|
+
{ 'id' => '123', 'primaryIP' => '1.1.1.1' }
|
32
|
+
]
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
let(:state_list) do
|
37
|
+
{
|
38
|
+
'states' => [
|
39
|
+
{
|
40
|
+
'hash' => 'abc123',
|
41
|
+
'machineID' => '123',
|
42
|
+
'name' => 'foo.service',
|
43
|
+
'systemdActiveState' => 'b',
|
44
|
+
'systemdLoadState' => 'a',
|
45
|
+
'systemdSubState' => 'c'
|
46
|
+
}
|
47
|
+
]
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
before do
|
52
|
+
allow(subject).to receive(:list_machines).and_return(machine_list)
|
53
|
+
allow(subject).to receive(:list_states).and_return(state_list)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'looks-up the list of machines' do
|
57
|
+
expect(subject).to receive(:list_machines)
|
58
|
+
subject.list
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'looks-up the list of job states' do
|
62
|
+
expect(subject).to receive(:list_states)
|
63
|
+
subject.list
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'returns the list of units' do
|
67
|
+
expected = [{
|
68
|
+
name: 'foo.service',
|
69
|
+
load_state: 'a',
|
70
|
+
active_state: 'b',
|
71
|
+
sub_state: 'c',
|
72
|
+
machine_id: '123',
|
73
|
+
machine_ip: '1.1.1.1'
|
74
|
+
}]
|
75
|
+
|
76
|
+
expect(subject.list).to eq(expected)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe '#submit' do
|
81
|
+
let(:name) { 'foo.service' }
|
82
|
+
let(:service_def) { { 'Unit' => { 'Description' => 'bar' } } }
|
83
|
+
let(:sd) { Fleet::ServiceDefinition.new(service_def) }
|
84
|
+
let(:response) { double(:response) }
|
85
|
+
|
86
|
+
before do
|
87
|
+
allow(subject).to receive(:create_unit).and_return(response)
|
88
|
+
allow(Fleet::ServiceDefinition).to receive(:new).and_return(sd)
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'invokes #create_unit' do
|
92
|
+
expect(subject).to receive(:create_unit)
|
93
|
+
.with(name, sd.to_unit(name))
|
94
|
+
|
95
|
+
subject.submit(name, service_def)
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'returns the #create_unit response' do
|
99
|
+
r = subject.submit(name, service_def)
|
100
|
+
expect(r).to eq response
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'when #create_unit raises PreconditionFailed' do
|
104
|
+
|
105
|
+
before do
|
106
|
+
allow(subject).to receive(:create_unit)
|
107
|
+
.and_raise(Fleet::PreconditionFailed.new('boom'))
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'does not blow up' do
|
111
|
+
expect { subject.submit(name, service_def) }.to_not raise_error
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'when #create_unit raises something other than PreconditionFailed' do
|
116
|
+
|
117
|
+
before do
|
118
|
+
allow(subject).to receive(:create_unit)
|
119
|
+
.and_raise(Fleet::BadRequest.new('boom'))
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'propagates the error' do
|
123
|
+
expect { subject.submit(name, service_def) }.to(raise_error(Fleet::BadRequest))
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context 'when the supplied name is invalid' do
|
128
|
+
|
129
|
+
let(:name) { 'foo!.service' }
|
130
|
+
|
131
|
+
it 'raises an ArgumentError' do
|
132
|
+
expect { subject.submit(name, nil) }.to raise_error(ArgumentError, /only contain/)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe '#load' do
|
138
|
+
|
139
|
+
let(:name) { 'foo.service' }
|
140
|
+
let(:response) { double(:response) }
|
141
|
+
|
142
|
+
before do
|
143
|
+
allow(subject).to receive(:update_unit).and_return(response)
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'does NOT invoke #submit' do
|
147
|
+
expect(subject).not_to receive(:submit)
|
148
|
+
subject.load(name)
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'invokes #update' do
|
152
|
+
expect(subject).to receive(:update_unit)
|
153
|
+
.with(name, { 'desiredState' => 'loaded', 'name' => name })
|
154
|
+
|
155
|
+
subject.load(name)
|
156
|
+
end
|
157
|
+
|
158
|
+
context 'when a service definition is provided' do
|
159
|
+
|
160
|
+
let(:service_def) { { 'Unit' => { 'Description' => 'bar' } } }
|
161
|
+
|
162
|
+
before do
|
163
|
+
allow(subject).to receive(:submit)
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'invokes #load' do
|
167
|
+
expect(subject).to receive(:submit)
|
168
|
+
subject.load(name, service_def)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
describe '#start' do
|
175
|
+
let(:service_name) { 'foo.service' }
|
176
|
+
|
177
|
+
before do
|
178
|
+
allow(subject).to receive(:update_unit).and_return(nil)
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'invokes #update_unit' do
|
182
|
+
expect(subject).to receive(:update_unit)
|
183
|
+
.with(service_name, { 'desiredState' => 'launched', 'name' => service_name })
|
184
|
+
|
185
|
+
subject.start(service_name)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe '#stop' do
|
190
|
+
let(:service_name) { 'foo.service' }
|
191
|
+
|
192
|
+
before do
|
193
|
+
allow(subject).to receive(:update_unit).and_return(nil)
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'invokes #update_unit' do
|
197
|
+
expect(subject).to receive(:update_unit)
|
198
|
+
.with(service_name, { 'desiredState' => 'loaded', 'name' => service_name })
|
199
|
+
|
200
|
+
subject.stop(service_name)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe '#unload' do
|
205
|
+
let(:service_name) { 'foo.service' }
|
206
|
+
|
207
|
+
before do
|
208
|
+
allow(subject).to receive(:update_unit).and_return(nil)
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'invokes #update_unit' do
|
212
|
+
expect(subject).to receive(:update_unit)
|
213
|
+
.with(service_name, { 'desiredState' => 'inactive', 'name' => service_name })
|
214
|
+
|
215
|
+
subject.unload(service_name)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
describe '#destroy' do
|
220
|
+
let(:service_name) { 'foo.service' }
|
221
|
+
|
222
|
+
before do
|
223
|
+
allow(subject).to receive(:delete_unit).and_return(nil)
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'invokes #delete_job' do
|
227
|
+
|
228
|
+
expect(subject).to receive(:delete_unit)
|
229
|
+
.with(service_name)
|
230
|
+
.and_return(nil)
|
231
|
+
|
232
|
+
subject.destroy(service_name)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
describe '#status' do
|
237
|
+
|
238
|
+
let(:service_name) { 'foo.service' }
|
239
|
+
|
240
|
+
let(:fleet_state) do
|
241
|
+
{ 'currentState' => 'launched' }
|
242
|
+
end
|
243
|
+
|
244
|
+
before do
|
245
|
+
allow(subject).to receive(:get_unit).and_return(fleet_state)
|
246
|
+
end
|
247
|
+
|
248
|
+
it 'retrieves service state from the fleet client' do
|
249
|
+
expect(subject).to receive(:get_unit).with(service_name)
|
250
|
+
subject.status(service_name)
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'returns the symbolized state' do
|
254
|
+
expect(subject.status(service_name)).to eq(:launched)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
describe '#get_unit_state' do
|
259
|
+
|
260
|
+
let(:service_name) { 'foo.service' }
|
261
|
+
|
262
|
+
let(:states) do
|
263
|
+
{ 'states' => [] }
|
264
|
+
end
|
265
|
+
|
266
|
+
before do
|
267
|
+
allow(subject).to receive(:list_states).and_return(states)
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'retrieves the states from the fleet API' do
|
271
|
+
expect(subject).to receive(:list_states).with({ unitName: service_name })
|
272
|
+
subject.get_unit_state(service_name)
|
273
|
+
end
|
274
|
+
|
275
|
+
context 'when unit is found' do
|
276
|
+
|
277
|
+
let(:states) do
|
278
|
+
{ 'states' => [{ 'name' => 'foo.service' }, {}] }
|
279
|
+
end
|
280
|
+
|
281
|
+
it 'returns the first matching state hash' do
|
282
|
+
expect(subject.get_unit_state(service_name)).to eq(states['states'].first)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
context 'when unit is NOT found' do
|
287
|
+
|
288
|
+
let(:states) { {} }
|
289
|
+
|
290
|
+
it 'returns the first matching state hash' do
|
291
|
+
expect { subject.get_unit_state(service_name) }.to(
|
292
|
+
raise_error(Fleet::NotFound))
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|