vmfloaty 0.7.9 → 0.8.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 +4 -4
- data/README.md +67 -1
- data/lib/vmfloaty/errors.rb +6 -0
- data/lib/vmfloaty/nonstandard_pooler.rb +135 -0
- data/lib/vmfloaty/pooler.rb +20 -8
- data/lib/vmfloaty/service.rb +133 -0
- data/lib/vmfloaty/utils.rb +163 -73
- data/lib/vmfloaty/version.rb +1 -1
- data/lib/vmfloaty.rb +191 -327
- data/spec/spec_helper.rb +7 -0
- data/spec/vmfloaty/nonstandard_pooler_spec.rb +325 -0
- data/spec/vmfloaty/pooler_spec.rb +4 -3
- data/spec/vmfloaty/service_spec.rb +79 -0
- data/spec/vmfloaty/utils_spec.rb +183 -49
- metadata +10 -4
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,13 @@
|
|
1
1
|
require 'vmfloaty'
|
2
2
|
require 'webmock/rspec'
|
3
3
|
|
4
|
+
# Mock Commander Options object to allow pre-population with values
|
5
|
+
class MockOptions < Commander::Command::Options
|
6
|
+
def initialize(values = {})
|
7
|
+
@table = values
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
4
11
|
RSpec.configure do |config|
|
5
12
|
config.color = true
|
6
13
|
config.tty = true
|
@@ -0,0 +1,325 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'vmfloaty/utils'
|
3
|
+
require 'vmfloaty/errors'
|
4
|
+
require 'vmfloaty/nonstandard_pooler'
|
5
|
+
|
6
|
+
describe NonstandardPooler do
|
7
|
+
before :each do
|
8
|
+
@nspooler_url = 'https://nspooler.example.com'
|
9
|
+
@post_request_headers = {
|
10
|
+
'Accept' => '*/*',
|
11
|
+
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
|
12
|
+
'User-Agent' => 'Faraday v0.9.2',
|
13
|
+
'X-Auth-Token' => 'token-value'
|
14
|
+
}
|
15
|
+
@get_request_headers = {
|
16
|
+
'Accept' => '*/*',
|
17
|
+
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
|
18
|
+
'User-Agent' => 'Faraday v0.9.2',
|
19
|
+
'X-Auth-Token' => 'token-value'
|
20
|
+
}
|
21
|
+
@get_request_headers_notoken = @get_request_headers.tap do |headers|
|
22
|
+
headers.delete('X-Auth-Token')
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#list' do
|
28
|
+
before :each do
|
29
|
+
@status_response_body = <<-BODY
|
30
|
+
{
|
31
|
+
"ok": true,
|
32
|
+
"solaris-10-sparc": {
|
33
|
+
"total_hosts": 11,
|
34
|
+
"available_hosts": 11
|
35
|
+
},
|
36
|
+
"ubuntu-16.04-power8": {
|
37
|
+
"total_hosts": 10,
|
38
|
+
"available_hosts": 10
|
39
|
+
},
|
40
|
+
"aix-7.2-power": {
|
41
|
+
"total_hosts": 5,
|
42
|
+
"available_hosts": 4
|
43
|
+
}
|
44
|
+
}
|
45
|
+
BODY
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'returns an array with operating systems from the pooler' do
|
49
|
+
stub_request(:get, "#{@nspooler_url}/status")
|
50
|
+
.to_return(status: 200, body: @status_response_body, headers: {})
|
51
|
+
|
52
|
+
list = NonstandardPooler.list(false, @nspooler_url, nil)
|
53
|
+
expect(list).to be_an_instance_of Array
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'filters operating systems based on the filter param' do
|
57
|
+
stub_request(:get, "#{@nspooler_url}/status")
|
58
|
+
.to_return(status: 200, body: @status_response_body, headers: {})
|
59
|
+
|
60
|
+
list = NonstandardPooler.list(false, @nspooler_url, 'aix')
|
61
|
+
expect(list).to be_an_instance_of Array
|
62
|
+
expect(list.size).to equal 1
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'returns nothing if the filter does not match' do
|
66
|
+
stub_request(:get, "#{@nspooler_url}/status")
|
67
|
+
.to_return(status: 199, body: @status_response_body, headers: {})
|
68
|
+
|
69
|
+
list = NonstandardPooler.list(false, @nspooler_url, 'windows')
|
70
|
+
expect(list).to be_an_instance_of Array
|
71
|
+
expect(list.size).to equal 0
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#list_active' do
|
76
|
+
before :each do
|
77
|
+
@token_status_body_active = <<-BODY
|
78
|
+
{
|
79
|
+
"ok": true,
|
80
|
+
"user": "first.last",
|
81
|
+
"created": "2017-09-18 01:25:41 +0000",
|
82
|
+
"last_accessed": "2017-09-21 19:46:25 +0000",
|
83
|
+
"reserved_hosts": ["sol10-9", "sol10-11"]
|
84
|
+
}
|
85
|
+
BODY
|
86
|
+
@token_status_body_empty = <<-BODY
|
87
|
+
{
|
88
|
+
"ok": true,
|
89
|
+
"user": "first.last",
|
90
|
+
"created": "2017-09-18 01:25:41 +0000",
|
91
|
+
"last_accessed": "2017-09-21 19:46:25 +0000",
|
92
|
+
"reserved_hosts": []
|
93
|
+
}
|
94
|
+
BODY
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'prints an output of fqdn, template, and duration' do
|
98
|
+
allow(Auth).to receive(:token_status)
|
99
|
+
.with(false, @nspooler_url, 'token-value')
|
100
|
+
.and_return(JSON.parse(@token_status_body_active))
|
101
|
+
|
102
|
+
list = NonstandardPooler.list_active(false, @nspooler_url, 'token-value')
|
103
|
+
expect(list).to eql ['sol10-9', 'sol10-11']
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe '#retrieve' do
|
108
|
+
before :each do
|
109
|
+
@retrieve_response_body_single = <<-BODY
|
110
|
+
{
|
111
|
+
"ok": true,
|
112
|
+
"solaris-11-sparc": {
|
113
|
+
"hostname": "sol11-4.delivery.puppetlabs.net"
|
114
|
+
}
|
115
|
+
}
|
116
|
+
BODY
|
117
|
+
@retrieve_response_body_many = <<-BODY
|
118
|
+
{
|
119
|
+
"ok": true,
|
120
|
+
"solaris-10-sparc": {
|
121
|
+
"hostname": [
|
122
|
+
"sol10-9.delivery.puppetlabs.net",
|
123
|
+
"sol10-10.delivery.puppetlabs.net"
|
124
|
+
]
|
125
|
+
},
|
126
|
+
"aix-7.1-power": {
|
127
|
+
"hostname": "pe-aix-71-ci-acceptance.delivery.puppetlabs.net"
|
128
|
+
}
|
129
|
+
}
|
130
|
+
BODY
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'raises an AuthError if the token is invalid' do
|
134
|
+
stub_request(:post, "#{@nspooler_url}/host/solaris-11-sparc")
|
135
|
+
.with(headers: @post_request_headers)
|
136
|
+
.to_return(status: 401, body: '{"ok":false,"reason": "token: token-value does not exist"}', headers: {})
|
137
|
+
|
138
|
+
vm_hash = { 'solaris-11-sparc' => 1 }
|
139
|
+
expect { NonstandardPooler.retrieve(false, vm_hash, 'token-value', @nspooler_url) }.to raise_error(AuthError)
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'retrieves a single vm with a token' do
|
143
|
+
stub_request(:post, "#{@nspooler_url}/host/solaris-11-sparc")
|
144
|
+
.with(headers: @post_request_headers)
|
145
|
+
.to_return(status: 200, body: @retrieve_response_body_single, headers: {})
|
146
|
+
|
147
|
+
vm_hash = { 'solaris-11-sparc' => 1 }
|
148
|
+
vm_req = NonstandardPooler.retrieve(false, vm_hash, 'token-value', @nspooler_url)
|
149
|
+
expect(vm_req).to be_an_instance_of Hash
|
150
|
+
expect(vm_req['ok']).to equal true
|
151
|
+
expect(vm_req['solaris-11-sparc']['hostname']).to eq 'sol11-4.delivery.puppetlabs.net'
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'retrieves a multiple vms with a token' do
|
155
|
+
stub_request(:post,"#{@nspooler_url}/host/aix-7.1-power+solaris-10-sparc+solaris-10-sparc")
|
156
|
+
.with(headers: @post_request_headers)
|
157
|
+
.to_return(status: 200, body: @retrieve_response_body_many, headers: {})
|
158
|
+
|
159
|
+
vm_hash = { 'aix-7.1-power' => 1, 'solaris-10-sparc' => 2 }
|
160
|
+
vm_req = NonstandardPooler.retrieve(false, vm_hash, 'token-value', @nspooler_url)
|
161
|
+
expect(vm_req).to be_an_instance_of Hash
|
162
|
+
expect(vm_req['ok']).to equal true
|
163
|
+
expect(vm_req['solaris-10-sparc']['hostname']).to be_an_instance_of Array
|
164
|
+
expect(vm_req['solaris-10-sparc']['hostname']).to eq ['sol10-9.delivery.puppetlabs.net', 'sol10-10.delivery.puppetlabs.net']
|
165
|
+
expect(vm_req['aix-7.1-power']['hostname']).to eq 'pe-aix-71-ci-acceptance.delivery.puppetlabs.net'
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe '#modify' do
|
170
|
+
before :each do
|
171
|
+
@modify_response_body_success = '{"ok":true}'
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'raises an error if the user tries to modify an unsupported attribute' do
|
175
|
+
stub_request(:put, "https://nspooler.example.com/host/myfakehost").
|
176
|
+
with(body: {"{}"=>true},
|
177
|
+
headers: {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>'Faraday v0.9.2', 'X-Auth-Token'=>'token-value'}).
|
178
|
+
to_return(status: 200, body: "", headers: {})
|
179
|
+
details = { lifetime: 12 }
|
180
|
+
expect { NonstandardPooler.modify(false, @nspooler_url, 'myfakehost', 'token-value', details) }
|
181
|
+
.to raise_error(ModifyError)
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'modifies the reason of a vm' do
|
185
|
+
modify_request_body = { '{"reserved_for_reason":"testing"}' => true }
|
186
|
+
stub_request(:put, "#{@nspooler_url}/host/myfakehost")
|
187
|
+
.with(body: modify_request_body,
|
188
|
+
headers: @post_request_headers)
|
189
|
+
.to_return(status: 200, body: '{"ok": true}', headers: {})
|
190
|
+
|
191
|
+
modify_hash = { reason: "testing" }
|
192
|
+
modify_req = NonstandardPooler.modify(false, @nspooler_url, 'myfakehost', 'token-value', modify_hash)
|
193
|
+
expect(modify_req['ok']).to be true
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
describe '#status' do
|
198
|
+
before :each do
|
199
|
+
@status_response_body = '{"capacity":{"current":716,"total":717,"percent": 99.9},"status":{"ok":true,"message":"Battle station fully armed and operational."}}'
|
200
|
+
# TODO: make this report stuff like 'broken'
|
201
|
+
@status_response_body = <<-BODY
|
202
|
+
{
|
203
|
+
"ok": true,
|
204
|
+
"solaris-10-sparc": {
|
205
|
+
"total_hosts": 11,
|
206
|
+
"available_hosts": 10
|
207
|
+
},
|
208
|
+
"ubuntu-16.04-power8": {
|
209
|
+
"total_hosts": 10,
|
210
|
+
"available_hosts": 10
|
211
|
+
},
|
212
|
+
"aix-7.2-power": {
|
213
|
+
"total_hosts": 5,
|
214
|
+
"available_hosts": 4
|
215
|
+
}
|
216
|
+
}
|
217
|
+
BODY
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'prints the status' do
|
221
|
+
stub_request(:get, "#{@nspooler_url}/status")
|
222
|
+
.with(headers: @get_request_headers)
|
223
|
+
.to_return(status: 200, body: @status_response_body, headers: {})
|
224
|
+
|
225
|
+
status = NonstandardPooler.status(false, @nspooler_url)
|
226
|
+
expect(status).to be_an_instance_of Hash
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
describe '#summary' do
|
231
|
+
before :each do
|
232
|
+
@status_response_body = <<-BODY
|
233
|
+
{
|
234
|
+
"ok": true,
|
235
|
+
"total": 57,
|
236
|
+
"available": 39,
|
237
|
+
"in_use": 16,
|
238
|
+
"resetting": 2,
|
239
|
+
"broken": 0
|
240
|
+
}
|
241
|
+
BODY
|
242
|
+
end
|
243
|
+
|
244
|
+
it 'prints the summary' do
|
245
|
+
stub_request(:get, "#{@nspooler_url}/summary")
|
246
|
+
.with(headers: @get_request_headers)
|
247
|
+
.to_return(status: 200, body: @status_response_body, headers: {})
|
248
|
+
|
249
|
+
summary = NonstandardPooler.summary(false, @nspooler_url)
|
250
|
+
expect(summary).to be_an_instance_of Hash
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
describe '#query' do
|
255
|
+
before :each do
|
256
|
+
@query_response_body = <<-BODY
|
257
|
+
{
|
258
|
+
"ok": true,
|
259
|
+
"sol10-11": {
|
260
|
+
"fqdn": "sol10-11.delivery.puppetlabs.net",
|
261
|
+
"os_triple": "solaris-10-sparc",
|
262
|
+
"reserved_by_user": "first.last",
|
263
|
+
"reserved_for_reason": "testing",
|
264
|
+
"hours_left_on_reservation": 29.12
|
265
|
+
}
|
266
|
+
}
|
267
|
+
BODY
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'makes a query about a vm' do
|
271
|
+
stub_request(:get, "#{@nspooler_url}/host/sol10-11")
|
272
|
+
.with(headers: @get_request_headers_notoken)
|
273
|
+
.to_return(status: 200, body: @query_response_body, headers: {})
|
274
|
+
|
275
|
+
query_req = NonstandardPooler.query(false, @nspooler_url, 'sol10-11')
|
276
|
+
expect(query_req).to be_an_instance_of Hash
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
describe '#delete' do
|
281
|
+
before :each do
|
282
|
+
@delete_response_success = '{"ok": true}'
|
283
|
+
@delete_response_failure = '{"ok": false, "failure": "ERROR: fakehost does not exist"}'
|
284
|
+
end
|
285
|
+
|
286
|
+
it 'deletes a single existing vm' do
|
287
|
+
stub_request(:delete, "#{@nspooler_url}/host/sol11-7")
|
288
|
+
.with(headers: @post_request_headers)
|
289
|
+
.to_return(status: 200, body: @delete_response_success, headers: {})
|
290
|
+
|
291
|
+
request = NonstandardPooler.delete(false, @nspooler_url, 'sol11-7', 'token-value')
|
292
|
+
expect(request['sol11-7']['ok']).to be true
|
293
|
+
end
|
294
|
+
|
295
|
+
it 'does not delete a nonexistant vm' do
|
296
|
+
stub_request(:delete, "#{@nspooler_url}/host/fakehost")
|
297
|
+
.with(headers: @post_request_headers)
|
298
|
+
.to_return(status: 401, body: @delete_response_failure, headers: {})
|
299
|
+
|
300
|
+
request = NonstandardPooler.delete(false, @nspooler_url, 'fakehost', 'token-value')
|
301
|
+
expect(request['fakehost']['ok']).to be false
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
describe '#snapshot' do
|
306
|
+
it 'logs an error explaining that snapshots are not supported' do
|
307
|
+
expect { NonstandardPooler.snapshot(false, @nspooler_url, 'myfakehost', 'token-value') }
|
308
|
+
.to raise_error(ModifyError)
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
describe '#revert' do
|
313
|
+
it 'logs an error explaining that snapshots are not supported' do
|
314
|
+
expect { NonstandardPooler.revert(false, @nspooler_url, 'myfakehost', 'token-value', 'snapshot-sha') }
|
315
|
+
.to raise_error(ModifyError)
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
describe '#disk' do
|
320
|
+
it 'logs an error explaining that disk modification is not supported' do
|
321
|
+
expect { NonstandardPooler.disk(false, @nspooler_url, 'myfakehost', 'token-value', 'diskname') }
|
322
|
+
.to raise_error(ModifyError)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
@@ -91,16 +91,17 @@ describe Pooler do
|
|
91
91
|
end
|
92
92
|
|
93
93
|
it "raises a TokenError if token provided is nil" do
|
94
|
-
expect{ Pooler.modify(false, @vmpooler_url, 'myfakehost', nil,
|
94
|
+
expect{ Pooler.modify(false, @vmpooler_url, 'myfakehost', nil, {}) }.to raise_error(TokenError)
|
95
95
|
end
|
96
96
|
|
97
97
|
it "modifies the TTL of a vm" do
|
98
|
+
modify_hash = { :lifetime => 12 }
|
98
99
|
stub_request(:put, "#{@vmpooler_url}/vm/fq6qlpjlsskycq6").
|
99
|
-
with(:body => {
|
100
|
+
with(:body => {'{"lifetime":12}'=>true},
|
100
101
|
:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>'Faraday v0.9.2', 'X-Auth-Token'=>'mytokenfile'}).
|
101
102
|
to_return(:status => 200, :body => @modify_response_body_success, :headers => {})
|
102
103
|
|
103
|
-
modify_req = Pooler.modify(false, @vmpooler_url, 'fq6qlpjlsskycq6', 'mytokenfile',
|
104
|
+
modify_req = Pooler.modify(false, @vmpooler_url, 'fq6qlpjlsskycq6', 'mytokenfile', modify_hash)
|
104
105
|
expect(modify_req["ok"]).to be true
|
105
106
|
end
|
106
107
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require_relative '../../lib/vmfloaty/service'
|
2
|
+
|
3
|
+
describe Service do
|
4
|
+
|
5
|
+
describe '#initialize' do
|
6
|
+
it 'store configuration options' do
|
7
|
+
options = MockOptions.new({})
|
8
|
+
config = {'url' => 'http://example.url'}
|
9
|
+
service = Service.new(options, config)
|
10
|
+
expect(service.config).to include config
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#get_new_token' do
|
15
|
+
it 'prompts the user for their password and retrieves a token' do
|
16
|
+
config = { 'user' => 'first.last', 'url' => 'http://default.url' }
|
17
|
+
service = Service.new(MockOptions.new, config)
|
18
|
+
allow(STDOUT).to receive(:puts).with('Enter your pooler service password:')
|
19
|
+
allow(Commander::UI).to(receive(:password)
|
20
|
+
.with('Enter your pooler service password:', '*')
|
21
|
+
.and_return('hunter2'))
|
22
|
+
allow(Auth).to(receive(:get_token)
|
23
|
+
.with(nil, config['url'], config['user'], 'hunter2')
|
24
|
+
.and_return('token-value'))
|
25
|
+
expect(service.get_new_token(nil)).to eql 'token-value'
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'prompts the user for their username and password if the username is unknown' do
|
29
|
+
config = { 'url' => 'http://default.url' }
|
30
|
+
service = Service.new(MockOptions.new({}), config)
|
31
|
+
allow(STDOUT).to receive(:puts).with 'Enter your pooler service username:'
|
32
|
+
allow(STDOUT).to receive(:puts).with "\n"
|
33
|
+
allow(STDIN).to receive(:gets).and_return('first.last')
|
34
|
+
allow(Commander::UI).to(receive(:password)
|
35
|
+
.with('Enter your pooler service password:', '*')
|
36
|
+
.and_return('hunter2'))
|
37
|
+
allow(Auth).to(receive(:get_token)
|
38
|
+
.with(nil, config['url'], 'first.last', 'hunter2')
|
39
|
+
.and_return('token-value'))
|
40
|
+
expect(service.get_new_token(nil)).to eql 'token-value'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#delete_token' do
|
45
|
+
it 'deletes a token' do
|
46
|
+
service = Service.new(MockOptions.new,{'user' => 'first.last', 'url' => 'http://default.url'})
|
47
|
+
allow(Commander::UI).to(receive(:password)
|
48
|
+
.with('Enter your pooler service password:', '*')
|
49
|
+
.and_return('hunter2'))
|
50
|
+
allow(Auth).to(receive(:delete_token)
|
51
|
+
.with(nil, 'http://default.url', 'first.last', 'hunter2', 'token-value')
|
52
|
+
.and_return('ok' => true))
|
53
|
+
expect(service.delete_token(nil, 'token-value')).to eql({'ok' => true})
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#token_status' do
|
58
|
+
it 'reports the status of a token' do
|
59
|
+
config = {
|
60
|
+
'user' => 'first.last',
|
61
|
+
'url' => 'http://default.url'
|
62
|
+
}
|
63
|
+
options = MockOptions.new('token' => 'token-value')
|
64
|
+
service = Service.new(options, config)
|
65
|
+
status = {
|
66
|
+
'ok' => true,
|
67
|
+
'user' => config['user'],
|
68
|
+
'created' => '2017-09-22 02:04:18 +0000',
|
69
|
+
'last_accessed' => '2017-09-22 02:04:28 +0000',
|
70
|
+
'reserved_hosts' => []
|
71
|
+
}
|
72
|
+
allow(Auth).to(receive(:token_status)
|
73
|
+
.with(nil, config['url'], 'token-value')
|
74
|
+
.and_return(status))
|
75
|
+
expect(service.token_status(nil, 'token-value')).to eql(status)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|