mirror of
https://github.com/puppetlabs/vmfloaty.git
synced 2026-01-26 13:28:42 -05:00
Integrate nonstandard pooler service into vmfloaty
This commit is contained in:
parent
e78bcc6216
commit
ca5b0f5e8b
12 changed files with 1190 additions and 482 deletions
450
lib/vmfloaty.rb
450
lib/vmfloaty.rb
|
|
@ -5,11 +5,13 @@ require 'commander'
|
|||
require 'colorize'
|
||||
require 'json'
|
||||
require 'pp'
|
||||
require 'uri'
|
||||
require 'vmfloaty/auth'
|
||||
require 'vmfloaty/pooler'
|
||||
require 'vmfloaty/version'
|
||||
require 'vmfloaty/conf'
|
||||
require 'vmfloaty/utils'
|
||||
require 'vmfloaty/service'
|
||||
require 'vmfloaty/ssh'
|
||||
|
||||
class Vmfloaty
|
||||
|
|
@ -35,11 +37,8 @@ class Vmfloaty
|
|||
c.option '--force', 'Forces vmfloaty to get requested vms'
|
||||
c.action do |args, options|
|
||||
verbose = options.verbose || config['verbose']
|
||||
service_config = Utils.get_service_from_config(config, options.service)
|
||||
token = options.token || service_config['token'] || config['token']
|
||||
user = options.user ||= service_config['user'] || config['user']
|
||||
url = options.url ||= service_config['url'] || config['url']
|
||||
no_token = options.notoken
|
||||
service = Service.new(options, config)
|
||||
use_token = !options.notoken
|
||||
force = options.force
|
||||
|
||||
if args.empty?
|
||||
|
|
@ -50,61 +49,20 @@ class Vmfloaty
|
|||
os_types = Utils.generate_os_hash(args)
|
||||
|
||||
max_pool_request = 5
|
||||
large_pool_requests = os_types.select{|k,v| v > max_pool_request}
|
||||
large_pool_requests = os_types.select{|_,v| v > max_pool_request}
|
||||
if ! large_pool_requests.empty? and ! force
|
||||
STDERR.puts "Requesting vms over #{max_pool_request} requires a --force flag."
|
||||
STDERR.puts "Try again with `floaty get --force`"
|
||||
exit 1
|
||||
end
|
||||
|
||||
unless os_types.empty?
|
||||
if no_token
|
||||
begin
|
||||
response = Pooler.retrieve(verbose, os_types, nil, url)
|
||||
rescue MissingParamError
|
||||
STDERR.puts e
|
||||
STDERR.puts "See `floaty get --help` for more information on how to get VMs."
|
||||
rescue AuthError => e
|
||||
STDERR.puts e
|
||||
exit 1
|
||||
end
|
||||
puts Utils.format_hosts(response)
|
||||
exit 0
|
||||
else
|
||||
unless token
|
||||
puts "No token found. Retrieving a token..."
|
||||
if !user
|
||||
STDERR.puts "You did not provide a user to authenticate to the pooler service with"
|
||||
exit 1
|
||||
end
|
||||
pass = password "Enter your pooler service password please:", '*'
|
||||
begin
|
||||
token = Auth.get_token(verbose, url, user, pass)
|
||||
rescue TokenError => e
|
||||
STDERR.puts e
|
||||
exit 1
|
||||
end
|
||||
|
||||
puts "\nToken retrieved!"
|
||||
puts token
|
||||
end
|
||||
|
||||
begin
|
||||
response = Pooler.retrieve(verbose, os_types, token, url)
|
||||
rescue MissingParamError
|
||||
STDERR.puts e
|
||||
STDERR.puts "See `floaty get --help` for more information on how to get VMs."
|
||||
rescue AuthError => e
|
||||
STDERR.puts e
|
||||
exit 1
|
||||
end
|
||||
puts Utils.format_hosts(response)
|
||||
exit 0
|
||||
end
|
||||
else
|
||||
if os_types.empty?
|
||||
STDERR.puts "No operating systems provided to obtain. See `floaty get --help` for more information on how to get VMs."
|
||||
exit 1
|
||||
end
|
||||
|
||||
response = service.retrieve(verbose, os_types, use_token)
|
||||
puts Utils.format_hosts(response)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -120,30 +78,22 @@ class Vmfloaty
|
|||
c.option '--url STRING', String, 'URL of pooler service'
|
||||
c.action do |args, options|
|
||||
verbose = options.verbose || config['verbose']
|
||||
service_config = Utils.get_service_from_config(config, options.service)
|
||||
service = Service.new(options, config)
|
||||
filter = args[0]
|
||||
url = options.url ||= service_config['url'] ||=config['url']
|
||||
token = options.token || service_config['token'] || config['token']
|
||||
active = options.active
|
||||
|
||||
if active
|
||||
if options.active
|
||||
# list active vms
|
||||
begin
|
||||
running_vms = Utils.get_all_token_vms(verbose, url, token)
|
||||
rescue TokenError => e
|
||||
STDERR.puts e
|
||||
exit 1
|
||||
rescue Exception => e
|
||||
STDERR.puts e
|
||||
exit 1
|
||||
end
|
||||
|
||||
if ! running_vms.nil?
|
||||
Utils.prettyprint_hosts(running_vms, verbose, url)
|
||||
running_vms = service.list_active(verbose)
|
||||
host = URI.parse(service.url).host
|
||||
if running_vms.empty?
|
||||
puts "You have no running VMs on #{host}"
|
||||
else
|
||||
puts "Your VMs on #{host}:"
|
||||
Utils.pretty_print_hosts(verbose, service, running_vms)
|
||||
end
|
||||
else
|
||||
# list available vms from pooler
|
||||
os_list = Pooler.list(verbose, url, filter)
|
||||
os_list = service.list(verbose, filter)
|
||||
puts os_list
|
||||
end
|
||||
end
|
||||
|
|
@ -159,136 +109,67 @@ class Vmfloaty
|
|||
c.option '--url STRING', String, 'URL of pooler service'
|
||||
c.action do |args, options|
|
||||
verbose = options.verbose || config['verbose']
|
||||
service_config = Utils.get_service_from_config(config, options.service)
|
||||
url = options.url ||= service_config['url'] ||= config['url']
|
||||
service = Service.new(options, config)
|
||||
hostname = args[0]
|
||||
|
||||
query_req = Pooler.query(verbose, url, hostname)
|
||||
query_req = service.query(verbose, hostname)
|
||||
pp query_req
|
||||
end
|
||||
end
|
||||
|
||||
command :modify do |c|
|
||||
c.syntax = 'floaty modify hostname [options]'
|
||||
c.summary = 'Modify a vms tags, time to live, and disk space'
|
||||
c.summary = 'Modify a VM\'s tags, time to live, disk space, or reservation reason'
|
||||
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.'
|
||||
c.example 'Modifies myhost1 to have a TTL of 12 hours and adds a custom tag', 'floaty modify myhost1 --lifetime 12 --url https://myurl --token mytokenstring --tags \'{"tag":"myvalue"}\''
|
||||
c.option '--verbose', 'Enables verbose output'
|
||||
c.option '--service STRING', String, 'Configured pooler service name'
|
||||
c.option '--url STRING', String, 'URL of pooler service'
|
||||
c.option '--token STRING', String, 'Token for pooler service'
|
||||
c.option '--lifetime INT', Integer, 'VM TTL (Integer, in hours)'
|
||||
c.option '--disk INT', Integer, 'Increases VM disk space (Integer, in gb)'
|
||||
c.option '--tags STRING', String, 'free-form VM tagging (json)'
|
||||
c.option '--lifetime INT', Integer, 'VM TTL (Integer, in hours) [vmpooler only]'
|
||||
c.option '--disk INT', Integer, 'Increases VM disk space (Integer, in gb) [vmpooler only]'
|
||||
c.option '--tags STRING', String, 'free-form VM tagging (json) [vmpooler only]'
|
||||
c.option '--reason STRING', String, 'VM reservation reason [nspooler only]'
|
||||
c.option '--all', 'Modifies all vms acquired by a token'
|
||||
c.action do |args, options|
|
||||
verbose = options.verbose || config['verbose']
|
||||
service_config = Utils.get_service_from_config(config, options.service)
|
||||
url = options.url ||= service_config['url'] ||= config['url']
|
||||
service = Service.new(options, config)
|
||||
hostname = args[0]
|
||||
lifetime = options.lifetime
|
||||
disk = options.disk
|
||||
tags = JSON.parse(options.tags) if options.tags
|
||||
token = options.token || service_config['token'] || config['token']
|
||||
modify_all = options.all
|
||||
|
||||
running_vms = nil
|
||||
|
||||
if modify_all
|
||||
begin
|
||||
running_vms = Utils.get_all_token_vms(verbose, url, token)
|
||||
rescue Exception => e
|
||||
STDERR.puts e
|
||||
end
|
||||
elsif hostname.include? ","
|
||||
running_vms = hostname.split(",")
|
||||
if hostname.nil? and !modify_all
|
||||
STDERR.puts "ERROR: Provide a hostname or specify --all."
|
||||
exit 1
|
||||
end
|
||||
running_vms = modify_all ? service.list_active(verbose) : hostname.split(",")
|
||||
|
||||
if lifetime || tags
|
||||
# all vms
|
||||
if !running_vms.nil?
|
||||
tags = options.tags ? JSON.parse(options.tags) : nil
|
||||
modify_hash = {
|
||||
lifetime: options.lifetime,
|
||||
disk: options.disk,
|
||||
tags: tags,
|
||||
reason: options.reason
|
||||
}
|
||||
modify_hash.delete_if { |_, value| value.nil? }
|
||||
|
||||
unless modify_hash.empty?
|
||||
ok = true
|
||||
modified_hash = {}
|
||||
running_vms.each do |vm|
|
||||
begin
|
||||
modify_hash = {}
|
||||
modify_flag = true
|
||||
|
||||
running_vms.each do |vm|
|
||||
modify_hash[vm] = Pooler.modify(verbose, url, vm, token, lifetime, tags)
|
||||
end
|
||||
|
||||
modify_hash.each do |hostname,status|
|
||||
if status == false
|
||||
STDERR.puts "Could not modify #{hostname}."
|
||||
modify_flag = false
|
||||
end
|
||||
end
|
||||
|
||||
if modify_flag
|
||||
puts "Successfully modified all vms. Use `floaty list --active` to see the results."
|
||||
end
|
||||
rescue Exception => e
|
||||
modified_hash[vm] = service.modify(verbose, vm, modify_hash)
|
||||
rescue ModifyError => e
|
||||
STDERR.puts e
|
||||
exit 1
|
||||
end
|
||||
else
|
||||
# Single Vm
|
||||
begin
|
||||
modify_req = Pooler.modify(verbose, url, hostname, token, lifetime, tags)
|
||||
rescue TokenError => e
|
||||
STDERR.puts e
|
||||
exit 1
|
||||
end
|
||||
|
||||
if modify_req["ok"]
|
||||
puts "Successfully modified vm #{hostname}."
|
||||
else
|
||||
STDERR.puts "Could not modify given host #{hostname} at #{url}."
|
||||
puts modify_req
|
||||
exit 1
|
||||
ok = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if disk
|
||||
# all vms
|
||||
if !running_vms.nil?
|
||||
begin
|
||||
modify_hash = {}
|
||||
modify_flag = true
|
||||
|
||||
running_vms.each do |vm|
|
||||
modify_hash[vm] = Pooler.disk(verbose, url, vm, token, disk)
|
||||
end
|
||||
|
||||
modify_hash.each do |hostname,status|
|
||||
if status == false
|
||||
STDERR.puts "Could not update disk space on #{hostname}."
|
||||
modify_flag = false
|
||||
end
|
||||
end
|
||||
|
||||
if modify_flag
|
||||
puts "Successfully made request to update disk space on all vms."
|
||||
end
|
||||
rescue Exception => e
|
||||
STDERR.puts e
|
||||
exit 1
|
||||
end
|
||||
else
|
||||
# single vm
|
||||
begin
|
||||
disk_req = Pooler.disk(verbose, url, hostname, token, disk)
|
||||
rescue TokenError => e
|
||||
STDERR.puts e
|
||||
exit 1
|
||||
end
|
||||
|
||||
if disk_req["ok"]
|
||||
puts "Successfully made request to update disk space of vm #{hostname}."
|
||||
if ok
|
||||
if modify_all
|
||||
puts "Successfully modified all VMs."
|
||||
else
|
||||
STDERR.puts "Could not modify given host #{hostname} at #{url}."
|
||||
puts disk_req
|
||||
exit 1
|
||||
puts "Successfully modified VM #{hostname}."
|
||||
end
|
||||
puts "Use `floaty list --active` to see the results."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -307,67 +188,69 @@ class Vmfloaty
|
|||
c.option '--url STRING', String, 'URL of pooler service'
|
||||
c.action do |args, options|
|
||||
verbose = options.verbose || config['verbose']
|
||||
service_config = Utils.get_service_from_config(config, options.service)
|
||||
service = Service.new(options, config)
|
||||
hostnames = args[0]
|
||||
token = options.token || service_config['token'] || config['token']
|
||||
url = options.url ||= service_config['url'] ||= config['url']
|
||||
delete_all = options.all
|
||||
force = options.f
|
||||
|
||||
failures = []
|
||||
successes = []
|
||||
|
||||
if delete_all
|
||||
# get vms with token
|
||||
begin
|
||||
running_vms = Utils.get_all_token_vms(verbose, url, token)
|
||||
rescue TokenError => e
|
||||
STDERR.puts e
|
||||
exit 1
|
||||
rescue Exception => e
|
||||
STDERR.puts e
|
||||
exit 1
|
||||
end
|
||||
|
||||
if ! running_vms.nil?
|
||||
Utils.prettyprint_hosts(running_vms, verbose, url)
|
||||
# query y/n
|
||||
running_vms = service.list_active(verbose)
|
||||
if running_vms.empty?
|
||||
STDERR.puts "You have no running VMs."
|
||||
else
|
||||
Utils.pretty_print_hosts(verbose, service, running_vms)
|
||||
# Confirm deletion
|
||||
puts
|
||||
|
||||
if force
|
||||
ans = true
|
||||
else
|
||||
ans = agree("Delete all VMs associated with token #{token}? [y/N]")
|
||||
confirmed = true
|
||||
unless force
|
||||
confirmed = agree('Delete all these VMs? [y/N]')
|
||||
end
|
||||
|
||||
if ans
|
||||
# delete vms
|
||||
puts "Scheduling all vms for for deletion"
|
||||
response = Pooler.delete(verbose, url, running_vms, token)
|
||||
response.each do |host,vals|
|
||||
if vals['ok'] == false
|
||||
STDERR.puts "There was a problem with your request for vm #{host}."
|
||||
STDERR.puts vals
|
||||
if confirmed
|
||||
response = service.delete(verbose, running_vms)
|
||||
response.each do |hostname, result|
|
||||
if result['ok']
|
||||
successes << hostname
|
||||
else
|
||||
failures << hostname
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
exit 0
|
||||
end
|
||||
|
||||
if hostnames.nil?
|
||||
elsif hostnames || args
|
||||
hostnames = hostnames.split(',')
|
||||
results = service.delete(verbose, hostnames)
|
||||
results.each do |hostname, result|
|
||||
if result['ok']
|
||||
successes << hostname
|
||||
else
|
||||
failures << hostname
|
||||
end
|
||||
end
|
||||
else
|
||||
STDERR.puts "You did not provide any hosts to delete"
|
||||
exit 1
|
||||
else
|
||||
hosts = hostnames.split(',')
|
||||
begin
|
||||
Pooler.delete(verbose, url, hosts, token)
|
||||
rescue TokenError => e
|
||||
STDERR.puts e
|
||||
exit 1
|
||||
end
|
||||
|
||||
puts "Scheduled pooler service to delete vms #{hosts}."
|
||||
exit 0
|
||||
end
|
||||
|
||||
unless failures.empty?
|
||||
STDERR.puts 'Unable to delete the following VMs:'
|
||||
failures.each do |hostname|
|
||||
STDERR.puts "- #{hostname}"
|
||||
end
|
||||
STDERR.puts 'Check `floaty list --active`; Do you need to specify a different service?'
|
||||
end
|
||||
|
||||
unless successes.empty?
|
||||
puts unless failures.empty?
|
||||
puts 'Scheduled the following VMs for deletion:'
|
||||
successes.each do |hostname|
|
||||
puts "- #{hostname}"
|
||||
end
|
||||
end
|
||||
|
||||
exit 1 unless failures.empty?
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -382,14 +265,12 @@ class Vmfloaty
|
|||
c.option '--token STRING', String, 'Token for pooler service'
|
||||
c.action do |args, options|
|
||||
verbose = options.verbose || config['verbose']
|
||||
service_config = Utils.get_service_from_config(config, options.service)
|
||||
url = options.url ||= service_config['url'] ||= config['url']
|
||||
service = Service.new(options, config)
|
||||
hostname = args[0]
|
||||
token = options.token ||= service_config['token'] ||= config['token']
|
||||
|
||||
begin
|
||||
snapshot_req = Pooler.snapshot(verbose, url, hostname, token)
|
||||
rescue TokenError => e
|
||||
snapshot_req = service.snapshot(verbose, hostname)
|
||||
rescue TokenError, ModifyError => e
|
||||
STDERR.puts e
|
||||
exit 1
|
||||
end
|
||||
|
|
@ -411,10 +292,8 @@ class Vmfloaty
|
|||
c.option '--snapshot STRING', String, 'SHA of snapshot'
|
||||
c.action do |args, options|
|
||||
verbose = options.verbose || config['verbose']
|
||||
service_config = Utils.get_service_from_config(config, options.service)
|
||||
url = options.url ||= service_config['url'] ||= config['url']
|
||||
service = Service.new(options, config)
|
||||
hostname = args[0]
|
||||
token = options.token || service_config['token'] || config['token']
|
||||
snapshot_sha = args[1] || options.snapshot
|
||||
|
||||
if args[1] && options.snapshot
|
||||
|
|
@ -422,8 +301,8 @@ class Vmfloaty
|
|||
end
|
||||
|
||||
begin
|
||||
revert_req = Pooler.revert(verbose, url, hostname, token, snapshot_sha)
|
||||
rescue TokenError => e
|
||||
revert_req = service.revert(verbose, hostname, snapshot_sha)
|
||||
rescue TokenError, ModifyError => e
|
||||
STDERR.puts e
|
||||
exit 1
|
||||
end
|
||||
|
|
@ -441,22 +320,14 @@ class Vmfloaty
|
|||
c.option '--service STRING', String, 'Configured pooler service name'
|
||||
c.option '--url STRING', String, 'URL of pooler service'
|
||||
c.option '--json', 'Prints status in JSON format'
|
||||
c.action do |args, options|
|
||||
c.action do |_, options|
|
||||
verbose = options.verbose || config['verbose']
|
||||
service_config = Utils.get_service_from_config(config, options.service)
|
||||
url = options.url ||= service_config['url'] ||= config['url']
|
||||
|
||||
status = Pooler.status(verbose, url)
|
||||
message = status['status']['message']
|
||||
pools = status['pools']
|
||||
|
||||
service = Service.new(options, config)
|
||||
if options.json
|
||||
pp status
|
||||
pp service.status(verbose)
|
||||
else
|
||||
Utils.prettyprint_status(status, message, pools, verbose)
|
||||
Utils.pretty_print_status(verbose, service)
|
||||
end
|
||||
|
||||
exit status['status']['ok']
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -468,12 +339,11 @@ class Vmfloaty
|
|||
c.option '--verbose', 'Enables verbose output'
|
||||
c.option '--service STRING', String, 'Configured pooler service name'
|
||||
c.option '--url STRING', String, 'URL of pooler service'
|
||||
c.action do |args, options|
|
||||
c.action do |_, options|
|
||||
verbose = options.verbose || config['verbose']
|
||||
service_config = Utils.get_service_from_config(config, options.service)
|
||||
url = options.url ||= service_config['url'] ||= config['url']
|
||||
service = Service.new(options, config)
|
||||
|
||||
summary = Pooler.summary(verbose, url)
|
||||
summary = service.summary(verbose)
|
||||
pp summary
|
||||
exit 0
|
||||
end
|
||||
|
|
@ -491,47 +361,36 @@ class Vmfloaty
|
|||
c.option '--token STRING', String, 'Token for pooler service'
|
||||
c.action do |args, options|
|
||||
verbose = options.verbose || config['verbose']
|
||||
service_config = Utils.get_service_from_config(config, options.service)
|
||||
service = Service.new(options, config)
|
||||
action = args.first
|
||||
url = options.url ||= service_config['url'] ||= config['url']
|
||||
token = args[1] ||= options.token ||= service_config['token'] ||= config['token']
|
||||
user = options.user ||= service_config['user'] ||= config['user']
|
||||
|
||||
case action
|
||||
when "get"
|
||||
pass = password "Enter your pooler service password please:", '*'
|
||||
begin
|
||||
token = Auth.get_token(verbose, url, user, pass)
|
||||
rescue TokenError => e
|
||||
STDERR.puts e
|
||||
exit 1
|
||||
begin
|
||||
case action
|
||||
when 'get'
|
||||
token = service.get_new_token(verbose)
|
||||
puts token
|
||||
when 'delete'
|
||||
result = service.delete_token(verbose, options.token)
|
||||
puts result
|
||||
when 'status'
|
||||
token_value = options.token
|
||||
if token_value.nil?
|
||||
token_value = args[1]
|
||||
end
|
||||
status = service.token_status(verbose, token_value)
|
||||
puts status
|
||||
when nil
|
||||
STDERR.puts 'No action provided'
|
||||
exit 1
|
||||
else
|
||||
STDERR.puts "Unknown action: #{action}"
|
||||
exit 1
|
||||
end
|
||||
puts token
|
||||
exit 0
|
||||
when "delete"
|
||||
pass = password "Enter your pooler service password please:", '*'
|
||||
begin
|
||||
result = Auth.delete_token(verbose, url, user, pass, token)
|
||||
rescue TokenError => e
|
||||
STDERR.puts e
|
||||
exit 1
|
||||
end
|
||||
puts result
|
||||
exit 0
|
||||
when "status"
|
||||
begin
|
||||
status = Auth.token_status(verbose, url, token)
|
||||
rescue TokenError => e
|
||||
STDERR.puts e
|
||||
exit 1
|
||||
end
|
||||
puts status
|
||||
exit 0
|
||||
when nil
|
||||
STDERR.puts "No action provided"
|
||||
else
|
||||
STDERR.puts "Unknown action: #{action}"
|
||||
rescue TokenError => e
|
||||
STDERR.puts e
|
||||
exit 1
|
||||
end
|
||||
exit 0
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -548,11 +407,8 @@ class Vmfloaty
|
|||
c.option '--notoken', 'Makes a request without a token'
|
||||
c.action do |args, options|
|
||||
verbose = options.verbose || config['verbose']
|
||||
service_config = Utils.get_service_from_config(config, options.service)
|
||||
url = options.url ||= service_config['url'] ||= config['url']
|
||||
token = options.token ||= service_config['token'] ||= config['token']
|
||||
user = options.user ||= service_config['user'] ||= config['user']
|
||||
no_token = options.notoken
|
||||
service = Service.new(options, config)
|
||||
use_token = !options.notoken
|
||||
|
||||
if args.empty?
|
||||
STDERR.puts "No operating systems provided to obtain. See `floaty ssh --help` for more information on how to get VMs."
|
||||
|
|
@ -561,25 +417,11 @@ class Vmfloaty
|
|||
|
||||
host_os = args.first
|
||||
|
||||
if !no_token && !token
|
||||
puts "No token found. Retrieving a token..."
|
||||
if !user
|
||||
STDERR.puts "You did not provide a user to authenticate to the pooler service with"
|
||||
exit 1
|
||||
end
|
||||
pass = password "Enter your pooler service password please:", '*'
|
||||
begin
|
||||
token = Auth.get_token(verbose, url, user, pass)
|
||||
rescue TokenError => e
|
||||
STDERR.puts e
|
||||
STDERR.puts 'Could not get token...requesting vm without a token anyway...'
|
||||
else
|
||||
puts "\nToken retrieved!"
|
||||
puts token
|
||||
end
|
||||
if args.length > 1
|
||||
STDERR.puts "Can't ssh to multiple hosts; Using #{host_os} only..."
|
||||
end
|
||||
|
||||
Ssh.ssh(verbose, host_os, token, url)
|
||||
service.ssh(verbose, host_os, use_token)
|
||||
exit 0
|
||||
end
|
||||
end
|
||||
|
|
@ -596,7 +438,7 @@ class Vmfloaty
|
|||
EOF
|
||||
c.example 'Gets path to bash tab completion script', 'floaty completion --shell bash'
|
||||
c.option '--shell STRING', String, 'Shell to request completion script for'
|
||||
c.action do |args, options|
|
||||
c.action do |_, options|
|
||||
shell = (options.shell || 'bash').downcase.strip
|
||||
completion_file = File.expand_path(File.join('..', '..', 'extras', 'completions', "floaty.#{shell}"), __FILE__)
|
||||
|
||||
|
|
|
|||
|
|
@ -15,3 +15,9 @@ class MissingParamError < StandardError
|
|||
super
|
||||
end
|
||||
end
|
||||
|
||||
class ModifyError < StandardError
|
||||
def initialize(msg="Could not modify VM")
|
||||
super
|
||||
end
|
||||
end
|
||||
|
|
|
|||
135
lib/vmfloaty/nonstandard_pooler.rb
Normal file
135
lib/vmfloaty/nonstandard_pooler.rb
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
require 'vmfloaty/errors'
|
||||
require 'vmfloaty/http'
|
||||
require 'faraday'
|
||||
require 'json'
|
||||
|
||||
class NonstandardPooler
|
||||
def self.list(verbose, url, os_filter = nil)
|
||||
conn = Http.get_conn(verbose, url)
|
||||
|
||||
response = conn.get 'status'
|
||||
response_body = JSON.parse(response.body)
|
||||
os_list = response_body.keys.sort
|
||||
os_list.delete 'ok'
|
||||
|
||||
os_filter ? os_list.select { |i| i[/#{os_filter}/] } : os_list
|
||||
end
|
||||
|
||||
def self.list_active(verbose, url, token)
|
||||
status = Auth.token_status(verbose, url, token)
|
||||
status['reserved_hosts'] || []
|
||||
end
|
||||
|
||||
def self.retrieve(verbose, os_type, token, url)
|
||||
conn = Http.get_conn(verbose, url)
|
||||
conn.headers['X-AUTH-TOKEN'] = token if token
|
||||
|
||||
os_string = ''
|
||||
os_type.each do |os, num|
|
||||
num.times do |_i|
|
||||
os_string << os + '+'
|
||||
end
|
||||
end
|
||||
|
||||
os_string = os_string.chomp('+')
|
||||
|
||||
if os_string.empty?
|
||||
raise MissingParamError, 'No operating systems provided to obtain.'
|
||||
end
|
||||
|
||||
response = conn.post "host/#{os_string}"
|
||||
|
||||
res_body = JSON.parse(response.body)
|
||||
|
||||
if res_body['ok']
|
||||
res_body
|
||||
elsif response.status == 401
|
||||
raise AuthError, "HTTP #{response.status}: The token provided could not authenticate to the pooler.\n#{res_body}"
|
||||
else
|
||||
raise "HTTP #{response.status}: Failed to obtain VMs from the pooler at #{url}/host/#{os_string}. #{res_body}"
|
||||
end
|
||||
end
|
||||
|
||||
def self.modify(verbose, url, hostname, token, modify_hash)
|
||||
if token.nil?
|
||||
raise TokenError, 'Token provided was nil; Request cannot be made to modify VM'
|
||||
end
|
||||
|
||||
modify_hash.each do |key, value|
|
||||
unless [:reason, :reserved_for_reason].include? key
|
||||
raise ModifyError, "Configured service type does not support modification of #{key}"
|
||||
end
|
||||
end
|
||||
|
||||
if modify_hash[:reason]
|
||||
# "reason" is easier to type than "reserved_for_reason", but nspooler needs the latter
|
||||
modify_hash[:reserved_for_reason] = modify_hash.delete :reason
|
||||
end
|
||||
|
||||
conn = Http.get_conn(verbose, url)
|
||||
conn.headers['X-AUTH-TOKEN'] = token
|
||||
|
||||
response = conn.put do |req|
|
||||
req.url "host/#{hostname}"
|
||||
req.body = modify_hash.to_json
|
||||
end
|
||||
|
||||
response.body.empty? ? {} : JSON.parse(response.body)
|
||||
end
|
||||
|
||||
def self.disk(verbose, url, hostname, token, disk)
|
||||
raise ModifyError, 'Configured service type does not support modification of disk space'
|
||||
end
|
||||
|
||||
def self.snapshot(verbose, url, hostname, token)
|
||||
raise ModifyError, 'Configured service type does not support snapshots'
|
||||
end
|
||||
|
||||
def self.revert(verbose, url, hostname, token, snapshot_sha)
|
||||
raise ModifyError, 'Configured service type does not support snapshots'
|
||||
end
|
||||
|
||||
def self.delete(verbose, url, hosts, token)
|
||||
if token.nil?
|
||||
raise TokenError, 'Token provided was nil; Request cannot be made to delete VM'
|
||||
end
|
||||
|
||||
conn = Http.get_conn(verbose, url)
|
||||
|
||||
conn.headers['X-AUTH-TOKEN'] = token if token
|
||||
|
||||
response_body = {}
|
||||
|
||||
unless hosts.is_a? Array
|
||||
hosts = hosts.split(',')
|
||||
end
|
||||
hosts.each do |host|
|
||||
response = conn.delete "host/#{host}"
|
||||
res_body = JSON.parse(response.body)
|
||||
response_body[host] = res_body
|
||||
end
|
||||
|
||||
response_body
|
||||
end
|
||||
|
||||
def self.status(verbose, url)
|
||||
conn = Http.get_conn(verbose, url)
|
||||
|
||||
response = conn.get '/status'
|
||||
JSON.parse(response.body)
|
||||
end
|
||||
|
||||
def self.summary(verbose, url)
|
||||
conn = Http.get_conn(verbose, url)
|
||||
|
||||
response = conn.get '/summary'
|
||||
JSON.parse(response.body)
|
||||
end
|
||||
|
||||
def self.query(verbose, url, hostname)
|
||||
conn = Http.get_conn(verbose, url)
|
||||
|
||||
response = conn.get "host/#{hostname}"
|
||||
JSON.parse(response.body)
|
||||
end
|
||||
end
|
||||
|
|
@ -19,6 +19,15 @@ class Pooler
|
|||
hosts
|
||||
end
|
||||
|
||||
def self.list_active(verbose, url, token)
|
||||
status = Auth.token_status(verbose, url, token)
|
||||
vms = []
|
||||
if status[token] && status[token]['vms']
|
||||
vms = status[token]['vms']['running']
|
||||
end
|
||||
vms
|
||||
end
|
||||
|
||||
def self.retrieve(verbose, os_type, token, url)
|
||||
# NOTE:
|
||||
# Developers can use `Utils.generate_os_hash` to
|
||||
|
|
@ -54,25 +63,28 @@ class Pooler
|
|||
end
|
||||
end
|
||||
|
||||
def self.modify(verbose, url, hostname, token, lifetime, tags)
|
||||
def self.modify(verbose, url, hostname, token, modify_hash)
|
||||
if token.nil?
|
||||
raise TokenError, "Token provided was nil. Request cannot be made to modify vm"
|
||||
end
|
||||
|
||||
modify_body = {}
|
||||
if lifetime
|
||||
modify_body['lifetime'] = lifetime
|
||||
end
|
||||
if tags
|
||||
modify_body['tags'] = tags
|
||||
modify_hash.keys.each do |key|
|
||||
unless [:tags, :lifetime, :disk].include? key
|
||||
raise ModifyError, "Configured service type does not support modification of #{key}."
|
||||
end
|
||||
end
|
||||
|
||||
conn = Http.get_conn(verbose, url)
|
||||
conn.headers['X-AUTH-TOKEN'] = token
|
||||
|
||||
if modify_hash['disk']
|
||||
disk(verbose, url, hostname, token, modify_hash['disk'])
|
||||
modify_hash.delete 'disk'
|
||||
end
|
||||
|
||||
response = conn.put do |req|
|
||||
req.url "vm/#{hostname}"
|
||||
req.body = modify_body.to_json
|
||||
req.body = modify_hash.to_json
|
||||
end
|
||||
|
||||
res_body = JSON.parse(response.body)
|
||||
|
|
|
|||
133
lib/vmfloaty/service.rb
Normal file
133
lib/vmfloaty/service.rb
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
require 'commander/user_interaction'
|
||||
require 'commander/command'
|
||||
require 'vmfloaty/utils'
|
||||
require 'vmfloaty/ssh'
|
||||
|
||||
class Service
|
||||
|
||||
attr_reader :config
|
||||
|
||||
def initialize(options, config_hash = {})
|
||||
options ||= Commander::Command::Options.new
|
||||
@config = Utils.get_service_config config_hash, options
|
||||
@service_object = Utils.get_service_object @config['type']
|
||||
end
|
||||
|
||||
def method_missing(m, *args, &block)
|
||||
if @service_object.respond_to? m
|
||||
@service_object.send(m, *args, &block)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def url
|
||||
@config['url']
|
||||
end
|
||||
|
||||
def type
|
||||
@service_object.name
|
||||
end
|
||||
|
||||
def user
|
||||
unless @config['user']
|
||||
puts "Enter your pooler service username:"
|
||||
@config['user'] = STDIN.gets.chomp
|
||||
end
|
||||
@config['user']
|
||||
end
|
||||
|
||||
def token
|
||||
unless @config['token']
|
||||
puts "No token found. Retrieving a token..."
|
||||
@config['token'] = get_new_token(nil)
|
||||
end
|
||||
@config['token']
|
||||
end
|
||||
|
||||
def get_new_token(verbose)
|
||||
username = user
|
||||
pass = Commander::UI::password "Enter your pooler service password:", '*'
|
||||
Auth.get_token(verbose, url, username, pass)
|
||||
end
|
||||
|
||||
def delete_token(verbose, token_value = @config['token'])
|
||||
username = user
|
||||
pass = Commander::UI::password "Enter your pooler service password:", '*'
|
||||
Auth.delete_token(verbose, url, username, pass, token_value)
|
||||
end
|
||||
|
||||
def token_status(verbose, token_value)
|
||||
token_value ||= @config['token']
|
||||
Auth.token_status(verbose, url, token_value)
|
||||
end
|
||||
|
||||
def list(verbose, os_filter = nil)
|
||||
@service_object.list verbose, url, os_filter
|
||||
end
|
||||
|
||||
def list_active(verbose)
|
||||
@service_object.list_active verbose, url, token
|
||||
end
|
||||
|
||||
def retrieve(verbose, os_types, use_token = true)
|
||||
puts 'Requesting a vm without a token...' unless use_token
|
||||
token_value = use_token ? token : nil
|
||||
@service_object.retrieve verbose, os_types, token_value, url
|
||||
end
|
||||
|
||||
def ssh(verbose, host_os, use_token = true)
|
||||
token_value = nil
|
||||
if use_token
|
||||
begin
|
||||
token_value = token || get_new_token(verbose)
|
||||
rescue TokenError => e
|
||||
STDERR.puts e
|
||||
STDERR.puts 'Could not get token... requesting vm without a token anyway...'
|
||||
end
|
||||
end
|
||||
Ssh.ssh(verbose, host_os, token_value, url)
|
||||
end
|
||||
|
||||
def pretty_print_running(verbose, hostnames = [])
|
||||
if hostnames.empty?
|
||||
puts "You have no running VMs."
|
||||
else
|
||||
puts "Running VMs:"
|
||||
@service_object.pretty_print_hosts(verbose, hostnames, url)
|
||||
end
|
||||
end
|
||||
|
||||
def query(verbose, hostname)
|
||||
@service_object.query verbose, url, hostname
|
||||
end
|
||||
|
||||
def modify(verbose, hostname, modify_hash)
|
||||
@service_object.modify verbose, url, hostname, token, modify_hash
|
||||
end
|
||||
|
||||
def delete(verbose, hosts)
|
||||
@service_object.delete verbose, url, hosts, token
|
||||
end
|
||||
|
||||
def status(verbose)
|
||||
@service_object.status verbose, url
|
||||
end
|
||||
|
||||
def summary(verbose)
|
||||
@service_object.summary verbose, url
|
||||
end
|
||||
|
||||
def snapshot(verbose, hostname)
|
||||
@service_object.snapshot verbose, url, hostname, token
|
||||
end
|
||||
|
||||
def revert(verbose, hostname, snapshot_sha)
|
||||
@service_object.revert verbose, url, hostname, token, snapshot_sha
|
||||
end
|
||||
|
||||
def disk(verbose, hostname, disk)
|
||||
@service_object.disk(verbose, url, hostname, token, disk)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,27 +1,62 @@
|
|||
|
||||
require 'vmfloaty/pooler'
|
||||
require 'vmfloaty/nonstandard_pooler'
|
||||
|
||||
class Utils
|
||||
# TODO: Takes the json response body from an HTTP GET
|
||||
# request and "pretty prints" it
|
||||
def self.format_hosts(hostname_hash)
|
||||
host_hash = {}
|
||||
def self.format_hosts(response_body)
|
||||
# vmpooler response body example when `floaty get` arguments are `ubuntu-1610-x86_64=2 centos-7-x86_64`:
|
||||
# {
|
||||
# "ok": true,
|
||||
# "domain": "delivery.mycompany.net",
|
||||
# "ubuntu-1610-x86_64": {
|
||||
# "hostname": ["gdoy8q3nckuob0i", "ctnktsd0u11p9tm"]
|
||||
# },
|
||||
# "centos-7-x86_64": {
|
||||
# "hostname": "dlgietfmgeegry2"
|
||||
# }
|
||||
# }
|
||||
|
||||
hostname_hash.delete("ok")
|
||||
domain = hostname_hash["domain"]
|
||||
hostname_hash.each do |type, hosts|
|
||||
if type != "domain"
|
||||
if hosts["hostname"].kind_of?(Array)
|
||||
hosts["hostname"].map!{|host| host + "." + domain }
|
||||
# nonstandard pooler response body example when `floaty get` arguments are `solaris-11-sparc=2 ubuntu-16.04-power8`:
|
||||
# {
|
||||
# "ok": true,
|
||||
# "solaris-10-sparc": {
|
||||
# "hostname": ["sol10-10.delivery.mycompany.net", "sol10-11.delivery.mycompany.net"]
|
||||
# },
|
||||
# "ubuntu-16.04-power8": {
|
||||
# "hostname": "power8-ubuntu1604-6.delivery.mycompany.net"
|
||||
# }
|
||||
# }
|
||||
|
||||
unless response_body.delete('ok')
|
||||
raise ArgumentError, "Bad GET response passed to format_hosts: #{response_body.to_json}"
|
||||
end
|
||||
|
||||
hostnames = []
|
||||
|
||||
# vmpooler reports the domain separately from the hostname
|
||||
domain = response_body.delete('domain')
|
||||
|
||||
if domain
|
||||
# vmpooler output
|
||||
response_body.each do |os, hosts|
|
||||
if hosts['hostname'].kind_of?(Array)
|
||||
hosts['hostname'].map!{ |host| hostnames << host + "." + domain + " (#{os})"}
|
||||
else
|
||||
hosts["hostname"] = hosts["hostname"] + "." + domain
|
||||
hostnames << hosts["hostname"] + ".#{domain} (#{os})"
|
||||
end
|
||||
end
|
||||
else
|
||||
response_body.each do |os, hosts|
|
||||
if hosts['hostname'].kind_of?(Array)
|
||||
hosts['hostname'].map!{ |host| hostnames << host + " (#{os})" }
|
||||
else
|
||||
hostnames << hosts['hostname'] + " (#{os})"
|
||||
end
|
||||
|
||||
host_hash[type] = hosts["hostname"]
|
||||
end
|
||||
end
|
||||
|
||||
host_hash.to_json
|
||||
hostnames.map { |hostname| puts "- #{hostname}" }
|
||||
end
|
||||
|
||||
def self.generate_os_hash(os_args)
|
||||
|
|
@ -46,72 +81,84 @@ class Utils
|
|||
os_types
|
||||
end
|
||||
|
||||
def self.get_vm_info(hosts, verbose, url)
|
||||
vms = {}
|
||||
hosts.each do |host|
|
||||
vm_info = Pooler.query(verbose, url, host)
|
||||
if vm_info['ok']
|
||||
vms[host] = {}
|
||||
vms[host]['domain'] = vm_info[host]['domain']
|
||||
vms[host]['template'] = vm_info[host]['template']
|
||||
vms[host]['lifetime'] = vm_info[host]['lifetime']
|
||||
vms[host]['running'] = vm_info[host]['running']
|
||||
vms[host]['tags'] = vm_info[host]['tags']
|
||||
end
|
||||
end
|
||||
vms
|
||||
end
|
||||
|
||||
def self.prettyprint_hosts(hosts, verbose, url)
|
||||
puts "Running VMs:"
|
||||
vm_info = get_vm_info(hosts, verbose, url)
|
||||
vm_info.each do |vm,info|
|
||||
domain = info['domain']
|
||||
template = info['template']
|
||||
lifetime = info['lifetime']
|
||||
running = info['running']
|
||||
tags = info['tags'] || {}
|
||||
|
||||
tag_pairs = tags.map {|key,value| "#{key}: #{value}" }
|
||||
duration = "#{running}/#{lifetime} hours"
|
||||
metadata = [template, duration, *tag_pairs]
|
||||
|
||||
puts "- #{vm}.#{domain} (#{metadata.join(", ")})"
|
||||
end
|
||||
end
|
||||
|
||||
def self.get_all_token_vms(verbose, url, token)
|
||||
# get vms with token
|
||||
status = Auth.token_status(verbose, url, token)
|
||||
|
||||
vms = status[token]['vms']
|
||||
if vms.nil?
|
||||
raise "You have no running vms"
|
||||
end
|
||||
|
||||
running_vms = vms['running']
|
||||
running_vms
|
||||
end
|
||||
|
||||
def self.prettyprint_status(status, message, pools, verbose)
|
||||
pools.select! {|name,pool| pool['ready'] < pool['max']} if ! verbose
|
||||
|
||||
width = pools.keys.map(&:length).max
|
||||
pools.each do |name,pool|
|
||||
def self.pretty_print_hosts(verbose, service, hostnames = [])
|
||||
hostnames = [hostnames] unless hostnames.is_a? Array
|
||||
hostnames.each do |hostname|
|
||||
begin
|
||||
max = pool['max']
|
||||
ready = pool['ready']
|
||||
pending = pool['pending']
|
||||
missing = max - ready - pending
|
||||
char = 'o'
|
||||
puts "#{name.ljust(width)} #{(char*ready).green}#{(char*pending).yellow}#{(char*missing).red}"
|
||||
response = service.query(verbose, hostname)
|
||||
host_data = response[hostname]
|
||||
|
||||
case service.type
|
||||
when 'Pooler'
|
||||
tag_pairs = []
|
||||
unless host_data['tags'].nil?
|
||||
tag_pairs = host_data['tags'].map {|key, value| "#{key}: #{value}"}
|
||||
end
|
||||
duration = "#{host_data['running']}/#{host_data['lifetime']} hours"
|
||||
metadata = [host_data['template'], duration, *tag_pairs]
|
||||
puts "- #{hostname}.#{host_data['domain']} (#{metadata.join(", ")})"
|
||||
when 'NonstandardPooler'
|
||||
line = "- #{host_data['fqdn']} (#{host_data['os_triple']}"
|
||||
line += ", #{host_data['hours_left_on_reservation']}h remaining"
|
||||
unless host_data['reserved_for_reason'].empty?
|
||||
line += ", reason: #{host_data['reserved_for_reason']}"
|
||||
end
|
||||
line += ')'
|
||||
puts line
|
||||
else
|
||||
raise "Invalid service type #{service.type}"
|
||||
end
|
||||
rescue => e
|
||||
puts "#{name.ljust(width)} #{e.red}"
|
||||
STDERR.puts("Something went wrong while trying to gather information on #{hostname}:")
|
||||
STDERR.puts(e)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
puts
|
||||
puts message.colorize(status['status']['ok'] ? :default : :red)
|
||||
def self.pretty_print_status(verbose, service)
|
||||
status_response = service.status(verbose)
|
||||
|
||||
case service.type
|
||||
when 'Pooler'
|
||||
message = status_response['status']['message']
|
||||
pools = status_response['pools']
|
||||
pools.select! {|_, pool| pool['ready'] < pool['max']} unless verbose
|
||||
|
||||
width = pools.keys.map(&:length).max
|
||||
pools.each do |name, pool|
|
||||
begin
|
||||
max = pool['max']
|
||||
ready = pool['ready']
|
||||
pending = pool['pending']
|
||||
missing = max - ready - pending
|
||||
char = 'o'
|
||||
puts "#{name.ljust(width)} #{(char*ready).green}#{(char*pending).yellow}#{(char*missing).red}"
|
||||
rescue => e
|
||||
puts "#{name.ljust(width)} #{e.red}"
|
||||
end
|
||||
end
|
||||
puts message.colorize(status_response['status']['ok'] ? :default : :red)
|
||||
when 'NonstandardPooler'
|
||||
pools = status_response
|
||||
pools.delete 'ok'
|
||||
pools.select! {|_, pool| pool['available_hosts'] < pool['total_hosts']} unless verbose
|
||||
|
||||
width = pools.keys.map(&:length).max
|
||||
pools.each do |name, pool|
|
||||
begin
|
||||
max = pool['total_hosts']
|
||||
ready = pool['available_hosts']
|
||||
pending = pool['pending'] || 0 # not available for nspooler
|
||||
missing = max - ready - pending
|
||||
char = 'o'
|
||||
puts "#{name.ljust(width)} #{(char*ready).green}#{(char*pending).yellow}#{(char*missing).red}"
|
||||
rescue => e
|
||||
puts "#{name.ljust(width)} #{e.red}"
|
||||
end
|
||||
end
|
||||
else
|
||||
raise "Invalid service type #{service.type}"
|
||||
end
|
||||
end
|
||||
|
||||
# Adapted from ActiveSupport
|
||||
|
|
@ -122,34 +169,46 @@ class Utils
|
|||
str.gsub(/^[ \t]{#{min_indent_size}}/, '')
|
||||
end
|
||||
|
||||
def self.get_service_from_config(config, service_name = '')
|
||||
# The top-level url, user, and token values are treated as defaults
|
||||
service = {
|
||||
'url' => config['url'],
|
||||
'user' => config['user'],
|
||||
'token' => config['token']
|
||||
}
|
||||
|
||||
# If no named services have been configured, use the default values
|
||||
return service unless config['services'] and config['services'].length
|
||||
|
||||
if not service_name.empty?
|
||||
if config['services'][service_name]
|
||||
# If the user specified a configured service name, use that service
|
||||
# If values are missing, use the top-level defaults
|
||||
service.merge!(config['services'][service_name]) { |key, default, value| value }
|
||||
else
|
||||
STDERR.puts "WARNING: Could not find a configured service matching the name #{service_name} at #{Dir.home}/.vmfloaty.yml"
|
||||
return {}
|
||||
end
|
||||
def self.get_service_object(type = '')
|
||||
nspooler_strings = ['ns', 'nspooler', 'nonstandard', 'nonstandard_pooler']
|
||||
if nspooler_strings.include? type.downcase
|
||||
NonstandardPooler
|
||||
else
|
||||
# Otherwise, use the first service configured under the 'services' key
|
||||
# If values are missing, use the top-level defaults
|
||||
name, config_hash = config['services'].first
|
||||
service.merge!(config_hash) { |key, default, value| value }
|
||||
Pooler
|
||||
end
|
||||
|
||||
service
|
||||
end
|
||||
|
||||
def self.get_service_config(config, options)
|
||||
# The top-level url, user, and token values in the config file are treated as defaults
|
||||
service_config = {
|
||||
'url' => config['url'],
|
||||
'user' => config['user'],
|
||||
'token' => config['token'],
|
||||
'type' => config['type'] || 'vmpooler'
|
||||
}
|
||||
|
||||
if config['services']
|
||||
if options.service.nil?
|
||||
# If the user did not specify a service name at the command line, but configured services do exist,
|
||||
# use the first configured service in the list by default.
|
||||
_, values = config['services'].first
|
||||
service_config.merge! values
|
||||
else
|
||||
# If the user provided a service name at the command line, use that service if posible, or fail
|
||||
if config['services'][options.service]
|
||||
# If the service is configured but some values are missing, use the top-level defaults to fill them in
|
||||
service_config.merge! config['services'][options.service]
|
||||
else
|
||||
raise ArgumentError, "Could not find a configured service named '#{options.service}' in ~/.vmfloaty.yml"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Prioritize an explicitly specified url, user, or token if the user provided one
|
||||
service_config['url'] = options.url unless options.url.nil?
|
||||
service_config['token'] = options.token unless options.token.nil?
|
||||
service_config['user'] = options.user unless options.user.nil?
|
||||
|
||||
service_config
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue