fleet-api 0.9.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,56 +1,39 @@
1
- require 'digest/sha1'
2
-
3
1
  module Fleet
4
2
  class ServiceDefinition
5
3
 
6
- attr_reader :name
7
-
8
- def initialize(name, service_def={})
9
- @name = name
4
+ def initialize(service_def={})
10
5
  @service_def = service_def
11
6
  end
12
7
 
13
- def to_unit
14
- { 'Raw' => raw }
15
- end
16
-
17
- def to_job
8
+ def to_unit(name)
18
9
  {
19
- 'Name' => name,
20
- 'UnitHash' => sha1_byte_array
10
+ 'name' => name,
11
+ 'options' => options
21
12
  }
22
13
  end
23
14
 
24
- def sha1
25
- Digest::SHA1.hexdigest raw
26
- end
27
-
28
15
  private
29
16
 
30
- def raw
31
- raw_string = ''
32
-
33
- @service_def.each do |heading, section|
34
- raw_string += "[#{heading}]\n"
35
-
36
- if section.is_a?(Enumerable)
37
- section.each do |key, value|
38
- if value.is_a?(Enumerable)
39
- value.each { |command| raw_string += "#{key}=#{command}\n" }
40
- else
41
- raw_string += "#{key}=#{value}\n"
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
+ }
42
27
  end
28
+ else
29
+ h << {
30
+ 'section' => section,
31
+ 'name' => name,
32
+ 'value' => value
33
+ }
43
34
  end
44
35
  end
45
-
46
- raw_string += "\n"
47
36
  end
48
-
49
- raw_string.chomp
50
- end
51
-
52
- def sha1_byte_array
53
- Digest::SHA1.digest(raw).unpack('C20')
54
37
  end
55
38
  end
56
39
  end
data/lib/fleet/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Fleet
2
- VERSION = '0.9.0'.freeze unless defined?(Fleet::VERSION)
2
+ VERSION = '1.0.0'.freeze unless defined?(Fleet::VERSION)
3
3
  end
@@ -13,9 +13,8 @@ describe Fleet::Client::Machines do
13
13
  end
14
14
 
15
15
  it 'GETs the Fleet machines key' do
16
- opts = { consistent: true, recursive: true, sorted: true }
17
16
  expect(subject).to receive(:get)
18
- .with('v2/keys/_coreos.com/fleet/machines', opts)
17
+ .with('fleet/v1/machines', nil)
19
18
  .and_return(response)
20
19
 
21
20
  subject.list_machines
@@ -12,10 +12,9 @@ describe Fleet::Client::State do
12
12
  allow(subject).to receive(:get).and_return(response)
13
13
  end
14
14
 
15
- it 'GETs the Fleet state key' do
16
- opts = { consistent: true, recursive: true, sorted: false }
15
+ it 'GETs the state resource' do
17
16
  expect(subject).to receive(:get)
18
- .with('v2/keys/_coreos.com/fleet/state', opts)
17
+ .with('fleet/v1/state', {})
19
18
  .and_return(response)
20
19
 
21
20
  subject.list_states
@@ -25,26 +24,4 @@ describe Fleet::Client::State do
25
24
  expect(subject.list_states).to eql(response)
26
25
  end
27
26
  end
28
-
29
- describe '#get_state' do
30
-
31
- let(:service_name) { 'foo.service' }
32
-
33
- before do
34
- allow(subject).to receive(:get).and_return(response)
35
- end
36
-
37
- it 'GETs the named Fleet state key' do
38
- opts = { consistent: true, recursive: true, sorted: false }
39
- expect(subject).to receive(:get)
40
- .with("v2/keys/_coreos.com/fleet/state/#{service_name}", opts)
41
- .and_return(response)
42
-
43
- subject.get_state(service_name)
44
- end
45
-
46
- it 'returns the state response' do
47
- expect(subject.get_state(service_name)).to eql(response)
48
- end
49
- end
50
27
  end
@@ -12,65 +12,80 @@ describe Fleet::Client::Unit do
12
12
  allow(subject).to receive(:get).and_return(response)
13
13
  end
14
14
 
15
- it 'GETs the Fleet unit key' do
15
+ it 'GETs all Fleet units' do
16
16
  expect(subject).to receive(:get)
17
- .with("v2/keys/_coreos.com/fleet/unit")
17
+ .with("fleet/v1/units")
18
18
  .and_return(response)
19
19
 
20
20
  subject.list_units
21
21
  end
22
22
 
23
- it 'returns the job response' do
23
+ it 'returns the unit response' do
24
24
  expect(subject.list_units).to eql(response)
25
25
  end
26
26
  end
27
27
 
28
+ describe '#get_unit' do
29
+
30
+ let(:name) { 'foo.service' }
31
+
32
+ before do
33
+ allow(subject).to receive(:get).and_return(response)
34
+ end
35
+
36
+ it 'GETs the Fleet unit' do
37
+ expect(subject).to receive(:get)
38
+ .with("fleet/v1/units/#{name}")
39
+ .and_return(response)
40
+
41
+ subject.get_unit(name)
42
+ end
43
+
44
+ it 'returns the unit response' do
45
+ expect(subject.get_unit(name)).to eql(response)
46
+ end
47
+ end
48
+
28
49
  describe '#create_unit' do
29
50
 
30
- let(:sha) { '33ef9ba9029c' }
31
- let(:unit_def) { { exec_start: '/bin/bash' } }
51
+ let(:name) { 'foo.service' }
52
+ let(:options) { { exec_start: '/bin/bash' } }
32
53
 
33
54
  before do
34
55
  allow(subject).to receive(:put).and_return(response)
35
56
  end
36
57
 
37
58
  it 'PUTs the unit def to the Fleet unit key' do
38
- opts = {
39
- querystring: { 'prevExist' => false },
40
- body: { value: unit_def.to_json }
41
- }
42
-
43
59
  expect(subject).to receive(:put)
44
- .with("v2/keys/_coreos.com/fleet/unit/#{sha}", opts)
60
+ .with("fleet/v1/units/#{name}", options)
45
61
  .and_return(response)
46
62
 
47
- subject.create_unit(sha, unit_def)
63
+ subject.create_unit(name, options)
48
64
  end
49
65
 
50
- it 'returns the job response' do
51
- expect(subject.create_unit(sha, unit_def)).to eql(response)
66
+ it 'returns the unit response' do
67
+ expect(subject.create_unit(name, options)).to eql(response)
52
68
  end
53
69
  end
54
70
 
55
71
  describe '#delete_unit' do
56
72
 
57
- let(:sha) { '33ef9ba9029c' }
73
+ let(:name) { 'foo.service' }
58
74
 
59
75
  before do
60
76
  allow(subject).to receive(:delete).and_return(response)
61
77
  end
62
78
 
63
- it 'DELETEs the named Fleet unit key' do
64
- opts = { dir: false, recursive: false }
79
+ it 'DELETEs the named Fleet unit' do
65
80
  expect(subject).to receive(:delete)
66
- .with("v2/keys/_coreos.com/fleet/unit/#{sha}", opts)
81
+ .with("fleet/v1/units/#{name}")
67
82
  .and_return(response)
68
83
 
69
- subject.delete_unit(sha)
84
+ subject.delete_unit(name)
70
85
  end
71
86
 
72
87
  it 'returns the job response' do
73
- expect(subject.delete_unit(sha)).to eql(response)
88
+ expect(subject.delete_unit(name)).to eql(response)
74
89
  end
75
90
  end
76
91
  end
@@ -27,28 +27,24 @@ describe Fleet::Client do
27
27
 
28
28
  let(:machine_list) do
29
29
  {
30
- 'node' => {
31
- 'nodes' => [
32
- {
33
- 'nodes' => [
34
- { 'value' => '{"ID":"123","PublicIP":"1.1.1.1"}' }
35
- ]
36
- }
37
- ]
38
- }
30
+ 'machines' => [
31
+ { 'id' => '123', 'primaryIP' => '1.1.1.1' }
32
+ ]
39
33
  }
40
34
  end
41
35
 
42
36
  let(:state_list) do
43
37
  {
44
- 'node' => {
45
- 'nodes' => [
46
- {
47
- 'key' => '/_coreos.com/fleet/state/foo.service',
48
- 'value' => '{"loadState":"a","activeState":"b","subState":"c","machineState":{"ID":"123"}}'
49
- }
50
- ]
51
- }
38
+ 'states' => [
39
+ {
40
+ 'hash' => 'abc123',
41
+ 'machineID' => '123',
42
+ 'name' => 'foo.service',
43
+ 'systemdActiveState' => 'b',
44
+ 'systemdLoadState' => 'a',
45
+ 'systemdSubState' => 'c'
46
+ }
47
+ ]
52
48
  }
53
49
  end
54
50
 
@@ -69,8 +65,8 @@ describe Fleet::Client do
69
65
 
70
66
  it 'returns the list of units' do
71
67
  expected = [{
72
- name: 'foo.service',
73
- load_state: 'a',
68
+ name: 'foo.service',
69
+ load_state: 'a',
74
70
  active_state: 'b',
75
71
  sub_state: 'c',
76
72
  machine_id: '123',
@@ -85,51 +81,25 @@ describe Fleet::Client do
85
81
 
86
82
  let(:name) { 'foo.service' }
87
83
  let(:service_def) { { 'Unit' => { 'Description' => 'bar' } } }
88
- let(:sd) { Fleet::ServiceDefinition.new(name, service_def) }
84
+ let(:sd) { Fleet::ServiceDefinition.new(service_def) }
85
+ let(:response) { double(:response) }
89
86
 
90
87
  context 'when a service definition is provided' do
91
88
  before do
92
- allow(subject).to receive(:create_unit).and_return(nil)
93
- allow(subject).to receive(:create_job).and_return(nil)
94
- allow(subject).to receive(:update_job_target_state).and_return(nil)
89
+ allow(subject).to receive(:create_unit).and_return(response)
95
90
  allow(Fleet::ServiceDefinition).to receive(:new).and_return(sd)
96
91
  end
97
92
 
98
93
  it 'invokes #create_unit' do
99
94
  expect(subject).to receive(:create_unit)
100
- .with(sd.sha1, sd.to_unit)
95
+ .with(name, sd.to_unit(name))
101
96
 
102
97
  subject.load(name, service_def)
103
98
  end
104
99
 
105
- it 'invokes #create_job' do
106
- expect(subject).to receive(:create_job)
107
- .with(sd.name, sd.to_job)
108
-
109
- subject.load(name, service_def)
110
- end
111
-
112
- it 'invokes #update_job_target_state' do
113
- expect(subject).to receive(:update_job_target_state)
114
- .with(sd.name, :loaded)
115
-
116
- subject.load(name, service_def)
117
- end
118
-
119
- context 'when sync=true is set' do
120
-
121
- let(:fleet_state) do
122
- { 'node' => { 'value' => '{ "loadState": "loaded" }' } }
123
- end
124
-
125
- before do
126
- allow(subject).to receive(:get_state).and_return(fleet_state)
127
- end
128
-
129
- it 'checks the job state' do
130
- expect(subject).to receive(:get_state).with(sd.name)
131
- subject.load(name, service_def, sync=true)
132
- end
100
+ it 'returns the #create_unit response' do
101
+ r = subject.load(name, service_def)
102
+ expect(r).to eq response
133
103
  end
134
104
 
135
105
  context 'when #create_unit raises PreconditionFailed' do
@@ -155,36 +125,12 @@ describe Fleet::Client do
155
125
  expect { subject.load(name, service_def) }.to(raise_error(Fleet::BadRequest))
156
126
  end
157
127
  end
158
-
159
- context 'when #create_job raises PreconditionFailed' do
160
-
161
- before do
162
- allow(subject).to receive(:create_job)
163
- .and_raise(Fleet::PreconditionFailed.new('boom'))
164
- end
165
-
166
- it 'does not blow up' do
167
- expect { subject.load(name, service_def) }.to_not raise_error
168
- end
169
- end
170
-
171
- context 'when #create_job raises something other than PreconditionFailed' do
172
-
173
- before do
174
- allow(subject).to receive(:create_job)
175
- .and_raise(Fleet::BadRequest.new('boom'))
176
- end
177
-
178
- it 'propagates the error' do
179
- expect { subject.load(name, service_def) }.to(raise_error(Fleet::BadRequest))
180
- end
181
- end
182
128
  end
183
129
 
184
130
  context 'when no service definition is provided' do
185
131
 
186
132
  before do
187
- allow(subject).to receive(:update_job_target_state).and_return(nil)
133
+ allow(subject).to receive(:update_unit).and_return(response)
188
134
  end
189
135
 
190
136
  it 'does NOT invoke #create_unit' do
@@ -192,33 +138,12 @@ describe Fleet::Client do
192
138
  subject.load(name)
193
139
  end
194
140
 
195
- it 'does NOT invoke #create_job' do
196
- expect(subject).to_not receive(:create_job)
197
- subject.load(name)
198
- end
199
-
200
- it 'invokes #update_job_target_state' do
201
- expect(subject).to receive(:update_job_target_state)
202
- .with(sd.name, :loaded)
141
+ it 'invokes #update' do
142
+ expect(subject).to receive(:update_unit)
143
+ .with(name, { 'desiredState' => 'loaded', 'name' => name })
203
144
 
204
145
  subject.load(name)
205
146
  end
206
-
207
- context 'when sync=true is set' do
208
-
209
- let(:fleet_state) do
210
- { 'node' => { 'value' => '{ "loadState": "loaded" }' } }
211
- end
212
-
213
- before do
214
- allow(subject).to receive(:get_state).and_return(fleet_state)
215
- end
216
-
217
- it 'checks the job state' do
218
- expect(subject).to receive(:get_state).with(sd.name)
219
- subject.load(name, nil, sync=true)
220
- end
221
- end
222
147
  end
223
148
 
224
149
  context 'when the supplied name is invalid' do
@@ -235,12 +160,12 @@ describe Fleet::Client do
235
160
  let(:service_name) { 'foo.service' }
236
161
 
237
162
  before do
238
- allow(subject).to receive(:update_job_target_state)
163
+ allow(subject).to receive(:update_unit).and_return(nil)
239
164
  end
240
165
 
241
- it 'invokes #update_job_target_state' do
242
- expect(subject).to receive(:update_job_target_state)
243
- .with(service_name, :launched)
166
+ it 'invokes #update_unit' do
167
+ expect(subject).to receive(:update_unit)
168
+ .with(service_name, { 'desiredState' => 'launched', 'name' => service_name })
244
169
 
245
170
  subject.start(service_name)
246
171
  end
@@ -250,111 +175,47 @@ describe Fleet::Client do
250
175
  let(:service_name) { 'foo.service' }
251
176
 
252
177
  before do
253
- allow(subject).to receive(:update_job_target_state)
178
+ allow(subject).to receive(:update_unit).and_return(nil)
254
179
  end
255
180
 
256
- it 'invokes #update_job_target_state' do
257
- expect(subject).to receive(:update_job_target_state)
258
- .with(service_name, :loaded)
181
+ it 'invokes #update_unit' do
182
+ expect(subject).to receive(:update_unit)
183
+ .with(service_name, { 'desiredState' => 'loaded', 'name' => service_name })
259
184
 
260
185
  subject.stop(service_name)
261
186
  end
262
-
263
- context 'when sync=true is set' do
264
-
265
- let(:fleet_state) do
266
- { 'node' => { 'value' => '{ "load_state": "loaded" }' } }
267
- end
268
-
269
- before do
270
- allow(subject).to receive(:get_state).and_return(fleet_state)
271
- end
272
-
273
- it 'checks the job state' do
274
- expect(subject).to receive(:get_state).with(service_name)
275
- subject.stop(service_name, sync=true)
276
- end
277
- end
278
187
  end
279
188
 
280
189
  describe '#unload' do
281
190
  let(:service_name) { 'foo.service' }
282
191
 
283
192
  before do
284
- allow(subject).to receive(:update_job_target_state)
193
+ allow(subject).to receive(:update_unit).and_return(nil)
285
194
  end
286
195
 
287
- it 'invokes #update_job_target_state' do
288
- expect(subject).to receive(:update_job_target_state)
289
- .with(service_name, :inactive)
196
+ it 'invokes #update_unit' do
197
+ expect(subject).to receive(:update_unit)
198
+ .with(service_name, { 'desiredState' => 'inactive', 'name' => service_name })
290
199
 
291
200
  subject.unload(service_name)
292
201
  end
293
-
294
- context 'when sync=true is set' do
295
-
296
- before do
297
- allow(subject).to receive(:get_state).and_raise(Fleet::NotFound, 'boom')
298
- end
299
-
300
- it 'checks the job state' do
301
- expect(subject).to receive(:get_state).with(service_name)
302
- subject.unload(service_name, sync=true)
303
- end
304
-
305
- context 'when the unload state cannot be achieved' do
306
-
307
- let(:fleet_state) do
308
- { 'node' => { 'value' => '{ "load_state": "loaded" }' } }
309
- end
310
-
311
- before do
312
- allow(subject).to receive(:get_state).and_return(fleet_state)
313
- allow(subject).to receive(:sleep)
314
- end
315
-
316
- it 're-checks the state 20 times' do
317
- expect(subject).to receive(:get_state).exactly(20).times
318
- subject.unload(service_name, sync=true) rescue nil
319
- end
320
-
321
- it 'raises an error' do
322
- expect do
323
- subject.unload(service_name, sync=true)
324
- end.to raise_error(Fleet::Error)
325
- end
326
-
327
- end
328
- end
329
202
  end
330
203
 
331
204
  describe '#destroy' do
332
205
  let(:service_name) { 'foo.service' }
333
206
 
334
207
  before do
335
- allow(subject).to receive(:delete_job).and_return(nil)
208
+ allow(subject).to receive(:delete_unit).and_return(nil)
336
209
  end
337
210
 
338
211
  it 'invokes #delete_job' do
339
212
 
340
- expect(subject).to receive(:delete_job)
213
+ expect(subject).to receive(:delete_unit)
341
214
  .with(service_name)
342
215
  .and_return(nil)
343
216
 
344
217
  subject.destroy(service_name)
345
218
  end
346
-
347
- context 'when sync=true is set' do
348
-
349
- before do
350
- allow(subject).to receive(:get_state).and_raise(Fleet::NotFound, 'boom')
351
- end
352
-
353
- it 'checks the job state' do
354
- expect(subject).to receive(:get_state).with(service_name)
355
- subject.destroy(service_name, sync=true)
356
- end
357
- end
358
219
  end
359
220
 
360
221
  describe '#status' do
@@ -362,20 +223,59 @@ describe Fleet::Client do
362
223
  let(:service_name) { 'foo.service' }
363
224
 
364
225
  let(:fleet_state) do
365
- { 'node' => { 'value' => '{"load": "loaded", "run": "running"}' } }
226
+ { 'currentState' => 'launched' }
366
227
  end
367
228
 
368
229
  before do
369
- allow(subject).to receive(:get_state).and_return(fleet_state)
230
+ allow(subject).to receive(:get_unit).and_return(fleet_state)
370
231
  end
371
232
 
372
233
  it 'retrieves service state from the fleet client' do
373
- expect(subject).to receive(:get_state).with(service_name)
234
+ expect(subject).to receive(:get_unit).with(service_name)
374
235
  subject.status(service_name)
375
236
  end
376
237
 
377
- it 'returns the state hash w/ normalized keys' do
378
- expect(subject.status(service_name)).to eq(load: 'loaded', run: 'running')
238
+ it 'returns the symbolized state' do
239
+ expect(subject.status(service_name)).to eq(:launched)
240
+ end
241
+ end
242
+
243
+ describe '#get_unit_state' do
244
+
245
+ let(:service_name) { 'foo.service' }
246
+
247
+ let(:states) do
248
+ { 'states' => [] }
249
+ end
250
+
251
+ before do
252
+ allow(subject).to receive(:list_states).and_return(states)
253
+ end
254
+
255
+ it 'retrieves the states from the fleet API' do
256
+ expect(subject).to receive(:list_states).with({ unitName: service_name })
257
+ subject.get_unit_state(service_name)
258
+ end
259
+
260
+ context 'when unit is found' do
261
+
262
+ let(:states) do
263
+ { 'states' => [{ 'name' => 'foo.service' }, {}] }
264
+ end
265
+
266
+ it 'returns the first matching state hash' do
267
+ expect(subject.get_unit_state(service_name)).to eq(states['states'].first)
268
+ end
269
+ end
270
+
271
+ context 'when unit is NOT found' do
272
+
273
+ let(:states) { {} }
274
+
275
+ it 'returns the first matching state hash' do
276
+ expect { subject.get_unit_state(service_name) }.to(
277
+ raise_error(Fleet::NotFound))
278
+ end
379
279
  end
380
280
  end
381
281
  end
@@ -13,15 +13,15 @@ describe Fleet::Configuration do
13
13
 
14
14
  describe 'default values' do
15
15
 
16
- describe 'adapter' do
17
- it 'is matches DEFAULT_ADAPTER' do
18
- expect(subject.adapter).to eq Fleet::Configuration::DEFAULT_ADAPTER
16
+ describe 'fleet_api_url' do
17
+ it 'is matches DEFAULT_FLEET_API_URL' do
18
+ expect(subject.fleet_api_url).to eq Fleet::Configuration::DEFAULT_FLEET_API_URL
19
19
  end
20
20
  end
21
21
 
22
- describe 'fleet_api_url' do
23
- it 'is matches DEFAULT_ETCD_API_URL' do
24
- expect(subject.fleet_api_url).to eq Fleet::Configuration::DEFAULT_ETCD_API_URL
22
+ describe 'fleet_api_version' do
23
+ it 'is matches DEFAULT_FLEET_API_VERSION' do
24
+ expect(subject.fleet_api_version).to eq Fleet::Configuration::DEFAULT_FLEET_API_VERSION
25
25
  end
26
26
  end
27
27