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.
@@ -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,3 @@
1
+ module Fleet
2
+ VERSION = '1.2.0'.freeze unless defined?(Fleet::VERSION)
3
+ 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