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.
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, 12, nil) }.to raise_error(TokenError)
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 => {"{\"lifetime\":12}"=>true},
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', 12, nil)
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