vmfloaty 1.2.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -9
- data/lib/vmfloaty/abs.rb +50 -51
- data/lib/vmfloaty/http.rb +2 -6
- data/lib/vmfloaty/logger.rb +13 -10
- data/lib/vmfloaty/nonstandard_pooler.rb +3 -2
- data/lib/vmfloaty/pooler.rb +19 -24
- data/lib/vmfloaty/service.rb +3 -3
- data/lib/vmfloaty/utils.rb +68 -64
- data/lib/vmfloaty/version.rb +1 -2
- data/lib/vmfloaty.rb +52 -57
- data/spec/spec_helper.rb +30 -5
- data/spec/vmfloaty/abs/auth_spec.rb +26 -17
- data/spec/vmfloaty/abs_spec.rb +52 -43
- data/spec/vmfloaty/auth_spec.rb +23 -13
- data/spec/vmfloaty/nonstandard_pooler_spec.rb +32 -31
- data/spec/vmfloaty/pooler_spec.rb +29 -26
- data/spec/vmfloaty/service_spec.rb +10 -10
- data/spec/vmfloaty/ssh_spec.rb +3 -3
- data/spec/vmfloaty/utils_spec.rb +178 -161
- metadata +13 -21
data/lib/vmfloaty/utils.rb
CHANGED
@@ -39,7 +39,10 @@ class Utils
|
|
39
39
|
# "engine"=>"vmpooler"
|
40
40
|
# }
|
41
41
|
|
42
|
-
|
42
|
+
unless response_body.delete('ok')
|
43
|
+
raise ArgumentError,
|
44
|
+
"Bad GET response passed to format_hosts: #{response_body.to_json}"
|
45
|
+
end
|
43
46
|
|
44
47
|
# vmpooler reports the domain separately from the hostname
|
45
48
|
domain = response_body.delete('domain')
|
@@ -50,7 +53,7 @@ class Utils
|
|
50
53
|
abs_job_id = response_body.delete('job_id')
|
51
54
|
result['job_id'] = abs_job_id unless abs_job_id.nil?
|
52
55
|
|
53
|
-
filtered_response_body = response_body.reject { |key, _|
|
56
|
+
filtered_response_body = response_body.reject { |key, _| %w[request_id ready].include?(key) }
|
54
57
|
filtered_response_body.each do |os, value|
|
55
58
|
hostnames = Array(value['hostname'])
|
56
59
|
hostnames.map! { |host| "#{host}.#{domain}" } if domain
|
@@ -106,7 +109,7 @@ class Utils
|
|
106
109
|
def self.pretty_print_hosts(verbose, service, hostnames = [], print_to_stderr = false, indent = 0)
|
107
110
|
output_target = print_to_stderr ? $stderr : $stdout
|
108
111
|
|
109
|
-
fetched_data =
|
112
|
+
fetched_data = get_host_data(verbose, service, hostnames)
|
110
113
|
fetched_data.each do |hostname, host_data|
|
111
114
|
case service.type
|
112
115
|
when 'ABS'
|
@@ -116,13 +119,14 @@ class Utils
|
|
116
119
|
|
117
120
|
output_target.puts "- [JobID:#{host_data['request']['job']['id']}] <#{host_data['state']}>"
|
118
121
|
host_data['allocated_resources'].each do |allocated_resources, _i|
|
119
|
-
if (allocated_resources['engine'] ==
|
122
|
+
if (allocated_resources['engine'] == 'vmpooler' || allocated_resources['engine'] == 'ondemand') && service.config['vmpooler_fallback']
|
120
123
|
vmpooler_service = service.clone
|
121
124
|
vmpooler_service.silent = true
|
122
125
|
vmpooler_service.maybe_use_vmpooler
|
123
|
-
|
126
|
+
pretty_print_hosts(verbose, vmpooler_service, allocated_resources['hostname'].split('.')[0],
|
127
|
+
print_to_stderr, indent + 2)
|
124
128
|
else
|
125
|
-
#TODO we could add more specific metadata for the other services, nspooler and aws
|
129
|
+
# TODO: we could add more specific metadata for the other services, nspooler and aws
|
126
130
|
output_target.puts " - #{allocated_resources['hostname']} (#{allocated_resources['type']})"
|
127
131
|
end
|
128
132
|
end
|
@@ -130,12 +134,17 @@ class Utils
|
|
130
134
|
tag_pairs = []
|
131
135
|
tag_pairs = host_data['tags'].map { |key, value| "#{key}: #{value}" } unless host_data['tags'].nil?
|
132
136
|
duration = "#{host_data['running']}/#{host_data['lifetime']} hours"
|
133
|
-
metadata = [host_data['template'], duration, *tag_pairs]
|
134
|
-
|
137
|
+
metadata = [host_data['state'], host_data['template'], duration, *tag_pairs]
|
138
|
+
message = "- #{hostname}.#{host_data['domain']} (#{metadata.join(', ')})".gsub(/^/, ' ' * indent)
|
139
|
+
if host_data['state'] && host_data['state'] == 'destroyed'
|
140
|
+
output_target.puts "- DESTROYED #{hostname}.#{host_data['domain']}".gsub(/^/, ' ' * indent)
|
141
|
+
else
|
142
|
+
output_target.puts message
|
143
|
+
end
|
135
144
|
when 'NonstandardPooler'
|
136
145
|
line = "- #{host_data['fqdn']} (#{host_data['os_triple']}"
|
137
146
|
line += ", #{host_data['hours_left_on_reservation']}h remaining"
|
138
|
-
line += ", reason: #{host_data['reserved_for_reason']}" unless host_data['reserved_for_reason'].empty?
|
147
|
+
line += ", reason: #{host_data['reserved_for_reason']}" unless host_data['reserved_for_reason'].nil? || host_data['reserved_for_reason'].empty?
|
139
148
|
line += ')'
|
140
149
|
output_target.puts line
|
141
150
|
else
|
@@ -148,30 +157,26 @@ class Utils
|
|
148
157
|
result = {}
|
149
158
|
hostnames = [hostnames] unless hostnames.is_a? Array
|
150
159
|
hostnames.each do |hostname|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
160
|
+
response = service.query(verbose, hostname)
|
161
|
+
host_data = response[hostname]
|
162
|
+
if block_given?
|
163
|
+
yield host_data result
|
164
|
+
else
|
165
|
+
case service.type
|
166
|
+
when 'ABS'
|
167
|
+
# For ABS, 'hostname' variable is the jobID
|
168
|
+
result[hostname] = host_data if host_data['state'] == 'allocated' || host_data['state'] == 'filled'
|
169
|
+
when 'Pooler'
|
170
|
+
result[hostname] = host_data
|
171
|
+
when 'NonstandardPooler'
|
172
|
+
result[hostname] = host_data
|
156
173
|
else
|
157
|
-
|
158
|
-
when 'ABS'
|
159
|
-
# For ABS, 'hostname' variable is the jobID
|
160
|
-
if host_data['state'] == 'allocated' || host_data['state'] == 'filled'
|
161
|
-
result[hostname] = host_data
|
162
|
-
end
|
163
|
-
when 'Pooler'
|
164
|
-
result[hostname] = host_data
|
165
|
-
when 'NonstandardPooler'
|
166
|
-
result[hostname] = host_data
|
167
|
-
else
|
168
|
-
raise "Invalid service type #{service.type}"
|
169
|
-
end
|
174
|
+
raise "Invalid service type #{service.type}"
|
170
175
|
end
|
171
|
-
rescue StandardError => e
|
172
|
-
FloatyLogger.error("Something went wrong while trying to gather information on #{hostname}:")
|
173
|
-
FloatyLogger.error(e)
|
174
176
|
end
|
177
|
+
rescue StandardError => e
|
178
|
+
FloatyLogger.error("Something went wrong while trying to gather information on #{hostname}:")
|
179
|
+
FloatyLogger.error(e)
|
175
180
|
end
|
176
181
|
result
|
177
182
|
end
|
@@ -187,18 +192,16 @@ class Utils
|
|
187
192
|
|
188
193
|
width = pools.keys.map(&:length).max
|
189
194
|
pools.each do |name, pool|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
FloatyLogger.error "#{name.ljust(width)} #{e.red}"
|
199
|
-
end
|
195
|
+
max = pool['max']
|
196
|
+
ready = pool['ready']
|
197
|
+
pending = pool['pending']
|
198
|
+
missing = max - ready - pending
|
199
|
+
char = 'o'
|
200
|
+
puts "#{name.ljust(width)} #{(char * ready).green}#{(char * pending).yellow}#{(char * missing).red}"
|
201
|
+
rescue StandardError => e
|
202
|
+
FloatyLogger.error "#{name.ljust(width)} #{e.red}"
|
200
203
|
end
|
201
|
-
puts message
|
204
|
+
puts message
|
202
205
|
when 'NonstandardPooler'
|
203
206
|
pools = status_response
|
204
207
|
pools.delete 'ok'
|
@@ -206,16 +209,14 @@ class Utils
|
|
206
209
|
|
207
210
|
width = pools.keys.map(&:length).max
|
208
211
|
pools.each do |name, pool|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
FloatyLogger.error "#{name.ljust(width)} #{e.red}"
|
218
|
-
end
|
212
|
+
max = pool['total_hosts']
|
213
|
+
ready = pool['available_hosts']
|
214
|
+
pending = pool['pending'] || 0 # not available for nspooler
|
215
|
+
missing = max - ready - pending
|
216
|
+
char = 'o'
|
217
|
+
puts "#{name.ljust(width)} #{(char * ready).green}#{(char * pending).yellow}#{(char * missing).red}"
|
218
|
+
rescue StandardError => e
|
219
|
+
FloatyLogger.error "#{name.ljust(width)} #{e.red}"
|
219
220
|
end
|
220
221
|
when 'ABS'
|
221
222
|
FloatyLogger.error 'ABS Not OK' unless status_response
|
@@ -251,11 +252,11 @@ class Utils
|
|
251
252
|
def self.get_service_config(config, options)
|
252
253
|
# The top-level url, user, and token values in the config file are treated as defaults
|
253
254
|
service_config = {
|
254
|
-
'url'
|
255
|
-
'user'
|
255
|
+
'url' => config['url'],
|
256
|
+
'user' => config['user'],
|
256
257
|
'token' => config['token'],
|
257
258
|
'vmpooler_fallback' => config['vmpooler_fallback'],
|
258
|
-
'type'
|
259
|
+
'type' => config['type'] || 'vmpooler'
|
259
260
|
}
|
260
261
|
|
261
262
|
if config['services']
|
@@ -266,7 +267,10 @@ class Utils
|
|
266
267
|
service_config.merge! values
|
267
268
|
else
|
268
269
|
# If the user provided a service name at the command line, use that service if posible, or fail
|
269
|
-
|
270
|
+
unless config['services'][options.service]
|
271
|
+
raise ArgumentError,
|
272
|
+
"Could not find a configured service named '#{options.service}' in ~/.vmfloaty.yml"
|
273
|
+
end
|
270
274
|
|
271
275
|
# If the service is configured but some values are missing, use the top-level defaults to fill them in
|
272
276
|
service_config.merge! config['services'][options.service]
|
@@ -290,22 +294,22 @@ class Utils
|
|
290
294
|
config = Conf.read_config
|
291
295
|
# The top-level url, user, and token values in the config file are treated as defaults
|
292
296
|
service_config = {
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
+
'url' => config['url'],
|
298
|
+
'user' => config['user'],
|
299
|
+
'token' => config['token'],
|
300
|
+
'type' => 'vmpooler'
|
297
301
|
}
|
298
302
|
|
299
303
|
# at a minimum, the url needs to be configured
|
300
304
|
if config['services'] && config['services'][vmpooler_fallback] && config['services'][vmpooler_fallback]['url']
|
301
305
|
# If the service is configured but some values are missing, use the top-level defaults to fill them in
|
302
306
|
service_config.merge! config['services'][vmpooler_fallback]
|
307
|
+
elsif vmpooler_fallback.nil?
|
308
|
+
raise ArgumentError,
|
309
|
+
"The abs service should have a key named 'vmpooler_fallback' in ~/.vmfloaty.yml with a value that points to a vmpooler service name use this format:\nservices:\n myabs:\n url: 'http://abs.com'\n user: 'superman'\n token: 'kryptonite'\n vmpooler_fallback: 'myvmpooler'\n myvmpooler:\n url: 'http://vmpooler.com'\n user: 'superman'\n token: 'kryptonite'"
|
303
310
|
else
|
304
|
-
|
305
|
-
|
306
|
-
else
|
307
|
-
raise ArgumentError, "Could not find a configured service named '#{vmpooler_fallback}' in ~/.vmfloaty.yml use this format:\nservices:\n #{vmpooler_fallback}:\n url: 'http://vmpooler.com'\n user: 'superman'\n token: 'kryptonite'"
|
308
|
-
end
|
311
|
+
raise ArgumentError,
|
312
|
+
"Could not find a configured service named '#{vmpooler_fallback}' in ~/.vmfloaty.yml use this format:\nservices:\n #{vmpooler_fallback}:\n url: 'http://vmpooler.com'\n user: 'superman'\n token: 'kryptonite'"
|
309
313
|
end
|
310
314
|
|
311
315
|
service_config
|
data/lib/vmfloaty/version.rb
CHANGED
data/lib/vmfloaty.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'commander'
|
5
|
-
require 'colorize'
|
6
5
|
require 'json'
|
7
6
|
require 'pp'
|
8
7
|
require 'uri'
|
@@ -20,7 +19,8 @@ class Vmfloaty
|
|
20
19
|
|
21
20
|
def run # rubocop:disable Metrics/AbcSize
|
22
21
|
program :version, Vmfloaty::VERSION
|
23
|
-
program :description,
|
22
|
+
program :description,
|
23
|
+
"A CLI helper tool for Puppet's vmpooler to help you stay afloat.\n\nConfiguration may be placed in a ~/.vmfloaty.yml file."
|
24
24
|
|
25
25
|
config = Conf.read_config
|
26
26
|
|
@@ -43,9 +43,7 @@ class Vmfloaty
|
|
43
43
|
c.option '--loglevel STRING', String, 'the log level to use (debug, info, error)'
|
44
44
|
c.action do |args, options|
|
45
45
|
verbose = options.verbose || config['verbose']
|
46
|
-
if options.loglevel
|
47
|
-
FloatyLogger.setlevel = options.loglevel
|
48
|
-
end
|
46
|
+
FloatyLogger.setlevel = options.loglevel if options.loglevel
|
49
47
|
service = Service.new(options, config)
|
50
48
|
use_token = !options.notoken
|
51
49
|
force = options.force
|
@@ -100,21 +98,19 @@ class Vmfloaty
|
|
100
98
|
c.option '--loglevel STRING', String, 'the log level to use (debug, info, error)'
|
101
99
|
c.action do |args, options|
|
102
100
|
verbose = options.verbose || config['verbose']
|
103
|
-
if options.loglevel
|
104
|
-
FloatyLogger.setlevel = options.loglevel
|
105
|
-
end
|
101
|
+
FloatyLogger.setlevel = options.loglevel if options.loglevel
|
106
102
|
|
107
103
|
service = Service.new(options, config)
|
108
104
|
filter = args[0]
|
109
105
|
|
110
106
|
if options.active
|
111
107
|
# list active vms
|
112
|
-
if service.type ==
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
108
|
+
running_vms = if service.type == 'ABS'
|
109
|
+
# this is actually job_ids
|
110
|
+
service.list_active_job_ids(verbose, service.url, service.user)
|
111
|
+
else
|
112
|
+
service.list_active(verbose)
|
113
|
+
end
|
118
114
|
host = URI.parse(service.url).host
|
119
115
|
if running_vms.empty?
|
120
116
|
if options.json
|
@@ -122,17 +118,15 @@ class Vmfloaty
|
|
122
118
|
else
|
123
119
|
FloatyLogger.info "You have no running VMs on #{host}"
|
124
120
|
end
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
Utils.
|
130
|
-
Utils.print_fqdn_for_host(service, hostname, host_data)
|
131
|
-
end
|
132
|
-
else
|
133
|
-
puts "Your VMs on #{host}:"
|
134
|
-
Utils.pretty_print_hosts(verbose, service, running_vms)
|
121
|
+
elsif options.json
|
122
|
+
puts Utils.get_host_data(verbose, service, running_vms).to_json
|
123
|
+
elsif options.hostnameonly
|
124
|
+
Utils.get_host_data(verbose, service, running_vms).each do |hostname, host_data|
|
125
|
+
Utils.print_fqdn_for_host(service, hostname, host_data)
|
135
126
|
end
|
127
|
+
else
|
128
|
+
puts "Your VMs on #{host}:"
|
129
|
+
Utils.pretty_print_hosts(verbose, service, running_vms)
|
136
130
|
end
|
137
131
|
else
|
138
132
|
# list available vms from pooler
|
@@ -164,7 +158,8 @@ class Vmfloaty
|
|
164
158
|
c.syntax = 'floaty modify hostname [options]'
|
165
159
|
c.summary = 'Modify a VM\'s tags, time to live, disk space, or reservation reason'
|
166
160
|
c.description = 'This command makes modifications to the virtual machines state in the pooler service. You can either append tags to the vm, increase how long it stays active for, or increase the amount of disk space.'
|
167
|
-
c.example 'Modifies myhost1 to have a TTL of 12 hours and adds a custom tag',
|
161
|
+
c.example 'Modifies myhost1 to have a TTL of 12 hours and adds a custom tag',
|
162
|
+
'floaty modify myhost1 --lifetime 12 --url https://myurl --token mytokenstring --tags \'{"tag":"myvalue"}\''
|
168
163
|
c.option '--verbose', 'Enables verbose output'
|
169
164
|
c.option '--service STRING', String, 'Configured pooler service name'
|
170
165
|
c.option '--url STRING', String, 'URL of pooler service'
|
@@ -185,18 +180,18 @@ class Vmfloaty
|
|
185
180
|
exit 1
|
186
181
|
end
|
187
182
|
running_vms =
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
183
|
+
if modify_all
|
184
|
+
service.list_active(verbose)
|
185
|
+
else
|
186
|
+
hostname.split(',')
|
187
|
+
end
|
193
188
|
|
194
189
|
tags = options.tags ? JSON.parse(options.tags) : nil
|
195
190
|
modify_hash = {
|
196
|
-
:
|
197
|
-
:
|
198
|
-
:
|
199
|
-
:
|
191
|
+
lifetime: options.lifetime,
|
192
|
+
disk: options.disk,
|
193
|
+
tags: tags,
|
194
|
+
reason: options.reason
|
200
195
|
}
|
201
196
|
modify_hash.delete_if { |_, value| value.nil? }
|
202
197
|
|
@@ -204,12 +199,10 @@ class Vmfloaty
|
|
204
199
|
ok = true
|
205
200
|
modified_hash = {}
|
206
201
|
running_vms.each do |vm|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
ok = false
|
212
|
-
end
|
202
|
+
modified_hash[vm] = service.modify(verbose, vm, modify_hash)
|
203
|
+
rescue ModifyError => e
|
204
|
+
FloatyLogger.error e
|
205
|
+
ok = false
|
213
206
|
end
|
214
207
|
if ok
|
215
208
|
if modify_all
|
@@ -241,9 +234,7 @@ class Vmfloaty
|
|
241
234
|
c.option '--loglevel STRING', String, 'the log level to use (debug, info, error)'
|
242
235
|
c.action do |args, options|
|
243
236
|
verbose = options.verbose || config['verbose']
|
244
|
-
if options.loglevel
|
245
|
-
FloatyLogger.setlevel = options.loglevel
|
246
|
-
end
|
237
|
+
FloatyLogger.setlevel = options.loglevel if options.loglevel
|
247
238
|
|
248
239
|
service = Service.new(options, config)
|
249
240
|
hostnames = args[0]
|
@@ -254,17 +245,17 @@ class Vmfloaty
|
|
254
245
|
successes = []
|
255
246
|
|
256
247
|
if delete_all
|
257
|
-
if service.type ==
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
248
|
+
running_vms = if service.type == 'ABS'
|
249
|
+
# this is actually job_ids
|
250
|
+
service.list_active_job_ids(verbose, service.url, service.user)
|
251
|
+
else
|
252
|
+
service.list_active(verbose)
|
253
|
+
end
|
263
254
|
if running_vms.empty?
|
264
255
|
if options.json
|
265
256
|
puts {}.to_json
|
266
257
|
else
|
267
|
-
FloatyLogger.info
|
258
|
+
FloatyLogger.info 'You have no running VMs.'
|
268
259
|
end
|
269
260
|
else
|
270
261
|
confirmed = true
|
@@ -328,7 +319,8 @@ class Vmfloaty
|
|
328
319
|
c.syntax = 'floaty snapshot hostname [options]'
|
329
320
|
c.summary = 'Takes a snapshot of a given vm'
|
330
321
|
c.description = 'Will request a snapshot be taken of the given hostname in the pooler service. This command is known to take a while depending on how much load is on the pooler service.'
|
331
|
-
c.example 'Takes a snapshot for a given host',
|
322
|
+
c.example 'Takes a snapshot for a given host',
|
323
|
+
'floaty snapshot myvm.example.com --url http://vmpooler.example.com --token a9znth9dn01t416hrguu56ze37t790bl'
|
332
324
|
c.option '--verbose', 'Enables verbose output'
|
333
325
|
c.option '--service STRING', String, 'Configured pooler service name'
|
334
326
|
c.option '--url STRING', String, 'URL of pooler service'
|
@@ -354,7 +346,8 @@ class Vmfloaty
|
|
354
346
|
c.syntax = 'floaty revert hostname snapshot [options]'
|
355
347
|
c.summary = 'Reverts a vm to a specified snapshot'
|
356
348
|
c.description = 'Given a snapshot SHA, vmfloaty will request a revert to the pooler service to go back to a previous snapshot.'
|
357
|
-
c.example 'Reverts to a snapshot for a given host',
|
349
|
+
c.example 'Reverts to a snapshot for a given host',
|
350
|
+
'floaty revert myvm.example.com n4eb4kdtp7rwv4x158366vd9jhac8btq --url http://vmpooler.example.com --token a9znth9dn01t416hrguu56ze37t790bl'
|
358
351
|
c.option '--verbose', 'Enables verbose output'
|
359
352
|
c.option '--service STRING', String, 'Configured pooler service name'
|
360
353
|
c.option '--url STRING', String, 'URL of pooler service'
|
@@ -366,7 +359,9 @@ class Vmfloaty
|
|
366
359
|
hostname = args[0]
|
367
360
|
snapshot_sha = args[1] || options.snapshot
|
368
361
|
|
369
|
-
|
362
|
+
if args[1] && options.snapshot
|
363
|
+
FloatyLogger.info "Two snapshot arguments were given....using snapshot #{snapshot_sha}"
|
364
|
+
end
|
370
365
|
|
371
366
|
begin
|
372
367
|
revert_req = service.revert(verbose, hostname, snapshot_sha)
|
@@ -391,9 +386,7 @@ class Vmfloaty
|
|
391
386
|
c.option '--loglevel STRING', String, 'the log level to use (debug, info, error)'
|
392
387
|
c.action do |_, options|
|
393
388
|
verbose = options.verbose || config['verbose']
|
394
|
-
if options.loglevel
|
395
|
-
FloatyLogger.setlevel = options.loglevel
|
396
|
-
end
|
389
|
+
FloatyLogger.setlevel = options.loglevel if options.loglevel
|
397
390
|
service = Service.new(options, config)
|
398
391
|
if options.json
|
399
392
|
pp service.status(verbose)
|
@@ -475,6 +468,8 @@ class Vmfloaty
|
|
475
468
|
c.option '--user STRING', String, 'User to authenticate with'
|
476
469
|
c.option '--token STRING', String, 'Token for pooler service'
|
477
470
|
c.option '--notoken', 'Makes a request without a token'
|
471
|
+
c.option '--priority STRING', 'Priority for supported backends(ABS) (High(1), Medium(2), Low(3))'
|
472
|
+
c.option '--ondemand', 'Requested vms are provisioned upon receival of the request, tracked by a request ID'
|
478
473
|
c.action do |args, options|
|
479
474
|
verbose = options.verbose || config['verbose']
|
480
475
|
service = Service.new(options, config)
|
@@ -527,7 +522,7 @@ class Vmfloaty
|
|
527
522
|
c.example 'Print a list of the valid service types', 'floaty service types'
|
528
523
|
c.example 'Print a sample config file with multiple services', 'floaty service examples'
|
529
524
|
c.example 'list vms from the service named "nspooler-prod"', 'floaty list --service nspooler-prod'
|
530
|
-
c.action do |args,
|
525
|
+
c.action do |args, _options|
|
531
526
|
action = args.first
|
532
527
|
|
533
528
|
example_config = Utils.strip_heredoc(<<-CONFIG)
|
data/spec/spec_helper.rb
CHANGED
@@ -1,12 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'simplecov'
|
4
|
-
require '
|
4
|
+
require 'simplecov-lcov'
|
5
|
+
require 'base64'
|
6
|
+
|
7
|
+
SimpleCov::Formatter::LcovFormatter.config do |c|
|
8
|
+
c.report_with_single_file = true
|
9
|
+
c.single_report_path = 'coverage/lcov.info'
|
10
|
+
end
|
11
|
+
|
12
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
|
13
|
+
[
|
14
|
+
SimpleCov::Formatter::HTMLFormatter,
|
15
|
+
SimpleCov::Formatter::LcovFormatter
|
16
|
+
]
|
17
|
+
)
|
5
18
|
|
6
|
-
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([
|
7
|
-
SimpleCov::Formatter::HTMLFormatter,
|
8
|
-
Coveralls::SimpleCov::Formatter
|
9
|
-
])
|
10
19
|
SimpleCov.start do
|
11
20
|
add_filter %r{^/spec/}
|
12
21
|
end
|
@@ -26,3 +35,19 @@ RSpec.configure do |config|
|
|
26
35
|
config.tty = true
|
27
36
|
config.formatter = :documentation
|
28
37
|
end
|
38
|
+
|
39
|
+
def get_headers(username: nil, password: nil, token: nil, content_type: nil, content_length: nil)
|
40
|
+
headers = {
|
41
|
+
'Accept' => '*/*',
|
42
|
+
'Accept-Encoding' => /gzip/,
|
43
|
+
'User-Agent' => /Faraday/,
|
44
|
+
}
|
45
|
+
if username && password
|
46
|
+
auth = Base64.encode64("#{username}:#{password}").chomp
|
47
|
+
headers['Authorization'] = "Basic #{auth}"
|
48
|
+
end
|
49
|
+
headers['X-Auth-Token'] = token if token
|
50
|
+
headers['Content-Type'] = content_type if content_type
|
51
|
+
headers['Content-Length'] = content_length.to_s if content_length
|
52
|
+
headers
|
53
|
+
end
|
@@ -3,7 +3,11 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
require_relative '../../../lib/vmfloaty/auth'
|
5
5
|
|
6
|
+
user = 'first.last'
|
7
|
+
pass = 'password'
|
8
|
+
|
6
9
|
describe Pooler do
|
10
|
+
|
7
11
|
before :each do
|
8
12
|
@abs_url = 'https://abs.example.com/api/v2'
|
9
13
|
end
|
@@ -15,18 +19,20 @@ describe Pooler do
|
|
15
19
|
end
|
16
20
|
|
17
21
|
it 'returns a token from abs' do
|
18
|
-
stub_request(:post, 'https://
|
19
|
-
.
|
22
|
+
stub_request(:post, 'https://abs.example.com/api/v2/token')
|
23
|
+
.with(headers: get_headers(username: user, password: pass, content_length: 0))
|
24
|
+
.to_return(status: 200, body: @get_token_response, headers: {})
|
20
25
|
|
21
|
-
token = Auth.get_token(false, @abs_url,
|
26
|
+
token = Auth.get_token(false, @abs_url, user, pass)
|
22
27
|
expect(token).to eq @token
|
23
28
|
end
|
24
29
|
|
25
30
|
it 'raises a token error if something goes wrong' do
|
26
|
-
stub_request(:post, 'https://
|
27
|
-
.
|
31
|
+
stub_request(:post, 'https://abs.example.com/api/v2/token')
|
32
|
+
.with(headers: get_headers(username: user, password: pass, content_length: 0))
|
33
|
+
.to_return(status: 500, body: '{"ok":false}', headers: {})
|
28
34
|
|
29
|
-
expect { Auth.get_token(false, @abs_url,
|
35
|
+
expect { Auth.get_token(false, @abs_url, user, pass) }.to raise_error(TokenError)
|
30
36
|
end
|
31
37
|
end
|
32
38
|
|
@@ -37,21 +43,24 @@ describe Pooler do
|
|
37
43
|
end
|
38
44
|
|
39
45
|
it 'deletes the specified token' do
|
40
|
-
stub_request(:delete, 'https://
|
41
|
-
.
|
46
|
+
stub_request(:delete, 'https://abs.example.com/api/v2/token/utpg2i2xswor6h8ttjhu3d47z53yy47y')
|
47
|
+
.with(headers: get_headers(username: user, password: pass))
|
48
|
+
.to_return(status: 200, body: @delete_token_response, headers: {})
|
42
49
|
|
43
|
-
expect(Auth.delete_token(false, @abs_url,
|
50
|
+
expect(Auth.delete_token(false, @abs_url, user, pass,
|
51
|
+
@token)).to eq JSON.parse(@delete_token_response)
|
44
52
|
end
|
45
53
|
|
46
54
|
it 'raises a token error if something goes wrong' do
|
47
|
-
stub_request(:delete, 'https://
|
48
|
-
.
|
55
|
+
stub_request(:delete, 'https://abs.example.com/api/v2/token/utpg2i2xswor6h8ttjhu3d47z53yy47y')
|
56
|
+
.with(headers: get_headers(username: user, password: pass))
|
57
|
+
.to_return(status: 500, body: '{"ok":false}', headers: {})
|
49
58
|
|
50
|
-
expect { Auth.delete_token(false, @abs_url,
|
59
|
+
expect { Auth.delete_token(false, @abs_url, user, pass, @token) }.to raise_error(TokenError)
|
51
60
|
end
|
52
61
|
|
53
62
|
it 'raises a token error if no token provided' do
|
54
|
-
expect { Auth.delete_token(false, @abs_url,
|
63
|
+
expect { Auth.delete_token(false, @abs_url, user, pass, nil) }.to raise_error(TokenError)
|
55
64
|
end
|
56
65
|
end
|
57
66
|
|
@@ -63,16 +72,16 @@ describe Pooler do
|
|
63
72
|
|
64
73
|
it 'checks the status of a token' do
|
65
74
|
stub_request(:get, "#{@abs_url}/token/utpg2i2xswor6h8ttjhu3d47z53yy47y")
|
66
|
-
.with(:
|
67
|
-
.to_return(:
|
75
|
+
.with(headers: get_headers)
|
76
|
+
.to_return(status: 200, body: @token_status_response, headers: {})
|
68
77
|
|
69
78
|
expect(Auth.token_status(false, @abs_url, @token)).to eq JSON.parse(@token_status_response)
|
70
79
|
end
|
71
80
|
|
72
81
|
it 'raises a token error if something goes wrong' do
|
73
82
|
stub_request(:get, "#{@abs_url}/token/utpg2i2xswor6h8ttjhu3d47z53yy47y")
|
74
|
-
.with(:
|
75
|
-
.to_return(:
|
83
|
+
.with(headers: get_headers)
|
84
|
+
.to_return(status: 500, body: '{"ok":false}', headers: {})
|
76
85
|
|
77
86
|
expect { Auth.token_status(false, @abs_url, @token) }.to raise_error(TokenError)
|
78
87
|
end
|