vmfloaty 1.1.1 → 1.5.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 +2 -2
- data/lib/vmfloaty/abs.rb +58 -61
- data/lib/vmfloaty/http.rb +2 -6
- data/lib/vmfloaty/logger.rb +19 -3
- data/lib/vmfloaty/nonstandard_pooler.rb +3 -2
- data/lib/vmfloaty/pooler.rb +20 -25
- data/lib/vmfloaty/service.rb +5 -5
- data/lib/vmfloaty/utils.rb +66 -62
- data/lib/vmfloaty/version.rb +1 -2
- data/lib/vmfloaty.rb +64 -50
- 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 +19 -13
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,8 +134,13 @@ 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 message.colorize(:red)
|
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"
|
@@ -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,16 +192,14 @@ 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
204
|
puts message.colorize(status_response['status']['ok'] ? :default : :red)
|
202
205
|
when 'NonstandardPooler'
|
@@ -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
@@ -20,7 +20,8 @@ class Vmfloaty
|
|
20
20
|
|
21
21
|
def run # rubocop:disable Metrics/AbcSize
|
22
22
|
program :version, Vmfloaty::VERSION
|
23
|
-
program :description,
|
23
|
+
program :description,
|
24
|
+
"A CLI helper tool for Puppet's vmpooler to help you stay afloat.\n\nConfiguration may be placed in a ~/.vmfloaty.yml file."
|
24
25
|
|
25
26
|
config = Conf.read_config
|
26
27
|
|
@@ -39,8 +40,11 @@ class Vmfloaty
|
|
39
40
|
c.option '--force', 'Forces vmfloaty to get requested vms'
|
40
41
|
c.option '--json', 'Prints retrieved vms in JSON format'
|
41
42
|
c.option '--ondemand', 'Requested vms are provisioned upon receival of the request, tracked by a request ID'
|
43
|
+
c.option '--continue STRING', String, 'resume polling ABS for job_id, for use when the cli was interrupted'
|
44
|
+
c.option '--loglevel STRING', String, 'the log level to use (debug, info, error)'
|
42
45
|
c.action do |args, options|
|
43
46
|
verbose = options.verbose || config['verbose']
|
47
|
+
FloatyLogger.setlevel = options.loglevel if options.loglevel
|
44
48
|
service = Service.new(options, config)
|
45
49
|
use_token = !options.notoken
|
46
50
|
force = options.force
|
@@ -52,6 +56,11 @@ class Vmfloaty
|
|
52
56
|
|
53
57
|
os_types = Utils.generate_os_hash(args)
|
54
58
|
|
59
|
+
if os_types.empty?
|
60
|
+
FloatyLogger.error 'No operating systems provided to obtain. See `floaty get --help` for more information on how to get VMs.'
|
61
|
+
exit 1
|
62
|
+
end
|
63
|
+
|
55
64
|
max_pool_request = 5
|
56
65
|
large_pool_requests = os_types.select { |_, v| v > max_pool_request }
|
57
66
|
if !large_pool_requests.empty? && !force
|
@@ -60,12 +69,7 @@ class Vmfloaty
|
|
60
69
|
exit 1
|
61
70
|
end
|
62
71
|
|
63
|
-
|
64
|
-
FloatyLogger.error 'No operating systems provided to obtain. See `floaty get --help` for more information on how to get VMs.'
|
65
|
-
exit 1
|
66
|
-
end
|
67
|
-
|
68
|
-
response = service.retrieve(verbose, os_types, use_token, options.ondemand)
|
72
|
+
response = service.retrieve(verbose, os_types, use_token, options.ondemand, options.continue)
|
69
73
|
request_id = response['request_id'] if options.ondemand
|
70
74
|
response = service.wait_for_request(verbose, request_id) if options.ondemand
|
71
75
|
|
@@ -92,20 +96,22 @@ class Vmfloaty
|
|
92
96
|
c.option '--token STRING', String, 'Token for pooler service'
|
93
97
|
c.option '--url STRING', String, 'URL of pooler service'
|
94
98
|
c.option '--user STRING', String, 'User to authenticate with'
|
99
|
+
c.option '--loglevel STRING', String, 'the log level to use (debug, info, error)'
|
95
100
|
c.action do |args, options|
|
96
101
|
verbose = options.verbose || config['verbose']
|
102
|
+
FloatyLogger.setlevel = options.loglevel if options.loglevel
|
97
103
|
|
98
104
|
service = Service.new(options, config)
|
99
105
|
filter = args[0]
|
100
106
|
|
101
107
|
if options.active
|
102
108
|
# list active vms
|
103
|
-
if service.type ==
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
+
running_vms = if service.type == 'ABS'
|
110
|
+
# this is actually job_ids
|
111
|
+
service.list_active_job_ids(verbose, service.url, service.user)
|
112
|
+
else
|
113
|
+
service.list_active(verbose)
|
114
|
+
end
|
109
115
|
host = URI.parse(service.url).host
|
110
116
|
if running_vms.empty?
|
111
117
|
if options.json
|
@@ -113,17 +119,15 @@ class Vmfloaty
|
|
113
119
|
else
|
114
120
|
FloatyLogger.info "You have no running VMs on #{host}"
|
115
121
|
end
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
Utils.
|
121
|
-
Utils.print_fqdn_for_host(service, hostname, host_data)
|
122
|
-
end
|
123
|
-
else
|
124
|
-
puts "Your VMs on #{host}:"
|
125
|
-
Utils.pretty_print_hosts(verbose, service, running_vms)
|
122
|
+
elsif options.json
|
123
|
+
puts Utils.get_host_data(verbose, service, running_vms).to_json
|
124
|
+
elsif options.hostnameonly
|
125
|
+
Utils.get_host_data(verbose, service, running_vms).each do |hostname, host_data|
|
126
|
+
Utils.print_fqdn_for_host(service, hostname, host_data)
|
126
127
|
end
|
128
|
+
else
|
129
|
+
puts "Your VMs on #{host}:"
|
130
|
+
Utils.pretty_print_hosts(verbose, service, running_vms)
|
127
131
|
end
|
128
132
|
else
|
129
133
|
# list available vms from pooler
|
@@ -155,7 +159,8 @@ class Vmfloaty
|
|
155
159
|
c.syntax = 'floaty modify hostname [options]'
|
156
160
|
c.summary = 'Modify a VM\'s tags, time to live, disk space, or reservation reason'
|
157
161
|
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.'
|
158
|
-
c.example 'Modifies myhost1 to have a TTL of 12 hours and adds a custom tag',
|
162
|
+
c.example 'Modifies myhost1 to have a TTL of 12 hours and adds a custom tag',
|
163
|
+
'floaty modify myhost1 --lifetime 12 --url https://myurl --token mytokenstring --tags \'{"tag":"myvalue"}\''
|
159
164
|
c.option '--verbose', 'Enables verbose output'
|
160
165
|
c.option '--service STRING', String, 'Configured pooler service name'
|
161
166
|
c.option '--url STRING', String, 'URL of pooler service'
|
@@ -176,18 +181,18 @@ class Vmfloaty
|
|
176
181
|
exit 1
|
177
182
|
end
|
178
183
|
running_vms =
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
+
if modify_all
|
185
|
+
service.list_active(verbose)
|
186
|
+
else
|
187
|
+
hostname.split(',')
|
188
|
+
end
|
184
189
|
|
185
190
|
tags = options.tags ? JSON.parse(options.tags) : nil
|
186
191
|
modify_hash = {
|
187
|
-
:
|
188
|
-
:
|
189
|
-
:
|
190
|
-
:
|
192
|
+
lifetime: options.lifetime,
|
193
|
+
disk: options.disk,
|
194
|
+
tags: tags,
|
195
|
+
reason: options.reason
|
191
196
|
}
|
192
197
|
modify_hash.delete_if { |_, value| value.nil? }
|
193
198
|
|
@@ -195,12 +200,10 @@ class Vmfloaty
|
|
195
200
|
ok = true
|
196
201
|
modified_hash = {}
|
197
202
|
running_vms.each do |vm|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
ok = false
|
203
|
-
end
|
203
|
+
modified_hash[vm] = service.modify(verbose, vm, modify_hash)
|
204
|
+
rescue ModifyError => e
|
205
|
+
FloatyLogger.error e
|
206
|
+
ok = false
|
204
207
|
end
|
205
208
|
if ok
|
206
209
|
if modify_all
|
@@ -229,8 +232,11 @@ class Vmfloaty
|
|
229
232
|
c.option '--token STRING', String, 'Token for pooler service'
|
230
233
|
c.option '--url STRING', String, 'URL of pooler service'
|
231
234
|
c.option '--user STRING', String, 'User to authenticate with'
|
235
|
+
c.option '--loglevel STRING', String, 'the log level to use (debug, info, error)'
|
232
236
|
c.action do |args, options|
|
233
237
|
verbose = options.verbose || config['verbose']
|
238
|
+
FloatyLogger.setlevel = options.loglevel if options.loglevel
|
239
|
+
|
234
240
|
service = Service.new(options, config)
|
235
241
|
hostnames = args[0]
|
236
242
|
delete_all = options.all
|
@@ -240,17 +246,17 @@ class Vmfloaty
|
|
240
246
|
successes = []
|
241
247
|
|
242
248
|
if delete_all
|
243
|
-
if service.type ==
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
+
running_vms = if service.type == 'ABS'
|
250
|
+
# this is actually job_ids
|
251
|
+
service.list_active_job_ids(verbose, service.url, service.user)
|
252
|
+
else
|
253
|
+
service.list_active(verbose)
|
254
|
+
end
|
249
255
|
if running_vms.empty?
|
250
256
|
if options.json
|
251
257
|
puts {}.to_json
|
252
258
|
else
|
253
|
-
FloatyLogger.info
|
259
|
+
FloatyLogger.info 'You have no running VMs.'
|
254
260
|
end
|
255
261
|
else
|
256
262
|
confirmed = true
|
@@ -314,7 +320,8 @@ class Vmfloaty
|
|
314
320
|
c.syntax = 'floaty snapshot hostname [options]'
|
315
321
|
c.summary = 'Takes a snapshot of a given vm'
|
316
322
|
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.'
|
317
|
-
c.example 'Takes a snapshot for a given host',
|
323
|
+
c.example 'Takes a snapshot for a given host',
|
324
|
+
'floaty snapshot myvm.example.com --url http://vmpooler.example.com --token a9znth9dn01t416hrguu56ze37t790bl'
|
318
325
|
c.option '--verbose', 'Enables verbose output'
|
319
326
|
c.option '--service STRING', String, 'Configured pooler service name'
|
320
327
|
c.option '--url STRING', String, 'URL of pooler service'
|
@@ -340,7 +347,8 @@ class Vmfloaty
|
|
340
347
|
c.syntax = 'floaty revert hostname snapshot [options]'
|
341
348
|
c.summary = 'Reverts a vm to a specified snapshot'
|
342
349
|
c.description = 'Given a snapshot SHA, vmfloaty will request a revert to the pooler service to go back to a previous snapshot.'
|
343
|
-
c.example 'Reverts to a snapshot for a given host',
|
350
|
+
c.example 'Reverts to a snapshot for a given host',
|
351
|
+
'floaty revert myvm.example.com n4eb4kdtp7rwv4x158366vd9jhac8btq --url http://vmpooler.example.com --token a9znth9dn01t416hrguu56ze37t790bl'
|
344
352
|
c.option '--verbose', 'Enables verbose output'
|
345
353
|
c.option '--service STRING', String, 'Configured pooler service name'
|
346
354
|
c.option '--url STRING', String, 'URL of pooler service'
|
@@ -352,7 +360,9 @@ class Vmfloaty
|
|
352
360
|
hostname = args[0]
|
353
361
|
snapshot_sha = args[1] || options.snapshot
|
354
362
|
|
355
|
-
|
363
|
+
if args[1] && options.snapshot
|
364
|
+
FloatyLogger.info "Two snapshot arguments were given....using snapshot #{snapshot_sha}"
|
365
|
+
end
|
356
366
|
|
357
367
|
begin
|
358
368
|
revert_req = service.revert(verbose, hostname, snapshot_sha)
|
@@ -374,8 +384,10 @@ class Vmfloaty
|
|
374
384
|
c.option '--service STRING', String, 'Configured pooler service name'
|
375
385
|
c.option '--url STRING', String, 'URL of pooler service'
|
376
386
|
c.option '--json', 'Prints status in JSON format'
|
387
|
+
c.option '--loglevel STRING', String, 'the log level to use (debug, info, error)'
|
377
388
|
c.action do |_, options|
|
378
389
|
verbose = options.verbose || config['verbose']
|
390
|
+
FloatyLogger.setlevel = options.loglevel if options.loglevel
|
379
391
|
service = Service.new(options, config)
|
380
392
|
if options.json
|
381
393
|
pp service.status(verbose)
|
@@ -457,6 +469,8 @@ class Vmfloaty
|
|
457
469
|
c.option '--user STRING', String, 'User to authenticate with'
|
458
470
|
c.option '--token STRING', String, 'Token for pooler service'
|
459
471
|
c.option '--notoken', 'Makes a request without a token'
|
472
|
+
c.option '--priority STRING', 'Priority for supported backends(ABS) (High(1), Medium(2), Low(3))'
|
473
|
+
c.option '--ondemand', 'Requested vms are provisioned upon receival of the request, tracked by a request ID'
|
460
474
|
c.action do |args, options|
|
461
475
|
verbose = options.verbose || config['verbose']
|
462
476
|
service = Service.new(options, config)
|
@@ -509,7 +523,7 @@ class Vmfloaty
|
|
509
523
|
c.example 'Print a list of the valid service types', 'floaty service types'
|
510
524
|
c.example 'Print a sample config file with multiple services', 'floaty service examples'
|
511
525
|
c.example 'list vms from the service named "nspooler-prod"', 'floaty list --service nspooler-prod'
|
512
|
-
c.action do |args,
|
526
|
+
c.action do |args, _options|
|
513
527
|
action = args.first
|
514
528
|
|
515
529
|
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
|