fleet-api 0.0.1 → 0.5.1
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/.travis.yml +5 -0
- data/Gemfile.lock +3 -3
- data/LICENSE +1 -1
- data/README.md +113 -0
- data/fleet-api.gemspec +4 -2
- data/lib/fleet/client.rb +18 -16
- data/lib/fleet/version.rb +1 -1
- data/spec/fleet/client_spec.rb +86 -54
- metadata +13 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ded5145e7361d7b0e441c91f3811a783a11ec94e
|
4
|
+
data.tar.gz: b544579b3f35b5764b4a863618d0b96b38c68442
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b748b4fc6014400b8228415d432045d379ba191469a80e08c534bf16180126ee4a37f1d6b074043d2d86aa9e82eaac4fcd12a5a907fb94256028a87562d6cf08
|
7
|
+
data.tar.gz: 512d2230352cfa529fbeb4a7c412fab72bde79b68660f6047d1fe5f81a76a25464b85ee3d3939c60b5d56357927a86d1c3e8a5a8fc83547f6e80c1a2cae39dce
|
data/.travis.yml
ADDED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
fleet-api (0.0
|
4
|
+
fleet-api (0.5.0)
|
5
5
|
faraday (= 0.8.9)
|
6
6
|
faraday_middleware (= 0.9.0)
|
7
7
|
|
@@ -44,5 +44,5 @@ DEPENDENCIES
|
|
44
44
|
fleet-api!
|
45
45
|
rake
|
46
46
|
rspec (~> 3.0)
|
47
|
-
simplecov
|
48
|
-
simplecov-rcov
|
47
|
+
simplecov (~> 0.9.0)
|
48
|
+
simplecov-rcov (~> 0.2.3)
|
data/LICENSE
CHANGED
@@ -186,7 +186,7 @@ Apache License
|
|
186
186
|
same "printed page" as the copyright notice for easier
|
187
187
|
identification within third-party archives.
|
188
188
|
|
189
|
-
Copyright
|
189
|
+
Copyright 2014 CenturyLink
|
190
190
|
|
191
191
|
Licensed under the Apache License, Version 2.0 (the "License");
|
192
192
|
you may not use this file except in compliance with the License.
|
data/README.md
CHANGED
@@ -1,2 +1,115 @@
|
|
1
1
|
fleet-api
|
2
2
|
=========
|
3
|
+
|
4
|
+
Provides a Ruby wrapper around the CoreOS Fleet API.
|
5
|
+
|
6
|
+
The client allows programmatic access to most of the *fleetctl* commands including the ability to load, start, stop, unload and destroy unit files.
|
7
|
+
|
8
|
+
At this point, there is no official Fleet API (though one is [in the works](https://github.com/coreos/fleet/blob/master/Documentation/api-v1-alpha.md)) so this library mimcs the behavior of the *fleetctl* command line tool and simply writes data to the [etcd]() key-value-store. The Fleet daemon reads data out of specific keys in etcd and processes it as appropiate.
|
9
|
+
|
10
|
+
As work on the actual Fleet API progresses, this library will be refactored to use the real API.
|
11
|
+
|
12
|
+
An alternative implementation is available in the [cloudspace/ruby-fleetctl](https://github.com/cloudspace/ruby-fleetctl) gem. The *ruby-fleetctl* gem takes a different approach and uses SSH to interact directly with the *fleetctl* binary to send commands. Our approach of writing directly to etcd cuts out the *fleetctl* middleman but is in more danger of being broken by future releases since we're effectively using a "private API".
|
13
|
+
|
14
|
+
The current version of the *fleet-api* gem is known to work with version 0.5.0 of Fleet which ships with the the current stable version of CoreOS (367.1.0)
|
15
|
+
|
16
|
+
### Installation
|
17
|
+
|
18
|
+
Install the gem directly:
|
19
|
+
|
20
|
+
gem install fleet-api
|
21
|
+
|
22
|
+
Alternatively, add this line to your application's Gemfile:
|
23
|
+
|
24
|
+
gem 'fleet-api', require: 'fleet'
|
25
|
+
|
26
|
+
|
27
|
+
### Usage
|
28
|
+
|
29
|
+
Configure the URL for the etcd API:
|
30
|
+
|
31
|
+
require 'fleet'
|
32
|
+
|
33
|
+
Fleet.configure do |fleet|
|
34
|
+
fleet.fleet_api_url = 'http://10.1.42.1:4001'
|
35
|
+
end
|
36
|
+
|
37
|
+
If you don't provide an explicit value for the `.fleet_api_url` attribute, it will default to using the value of the `FLEETCTL_ENDPOINT` environment variable.
|
38
|
+
|
39
|
+
**Note: since this Fleet API is not yet available in the stable version of CoreOS, the URL value provided must be the endpoint for the etcd API.**
|
40
|
+
|
41
|
+
#### Service Definitions
|
42
|
+
|
43
|
+
When submitting a service definition to the `Fleet::Client` you must convert your [unit file](http://www.freedesktop.org/software/systemd/man/systemd.unit.html) into a Ruby hash. Each section in the unit file is represented as a key/value pair in the hash where the key is the name of the section and the value is another hash containing all the statements for that section.
|
44
|
+
|
45
|
+
For example, look at the following unit file.
|
46
|
+
|
47
|
+
[Unit]
|
48
|
+
Description=Useless infinite loop
|
49
|
+
|
50
|
+
[Service]
|
51
|
+
ExecStart=/bin/bash -c "while true; do sleep 1; done"
|
52
|
+
|
53
|
+
This unit file would be represented as the following Ruby hash.
|
54
|
+
|
55
|
+
{
|
56
|
+
'Unit' => {
|
57
|
+
'Description' => 'Useless infinite loop'
|
58
|
+
},
|
59
|
+
'Service' => {
|
60
|
+
'ExecStart' => "/bin/bash -c \"while true; do sleep 1; done\""
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
#### Loading a Unit File
|
65
|
+
|
66
|
+
Equivalent of `fleetctl load`:
|
67
|
+
|
68
|
+
service = {
|
69
|
+
'Unit' => {
|
70
|
+
'Description' => 'Useless infinite loop'
|
71
|
+
},
|
72
|
+
'Service' => {
|
73
|
+
'ExecStart' => "/bin/bash -c \"while true; do sleep 1; done\""
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
client = Fleet.new
|
78
|
+
client.load('forever.service', service)
|
79
|
+
|
80
|
+
Note that the name you pass-in as the first parameter to the `.load` method should end in ".service"
|
81
|
+
|
82
|
+
#### Starting a Service
|
83
|
+
|
84
|
+
Equivalent of `fleetctl start`:
|
85
|
+
|
86
|
+
client = Fleet.new
|
87
|
+
client.start('forever.service')
|
88
|
+
|
89
|
+
#### Stopping a Service
|
90
|
+
|
91
|
+
Equivalent of `fleetctl stop`:
|
92
|
+
|
93
|
+
client = Fleet.new
|
94
|
+
client.stop('forever.service')
|
95
|
+
|
96
|
+
#### Unloading a Unit File
|
97
|
+
|
98
|
+
Equivalent of `fleetctl unload`:
|
99
|
+
|
100
|
+
client = Fleet.new
|
101
|
+
client.unload('forever.service')
|
102
|
+
|
103
|
+
#### Destroying a Service
|
104
|
+
|
105
|
+
Equivalent of `fleetctl destroy`:
|
106
|
+
|
107
|
+
client = Fleet.new
|
108
|
+
client.destroy('forever.service')
|
109
|
+
|
110
|
+
#### Retrieving Service Status
|
111
|
+
|
112
|
+
Equivalent of `fleetctl status`:
|
113
|
+
|
114
|
+
client = Fleet.new
|
115
|
+
client.status('forever.service')
|
data/fleet-api.gemspec
CHANGED
@@ -7,16 +7,18 @@ Gem::Specification.new do |gem|
|
|
7
7
|
gem.summary = 'A simple REST client for the CoreOS Fleet API'
|
8
8
|
gem.homepage = 'https://github.com/centurylinklabs/fleet-api'
|
9
9
|
gem.license = 'Apache 2'
|
10
|
+
gem.platform = Gem::Platform::RUBY
|
10
11
|
gem.files = `git ls-files`.split($\)
|
11
12
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
12
13
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
13
14
|
gem.name = 'fleet-api'
|
14
15
|
gem.require_paths = %w(lib)
|
15
16
|
gem.version = Fleet::VERSION
|
17
|
+
gem.required_ruby_version = '>= 1.9.3'
|
16
18
|
gem.add_dependency 'faraday', '= 0.8.9'
|
17
19
|
gem.add_dependency 'faraday_middleware', '= 0.9.0'
|
18
20
|
gem.add_development_dependency 'rake'
|
19
21
|
gem.add_development_dependency 'rspec', '~> 3.0'
|
20
|
-
gem.add_development_dependency 'simplecov'
|
21
|
-
gem.add_development_dependency 'simplecov-rcov'
|
22
|
+
gem.add_development_dependency 'simplecov', '~> 0.9.0'
|
23
|
+
gem.add_development_dependency 'simplecov-rcov', '~> 0.2.3'
|
22
24
|
end
|
data/lib/fleet/client.rb
CHANGED
@@ -12,7 +12,7 @@ module Fleet
|
|
12
12
|
|
13
13
|
FLEET_PATH = 'v2/keys/_coreos.com/fleet'
|
14
14
|
MAX_RETRIES = 10
|
15
|
-
SLEEP_TIME = (
|
15
|
+
SLEEP_TIME = (4.0 / MAX_RETRIES.to_f)
|
16
16
|
|
17
17
|
attr_accessor(*Configuration::VALID_OPTIONS_KEYS)
|
18
18
|
|
@@ -30,24 +30,26 @@ module Fleet
|
|
30
30
|
include Fleet::Client::State
|
31
31
|
include Fleet::Client::Unit
|
32
32
|
|
33
|
-
def load(name, service_def=
|
33
|
+
def load(name, service_def=nil)
|
34
34
|
|
35
|
-
|
36
|
-
service_def
|
37
|
-
|
35
|
+
if service_def
|
36
|
+
unless service_def.is_a?(ServiceDefinition)
|
37
|
+
service_def = ServiceDefinition.new(name, service_def)
|
38
|
+
end
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
begin
|
41
|
+
create_unit(service_def.sha1, service_def.to_unit)
|
42
|
+
rescue Fleet::PreconditionFailed
|
43
|
+
end
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
45
|
+
begin
|
46
|
+
create_job(service_def.name, service_def.to_job)
|
47
|
+
rescue Fleet::PreconditionFailed
|
48
|
+
end
|
47
49
|
end
|
48
50
|
|
49
|
-
update_job_target_state(
|
50
|
-
wait_for_load_state(
|
51
|
+
update_job_target_state(name, :loaded)
|
52
|
+
wait_for_load_state(name, 'loaded')
|
51
53
|
end
|
52
54
|
|
53
55
|
def start(service_name)
|
@@ -69,7 +71,7 @@ module Fleet
|
|
69
71
|
wait_for_load_state(service_name, :no_state)
|
70
72
|
end
|
71
73
|
|
72
|
-
def
|
74
|
+
def status(service_name)
|
73
75
|
fleet_state = get_state(service_name)
|
74
76
|
service_states = JSON.parse(fleet_state['node']['value'])
|
75
77
|
service_states.each_with_object({}) do |(k, v), hash|
|
@@ -86,7 +88,7 @@ module Fleet
|
|
86
88
|
def wait_for_load_state(service_name, target_state='loaded')
|
87
89
|
result = MAX_RETRIES.times do
|
88
90
|
begin
|
89
|
-
break target_state if
|
91
|
+
break target_state if status(service_name)[:load_state] == target_state
|
90
92
|
rescue Fleet::NotFound
|
91
93
|
# :no_state is a special case of target state that indicates we
|
92
94
|
# expect the state to not be found at all (useful when waiting for
|
data/lib/fleet/version.rb
CHANGED
data/spec/fleet/client_spec.rb
CHANGED
@@ -32,85 +32,117 @@ describe Fleet::Client do
|
|
32
32
|
{ 'node' => { 'value' => '{ "loadState": "loaded" }' } }
|
33
33
|
end
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
35
|
+
context 'when a service definition is provided' do
|
36
|
+
before do
|
37
|
+
allow(subject).to receive(:create_unit).and_return(nil)
|
38
|
+
allow(subject).to receive(:create_job).and_return(nil)
|
39
|
+
allow(subject).to receive(:update_job_target_state).and_return(nil)
|
40
|
+
allow(subject).to receive(:get_state).and_return(fleet_state)
|
41
|
+
allow(Fleet::ServiceDefinition).to receive(:new).and_return(sd)
|
42
|
+
end
|
42
43
|
|
43
|
-
|
44
|
-
|
45
|
-
|
44
|
+
it 'invokes #create_unit' do
|
45
|
+
expect(subject).to receive(:create_unit)
|
46
|
+
.with(sd.sha1, sd.to_unit)
|
46
47
|
|
47
|
-
|
48
|
-
|
48
|
+
subject.load(name, service_def)
|
49
|
+
end
|
49
50
|
|
50
|
-
|
51
|
-
|
52
|
-
|
51
|
+
it 'invokes #create_job' do
|
52
|
+
expect(subject).to receive(:create_job)
|
53
|
+
.with(sd.name, sd.to_job)
|
53
54
|
|
54
|
-
|
55
|
-
|
55
|
+
subject.load(name, service_def)
|
56
|
+
end
|
56
57
|
|
57
|
-
|
58
|
-
|
59
|
-
|
58
|
+
it 'invokes #update_job_target_state' do
|
59
|
+
expect(subject).to receive(:update_job_target_state)
|
60
|
+
.with(sd.name, :loaded)
|
60
61
|
|
61
|
-
|
62
|
-
|
62
|
+
subject.load(name, service_def)
|
63
|
+
end
|
63
64
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
65
|
+
it 'checks the job state' do
|
66
|
+
expect(subject).to receive(:get_state).with(sd.name)
|
67
|
+
subject.load(name, service_def)
|
68
|
+
end
|
68
69
|
|
69
|
-
|
70
|
+
context 'when #create_unit raises PreconditionFailed' do
|
70
71
|
|
71
|
-
|
72
|
-
|
73
|
-
|
72
|
+
before do
|
73
|
+
allow(subject).to receive(:create_unit)
|
74
|
+
.and_raise(Fleet::PreconditionFailed.new('boom'))
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'does not blow up' do
|
78
|
+
expect { subject.load(name, service_def) }.to_not raise_error
|
79
|
+
end
|
74
80
|
end
|
75
81
|
|
76
|
-
|
77
|
-
|
82
|
+
context 'when #create_unit raises something other than PreconditionFailed' do
|
83
|
+
|
84
|
+
before do
|
85
|
+
allow(subject).to receive(:create_unit)
|
86
|
+
.and_raise(Fleet::BadRequest.new('boom'))
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'propagates the error' do
|
90
|
+
expect { subject.load(name, service_def) }.to(raise_error(Fleet::BadRequest))
|
91
|
+
end
|
78
92
|
end
|
79
|
-
end
|
80
93
|
|
81
|
-
|
94
|
+
context 'when #create_job raises PreconditionFailed' do
|
82
95
|
|
83
|
-
|
84
|
-
|
85
|
-
|
96
|
+
before do
|
97
|
+
allow(subject).to receive(:create_job)
|
98
|
+
.and_raise(Fleet::PreconditionFailed.new('boom'))
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'does not blow up' do
|
102
|
+
expect { subject.load(name, service_def) }.to_not raise_error
|
103
|
+
end
|
86
104
|
end
|
87
105
|
|
88
|
-
|
89
|
-
|
106
|
+
context 'when #create_job raises something other than PreconditionFailed' do
|
107
|
+
|
108
|
+
before do
|
109
|
+
allow(subject).to receive(:create_job)
|
110
|
+
.and_raise(Fleet::BadRequest.new('boom'))
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'propagates the error' do
|
114
|
+
expect { subject.load(name, service_def) }.to(raise_error(Fleet::BadRequest))
|
115
|
+
end
|
90
116
|
end
|
91
117
|
end
|
92
118
|
|
93
|
-
context 'when
|
119
|
+
context 'when no service definition is provided' do
|
94
120
|
|
95
121
|
before do
|
96
|
-
allow(subject).to receive(:
|
97
|
-
|
122
|
+
allow(subject).to receive(:update_job_target_state).and_return(nil)
|
123
|
+
allow(subject).to receive(:get_state).and_return(fleet_state)
|
98
124
|
end
|
99
125
|
|
100
|
-
it 'does
|
101
|
-
expect
|
126
|
+
it 'does NOT invoke #create_unit' do
|
127
|
+
expect(subject).to_not receive(:create_unit)
|
128
|
+
subject.load(name)
|
102
129
|
end
|
103
|
-
end
|
104
130
|
|
105
|
-
|
131
|
+
it 'does NOT invoke #create_job' do
|
132
|
+
expect(subject).to_not receive(:create_job)
|
133
|
+
subject.load(name)
|
134
|
+
end
|
106
135
|
|
107
|
-
|
108
|
-
|
109
|
-
.
|
136
|
+
it 'invokes #update_job_target_state' do
|
137
|
+
expect(subject).to receive(:update_job_target_state)
|
138
|
+
.with(sd.name, :loaded)
|
139
|
+
|
140
|
+
subject.load(name)
|
110
141
|
end
|
111
142
|
|
112
|
-
it '
|
113
|
-
expect
|
143
|
+
it 'checks the job state' do
|
144
|
+
expect(subject).to receive(:get_state).with(sd.name)
|
145
|
+
subject.load(name)
|
114
146
|
end
|
115
147
|
end
|
116
148
|
end
|
@@ -223,7 +255,7 @@ describe Fleet::Client do
|
|
223
255
|
end
|
224
256
|
end
|
225
257
|
|
226
|
-
describe '#
|
258
|
+
describe '#status' do
|
227
259
|
|
228
260
|
let(:service_name) { 'foo.service' }
|
229
261
|
|
@@ -237,11 +269,11 @@ describe Fleet::Client do
|
|
237
269
|
|
238
270
|
it 'retrieves service state from the fleet client' do
|
239
271
|
expect(subject).to receive(:get_state).with(service_name)
|
240
|
-
subject.
|
272
|
+
subject.status(service_name)
|
241
273
|
end
|
242
274
|
|
243
275
|
it 'returns the state hash w/ normalized keys' do
|
244
|
-
expect(subject.
|
276
|
+
expect(subject.status(service_name)).to eq(load: 'loaded', run: 'running')
|
245
277
|
end
|
246
278
|
end
|
247
279
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fleet-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- CenturyLink
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -70,30 +70,30 @@ dependencies:
|
|
70
70
|
name: simplecov
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - "
|
73
|
+
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
75
|
+
version: 0.9.0
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - "
|
80
|
+
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: 0.9.0
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: simplecov-rcov
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
89
|
+
version: 0.2.3
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - "
|
94
|
+
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
96
|
+
version: 0.2.3
|
97
97
|
description: A simple REST client for the CoreOS Fleet API
|
98
98
|
email:
|
99
99
|
- clt-labs-futuretech@centurylink.com
|
@@ -102,6 +102,7 @@ extensions: []
|
|
102
102
|
extra_rdoc_files: []
|
103
103
|
files:
|
104
104
|
- ".gitignore"
|
105
|
+
- ".travis.yml"
|
105
106
|
- Gemfile
|
106
107
|
- Gemfile.lock
|
107
108
|
- LICENSE
|
@@ -144,7 +145,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
144
145
|
requirements:
|
145
146
|
- - ">="
|
146
147
|
- !ruby/object:Gem::Version
|
147
|
-
version:
|
148
|
+
version: 1.9.3
|
148
149
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
149
150
|
requirements:
|
150
151
|
- - ">="
|
@@ -152,7 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
152
153
|
version: '0'
|
153
154
|
requirements: []
|
154
155
|
rubyforge_project:
|
155
|
-
rubygems_version: 2.
|
156
|
+
rubygems_version: 2.2.0
|
156
157
|
signing_key:
|
157
158
|
specification_version: 4
|
158
159
|
summary: A simple REST client for the CoreOS Fleet API
|