vmfloaty 0.8.2 → 0.9.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 +5 -5
- data/README.md +13 -8
- data/bin/floaty +2 -1
- data/lib/vmfloaty.rb +43 -49
- data/lib/vmfloaty/abs.rb +293 -0
- data/lib/vmfloaty/auth.rb +14 -22
- data/lib/vmfloaty/conf.rb +3 -2
- data/lib/vmfloaty/errors.rb +6 -4
- data/lib/vmfloaty/http.rb +14 -25
- data/lib/vmfloaty/nonstandard_pooler.rb +14 -30
- data/lib/vmfloaty/pooler.rb +31 -52
- data/lib/vmfloaty/service.rb +19 -15
- data/lib/vmfloaty/ssh.rb +17 -23
- data/lib/vmfloaty/utils.rb +89 -80
- data/lib/vmfloaty/version.rb +3 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/vmfloaty/abs/auth_spec.rb +84 -0
- data/spec/vmfloaty/abs_spec.rb +96 -0
- data/spec/vmfloaty/auth_spec.rb +39 -43
- data/spec/vmfloaty/nonstandard_pooler_spec.rb +130 -144
- data/spec/vmfloaty/pooler_spec.rb +100 -100
- data/spec/vmfloaty/service_spec.rb +17 -17
- data/spec/vmfloaty/utils_spec.rb +106 -105
- metadata +19 -14
data/lib/vmfloaty/ssh.rb
CHANGED
@@ -1,43 +1,37 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
class Ssh
|
3
4
|
def self.which(cmd)
|
4
5
|
# Gets path of executable for given command
|
5
6
|
|
6
7
|
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
7
8
|
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
8
|
-
exts.each
|
9
|
+
exts.each do |ext|
|
9
10
|
exe = File.join(path, "#{cmd}#{ext}")
|
10
11
|
return exe if File.executable?(exe) && !File.directory?(exe)
|
11
|
-
|
12
|
+
end
|
12
13
|
end
|
13
|
-
|
14
|
+
nil
|
14
15
|
end
|
15
16
|
|
16
17
|
def self.ssh(verbose, host_os, token, url)
|
17
|
-
ssh_path = which(
|
18
|
-
|
19
|
-
|
20
|
-
end
|
18
|
+
ssh_path = which('ssh')
|
19
|
+
raise 'Could not determine path to ssh' unless ssh_path
|
20
|
+
|
21
21
|
os_types = {}
|
22
22
|
os_types[host_os] = 1
|
23
23
|
|
24
24
|
response = Pooler.retrieve(verbose, os_types, token, url)
|
25
|
-
|
26
|
-
if host_os =~ /win/
|
27
|
-
user = "Administrator"
|
28
|
-
else
|
29
|
-
user = "root"
|
30
|
-
end
|
25
|
+
raise "Could not get vm from vmpooler:\n #{response}" unless response['ok']
|
31
26
|
|
32
|
-
|
33
|
-
cmd = "#{ssh_path} #{user}@#{hostname}"
|
27
|
+
user = /win/.match?(host_os) ? 'Administrator' : 'root'
|
34
28
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
29
|
+
hostname = "#{response[host_os]['hostname']}.#{response['domain']}"
|
30
|
+
cmd = "#{ssh_path} #{user}@#{hostname}"
|
31
|
+
|
32
|
+
# TODO: Should this respect more ssh settings? Can it be configured
|
33
|
+
# by users ssh config and does this respect those settings?
|
34
|
+
Kernel.exec(cmd)
|
35
|
+
nil
|
42
36
|
end
|
43
37
|
end
|
data/lib/vmfloaty/utils.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'vmfloaty/abs'
|
2
4
|
require 'vmfloaty/nonstandard_pooler'
|
5
|
+
require 'vmfloaty/pooler'
|
3
6
|
|
4
7
|
class Utils
|
5
8
|
# TODO: Takes the json response body from an HTTP GET
|
@@ -28,9 +31,14 @@ class Utils
|
|
28
31
|
# }
|
29
32
|
# }
|
30
33
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
+
# abs pooler response body example when `floaty get` arguments are :
|
35
|
+
# {
|
36
|
+
# "hostname"=>"thin-soutane.delivery.puppetlabs.net",
|
37
|
+
# "type"=>"centos-7.2-tmpfs-x86_64",
|
38
|
+
# "engine"=>"vmpooler"
|
39
|
+
# }
|
40
|
+
|
41
|
+
raise ArgumentError, "Bad GET response passed to format_hosts: #{response_body.to_json}" unless response_body.delete('ok')
|
34
42
|
|
35
43
|
# vmpooler reports the domain separately from the hostname
|
36
44
|
domain = response_body.delete('domain')
|
@@ -39,9 +47,7 @@ class Utils
|
|
39
47
|
|
40
48
|
response_body.each do |os, value|
|
41
49
|
hostnames = Array(value['hostname'])
|
42
|
-
if domain
|
43
|
-
hostnames.map! {|host| "#{host}.#{domain}"}
|
44
|
-
end
|
50
|
+
hostnames.map! { |host| "#{host}.#{domain}" } if domain
|
45
51
|
result[os] = hostnames
|
46
52
|
end
|
47
53
|
|
@@ -65,13 +71,8 @@ class Utils
|
|
65
71
|
# ...]
|
66
72
|
os_types = {}
|
67
73
|
os_args.each do |arg|
|
68
|
-
os_arr = arg.split(
|
69
|
-
|
70
|
-
# assume they didn't specify an = sign if split returns 1 size
|
71
|
-
os_types[os_arr[0]] = 1
|
72
|
-
else
|
73
|
-
os_types[os_arr[0]] = os_arr[1].to_i
|
74
|
-
end
|
74
|
+
os_arr = arg.split('=')
|
75
|
+
os_types[os_arr[0]] = os_arr.size == 1 ? 1 : os_arr[1].to_i
|
75
76
|
end
|
76
77
|
os_types
|
77
78
|
end
|
@@ -84,26 +85,29 @@ class Utils
|
|
84
85
|
host_data = response[hostname]
|
85
86
|
|
86
87
|
case service.type
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
duration = "#{host_data['running']}/#{host_data['lifetime']} hours"
|
93
|
-
metadata = [host_data['template'], duration, *tag_pairs]
|
94
|
-
puts "- #{hostname}.#{host_data['domain']} (#{metadata.join(", ")})"
|
95
|
-
when 'NonstandardPooler'
|
96
|
-
line = "- #{host_data['fqdn']} (#{host_data['os_triple']}"
|
97
|
-
line += ", #{host_data['hours_left_on_reservation']}h remaining"
|
98
|
-
unless host_data['reserved_for_reason'].empty?
|
99
|
-
line += ", reason: #{host_data['reserved_for_reason']}"
|
88
|
+
when 'ABS'
|
89
|
+
# For ABS, 'hostname' variable is the jobID
|
90
|
+
if host_data['state'] == 'allocated' || host_data['state'] == 'filled'
|
91
|
+
host_data['allocated_resources'].each do |vm_name, _i|
|
92
|
+
puts "- [JobID:#{host_data['request']['job']['id']}] #{vm_name['hostname']} (#{vm_name['type']}) <#{host_data['state']}>"
|
100
93
|
end
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
94
|
+
end
|
95
|
+
when 'Pooler'
|
96
|
+
tag_pairs = []
|
97
|
+
tag_pairs = host_data['tags'].map { |key, value| "#{key}: #{value}" } unless host_data['tags'].nil?
|
98
|
+
duration = "#{host_data['running']}/#{host_data['lifetime']} hours"
|
99
|
+
metadata = [host_data['template'], duration, *tag_pairs]
|
100
|
+
puts "- #{hostname}.#{host_data['domain']} (#{metadata.join(', ')})"
|
101
|
+
when 'NonstandardPooler'
|
102
|
+
line = "- #{host_data['fqdn']} (#{host_data['os_triple']}"
|
103
|
+
line += ", #{host_data['hours_left_on_reservation']}h remaining"
|
104
|
+
line += ", reason: #{host_data['reserved_for_reason']}" unless host_data['reserved_for_reason'].empty?
|
105
|
+
line += ')'
|
106
|
+
puts line
|
107
|
+
else
|
108
|
+
raise "Invalid service type #{service.type}"
|
105
109
|
end
|
106
|
-
rescue => e
|
110
|
+
rescue StandardError => e
|
107
111
|
STDERR.puts("Something went wrong while trying to gather information on #{hostname}:")
|
108
112
|
STDERR.puts(e)
|
109
113
|
end
|
@@ -114,45 +118,48 @@ class Utils
|
|
114
118
|
status_response = service.status(verbose)
|
115
119
|
|
116
120
|
case service.type
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
end
|
121
|
+
when 'Pooler'
|
122
|
+
message = status_response['status']['message']
|
123
|
+
pools = status_response['pools']
|
124
|
+
pools.select! { |_, pool| pool['ready'] < pool['max'] } unless verbose
|
125
|
+
|
126
|
+
width = pools.keys.map(&:length).max
|
127
|
+
pools.each do |name, pool|
|
128
|
+
begin
|
129
|
+
max = pool['max']
|
130
|
+
ready = pool['ready']
|
131
|
+
pending = pool['pending']
|
132
|
+
missing = max - ready - pending
|
133
|
+
char = 'o'
|
134
|
+
puts "#{name.ljust(width)} #{(char * ready).green}#{(char * pending).yellow}#{(char * missing).red}"
|
135
|
+
rescue StandardError => e
|
136
|
+
puts "#{name.ljust(width)} #{e.red}"
|
134
137
|
end
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
138
|
+
end
|
139
|
+
puts message.colorize(status_response['status']['ok'] ? :default : :red)
|
140
|
+
when 'NonstandardPooler'
|
141
|
+
pools = status_response
|
142
|
+
pools.delete 'ok'
|
143
|
+
pools.select! { |_, pool| pool['available_hosts'] < pool['total_hosts'] } unless verbose
|
144
|
+
|
145
|
+
width = pools.keys.map(&:length).max
|
146
|
+
pools.each do |name, pool|
|
147
|
+
begin
|
148
|
+
max = pool['total_hosts']
|
149
|
+
ready = pool['available_hosts']
|
150
|
+
pending = pool['pending'] || 0 # not available for nspooler
|
151
|
+
missing = max - ready - pending
|
152
|
+
char = 'o'
|
153
|
+
puts "#{name.ljust(width)} #{(char * ready).green}#{(char * pending).yellow}#{(char * missing).red}"
|
154
|
+
rescue StandardError => e
|
155
|
+
puts "#{name.ljust(width)} #{e.red}"
|
153
156
|
end
|
154
|
-
|
155
|
-
|
157
|
+
end
|
158
|
+
when 'ABS'
|
159
|
+
puts 'ABS Not OK'.red unless status_response
|
160
|
+
puts 'ABS is OK'.green if status_response
|
161
|
+
else
|
162
|
+
raise "Invalid service type #{service.type}"
|
156
163
|
end
|
157
164
|
end
|
158
165
|
|
@@ -165,9 +172,12 @@ class Utils
|
|
165
172
|
end
|
166
173
|
|
167
174
|
def self.get_service_object(type = '')
|
168
|
-
nspooler_strings = [
|
175
|
+
nspooler_strings = %w[ns nspooler nonstandard nonstandard_pooler]
|
176
|
+
abs_strings = %w[abs alwaysbescheduling always_be_scheduling]
|
169
177
|
if nspooler_strings.include? type.downcase
|
170
178
|
NonstandardPooler
|
179
|
+
elsif abs_strings.include? type.downcase
|
180
|
+
ABS
|
171
181
|
else
|
172
182
|
Pooler
|
173
183
|
end
|
@@ -176,10 +186,10 @@ class Utils
|
|
176
186
|
def self.get_service_config(config, options)
|
177
187
|
# The top-level url, user, and token values in the config file are treated as defaults
|
178
188
|
service_config = {
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
189
|
+
'url' => config['url'],
|
190
|
+
'user' => config['user'],
|
191
|
+
'token' => config['token'],
|
192
|
+
'type' => config['type'] || 'vmpooler',
|
183
193
|
}
|
184
194
|
|
185
195
|
if config['services']
|
@@ -190,16 +200,15 @@ class Utils
|
|
190
200
|
service_config.merge! values
|
191
201
|
else
|
192
202
|
# If the user provided a service name at the command line, use that service if posible, or fail
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
raise ArgumentError, "Could not find a configured service named '#{options.service}' in ~/.vmfloaty.yml"
|
198
|
-
end
|
203
|
+
raise ArgumentError, "Could not find a configured service named '#{options.service}' in ~/.vmfloaty.yml" unless config['services'][options.service]
|
204
|
+
|
205
|
+
# If the service is configured but some values are missing, use the top-level defaults to fill them in
|
206
|
+
service_config.merge! config['services'][options.service]
|
199
207
|
end
|
200
208
|
end
|
201
209
|
|
202
210
|
# Prioritize an explicitly specified url, user, or token if the user provided one
|
211
|
+
service_config['priority'] = options.priority unless options.priority.nil?
|
203
212
|
service_config['url'] = options.url unless options.url.nil?
|
204
213
|
service_config['token'] = options.token unless options.token.nil?
|
205
214
|
service_config['user'] = options.user unless options.user.nil?
|
data/lib/vmfloaty/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require_relative '../../../lib/vmfloaty/auth'
|
5
|
+
|
6
|
+
describe Pooler do
|
7
|
+
before :each do
|
8
|
+
@abs_url = 'https://abs.example.com/api/v2'
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#get_token' do
|
12
|
+
before :each do
|
13
|
+
@get_token_response = '{"ok": true,"token":"utpg2i2xswor6h8ttjhu3d47z53yy47y"}'
|
14
|
+
@token = 'utpg2i2xswor6h8ttjhu3d47z53yy47y'
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'returns a token from abs' do
|
18
|
+
stub_request(:post, 'https://first.last:password@abs.example.com/api/v2/token')
|
19
|
+
.to_return(:status => 200, :body => @get_token_response, :headers => {})
|
20
|
+
|
21
|
+
token = Auth.get_token(false, @abs_url, 'first.last', 'password')
|
22
|
+
expect(token).to eq @token
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'raises a token error if something goes wrong' do
|
26
|
+
stub_request(:post, 'https://first.last:password@abs.example.com/api/v2/token')
|
27
|
+
.to_return(:status => 500, :body => '{"ok":false}', :headers => {})
|
28
|
+
|
29
|
+
expect { Auth.get_token(false, @abs_url, 'first.last', 'password') }.to raise_error(TokenError)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#delete_token' do
|
34
|
+
before :each do
|
35
|
+
@delete_token_response = '{"ok":true}'
|
36
|
+
@token = 'utpg2i2xswor6h8ttjhu3d47z53yy47y'
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'deletes the specified token' do
|
40
|
+
stub_request(:delete, 'https://first.last:password@abs.example.com/api/v2/token/utpg2i2xswor6h8ttjhu3d47z53yy47y')
|
41
|
+
.to_return(:status => 200, :body => @delete_token_response, :headers => {})
|
42
|
+
|
43
|
+
expect(Auth.delete_token(false, @abs_url, 'first.last', 'password', @token)).to eq JSON.parse(@delete_token_response)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'raises a token error if something goes wrong' do
|
47
|
+
stub_request(:delete, 'https://first.last:password@abs.example.com/api/v2/token/utpg2i2xswor6h8ttjhu3d47z53yy47y')
|
48
|
+
.to_return(:status => 500, :body => '{"ok":false}', :headers => {})
|
49
|
+
|
50
|
+
expect { Auth.delete_token(false, @abs_url, 'first.last', 'password', @token) }.to raise_error(TokenError)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'raises a token error if no token provided' do
|
54
|
+
expect { Auth.delete_token(false, @abs_url, 'first.last', 'password', nil) }.to raise_error(TokenError)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#token_status' do
|
59
|
+
before :each do
|
60
|
+
@token_status_response = '{"ok":true,"utpg2i2xswor6h8ttjhu3d47z53yy47y":{"created":"2015-04-28 19:17:47 -0700"}}'
|
61
|
+
@token = 'utpg2i2xswor6h8ttjhu3d47z53yy47y'
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'checks the status of a token' do
|
65
|
+
stub_request(:get, "#{@abs_url}/token/utpg2i2xswor6h8ttjhu3d47z53yy47y")
|
66
|
+
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3' })
|
67
|
+
.to_return(:status => 200, :body => @token_status_response, :headers => {})
|
68
|
+
|
69
|
+
expect(Auth.token_status(false, @abs_url, @token)).to eq JSON.parse(@token_status_response)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'raises a token error if something goes wrong' do
|
73
|
+
stub_request(:get, "#{@abs_url}/token/utpg2i2xswor6h8ttjhu3d47z53yy47y")
|
74
|
+
.with(:headers => { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3' })
|
75
|
+
.to_return(:status => 500, :body => '{"ok":false}', :headers => {})
|
76
|
+
|
77
|
+
expect { Auth.token_status(false, @abs_url, @token) }.to raise_error(TokenError)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'raises a token error if no token provided' do
|
81
|
+
expect { Auth.token_status(false, @abs_url, nil) }.to raise_error(TokenError)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'vmfloaty/utils'
|
5
|
+
require 'vmfloaty/errors'
|
6
|
+
require 'vmfloaty/abs'
|
7
|
+
|
8
|
+
describe ABS do
|
9
|
+
before :each do
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#format' do
|
13
|
+
it 'returns an hash formatted like a vmpooler return' do
|
14
|
+
abs_formatted_response = [
|
15
|
+
{ 'hostname' => 'aaaaaaaaaaaaaaa.delivery.puppetlabs.net', 'type' => 'centos-7.2-x86_64', 'engine' => 'vmpooler' },
|
16
|
+
{ 'hostname' => 'aaaaaaaaaaaaaab.delivery.puppetlabs.net', 'type' => 'centos-7.2-x86_64', 'engine' => 'vmpooler' },
|
17
|
+
{ 'hostname' => 'aaaaaaaaaaaaaac.delivery.puppetlabs.net', 'type' => 'ubuntu-7.2-x86_64', 'engine' => 'vmpooler' },
|
18
|
+
]
|
19
|
+
|
20
|
+
vmpooler_formatted_response = ABS.translated(abs_formatted_response)
|
21
|
+
|
22
|
+
vmpooler_formatted_compare = {
|
23
|
+
'centos-7.2-x86_64' => {},
|
24
|
+
'ubuntu-7.2-x86_64' => {},
|
25
|
+
}
|
26
|
+
|
27
|
+
vmpooler_formatted_compare['centos-7.2-x86_64']['hostname'] = ['aaaaaaaaaaaaaaa.delivery.puppetlabs.net', 'aaaaaaaaaaaaaab.delivery.puppetlabs.net']
|
28
|
+
vmpooler_formatted_compare['ubuntu-7.2-x86_64']['hostname'] = ['aaaaaaaaaaaaaac.delivery.puppetlabs.net']
|
29
|
+
|
30
|
+
vmpooler_formatted_compare['ok'] = true
|
31
|
+
|
32
|
+
expect(vmpooler_formatted_response).to eq(vmpooler_formatted_compare)
|
33
|
+
vmpooler_formatted_response.delete('ok')
|
34
|
+
vmpooler_formatted_compare.delete('ok')
|
35
|
+
expect(vmpooler_formatted_response).to eq(vmpooler_formatted_compare)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'won\'t delete a job if not all vms are listed' do
|
39
|
+
hosts = ['host1']
|
40
|
+
allocated_resources = [
|
41
|
+
{
|
42
|
+
'hostname' => 'host1',
|
43
|
+
},
|
44
|
+
{
|
45
|
+
'hostname' => 'host2',
|
46
|
+
},
|
47
|
+
]
|
48
|
+
expect(ABS.all_job_resources_accounted_for(allocated_resources, hosts)).to eq(false)
|
49
|
+
|
50
|
+
hosts = ['host1', 'host2']
|
51
|
+
allocated_resources = [
|
52
|
+
{
|
53
|
+
'hostname' => 'host1',
|
54
|
+
},
|
55
|
+
{
|
56
|
+
'hostname' => 'host2',
|
57
|
+
},
|
58
|
+
]
|
59
|
+
expect(ABS.all_job_resources_accounted_for(allocated_resources, hosts)).to eq(true)
|
60
|
+
end
|
61
|
+
|
62
|
+
before :each do
|
63
|
+
@abs_url = 'https://abs.example.com'
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#test_abs_status_queue_endpoint' do
|
67
|
+
before :each do
|
68
|
+
# rubocop:disable Metrics/LineLength
|
69
|
+
@active_requests_response = '
|
70
|
+
[
|
71
|
+
"{ \"state\":\"allocated\",\"last_processed\":\"2019-12-16 23:00:34 +0000\",\"allocated_resources\":[{\"hostname\":\"take-this.delivery.puppetlabs.net\",\"type\":\"win-2012r2-x86_64\",\"engine\":\"vmpooler\"}],\"audit_log\":{\"2019-12-13 16:45:29 +0000\":\"Allocated take-this.delivery.puppetlabs.net for job 1576255517241\"},\"request\":{\"resources\":{\"win-2012r2-x86_64\":1},\"job\":{\"id\":\"1576255517241\",\"tags\":{\"user\":\"test-user\"},\"user\":\"test-user\",\"time-received\":1576255519},\"priority\":1}}",
|
72
|
+
"null",
|
73
|
+
"{\"state\":\"allocated\",\"last_processed\":\"2019-12-16 23:00:34 +0000\",\"allocated_resources\":[{\"hostname\":\"not-this.delivery.puppetlabs.net\",\"type\":\"win-2012r2-x86_64\",\"engine\":\"vmpooler\"}],\"audit_log\":{\"2019-12-13 16:46:14 +0000\":\"Allocated not-this.delivery.puppetlabs.net for job 1576255565159\"},\"request\":{\"resources\":{\"win-2012r2-x86_64\":1},\"job\":{\"id\":\"1576255565159\",\"tags\":{\"user\":\"not-test-user\"},\"user\":\"not-test-user\",\"time-received\":1576255566},\"priority\":1}}"
|
74
|
+
]'
|
75
|
+
# rubocop:enable Metrics/LineLength
|
76
|
+
@token = 'utpg2i2xswor6h8ttjhu3d47z53yy47y'
|
77
|
+
@test_user = 'test-user'
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'will skip a line with a null value returned from abs' do
|
81
|
+
stub_request(:get, 'https://abs.example.com/status/queue')
|
82
|
+
.to_return(:status => 200, :body => @active_requests_response, :headers => {})
|
83
|
+
|
84
|
+
ret = ABS.get_active_requests(false, @abs_url, @test_user)
|
85
|
+
|
86
|
+
expect(ret[0]).to include(
|
87
|
+
'allocated_resources' => [{
|
88
|
+
'hostname' => 'take-this.delivery.puppetlabs.net',
|
89
|
+
'type' => 'win-2012r2-x86_64',
|
90
|
+
'engine' => 'vmpooler',
|
91
|
+
}],
|
92
|
+
)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|