rightscale-cli 0.5.6 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +11 -1
- data/bin/rightscale +25 -0
- data/bin/rs +2 -3
- data/lib/ask_pass.rb +2 -2
- data/lib/deployment_cat.rb +378 -0
- data/lib/rightscale_cli.rb +0 -1
- data/lib/rightscale_cli/client.rb +26 -16
- data/lib/rightscale_cli/clouds.rb +22 -22
- data/lib/rightscale_cli/config.rb +16 -11
- data/lib/rightscale_cli/configure.rb +24 -1
- data/lib/rightscale_cli/deployments.rb +37 -13
- data/lib/rightscale_cli/instances.rb +26 -3
- data/lib/rightscale_cli/logger.rb +8 -6
- data/lib/rightscale_cli/monkey_patches/client_attributes.rb +1 -0
- data/lib/rightscale_cli/network/networks.rb +61 -0
- data/lib/rightscale_cli/network/security_groups.rb +155 -0
- data/lib/rightscale_cli/output.rb +5 -4
- data/lib/rightscale_cli/recurring_volume_attachments.rb +50 -0
- data/lib/rightscale_cli/refresh.rb +63 -0
- data/lib/rightscale_cli/render_options.rb +12 -2
- data/lib/rightscale_cli/rightscale_cli.rb +10 -0
- data/lib/rightscale_cli/server_templates.rb +1 -1
- data/lib/rightscale_cli/servers.rb +5 -0
- data/lib/rightscale_cli/ssh_keys.rb +87 -0
- data/lib/rightscale_cli/tags.rb +29 -26
- data/lib/rightscale_cli/version.rb +2 -1
- data/lib/rightscale_cli/volumes.rb +54 -19
- data/lib/templates/right_api_client.yml.erb +6 -2
- data/lib/yesno.rb +4 -8
- metadata +31 -24
data/lib/rightscale_cli.rb
CHANGED
@@ -20,55 +20,65 @@ require 'rightscale_cli/logger'
|
|
20
20
|
require 'ask_pass'
|
21
21
|
|
22
22
|
class RightScaleCLI
|
23
|
+
# Represents a RightScale CLI Client
|
23
24
|
class Client
|
24
25
|
attr_accessor :client
|
25
26
|
attr_accessor :render
|
26
|
-
|
27
|
+
|
27
28
|
def initialize(options)
|
28
29
|
config = RightScaleCLI::Config.new.directives
|
29
30
|
config[:account_id] = options['account'] if options[:account]
|
30
31
|
config[:email] = options['user'] if options[:user]
|
31
32
|
config[:api_version] = options['api'] if options[:api]
|
33
|
+
config[:timeout] = nil
|
32
34
|
|
33
|
-
if options['password'] ||
|
35
|
+
if options['password'] || \
|
36
|
+
(!config[:password] && \
|
37
|
+
!config[:password_base64] &&
|
38
|
+
!config[:access_token])
|
39
|
+
# fallback to asking for user password
|
34
40
|
config[:password] = ask_pass
|
35
|
-
|
41
|
+
# set this to nil so it is not used by precedence
|
42
|
+
config[:password_base64] = nil
|
36
43
|
end
|
37
44
|
|
38
45
|
@options = options
|
39
46
|
@client = RightApi::Client.new(config)
|
40
|
-
@logger = RightScaleCLI::Logger.new
|
47
|
+
@logger = RightScaleCLI::Logger.new
|
41
48
|
end
|
42
49
|
|
43
50
|
def get(resource)
|
44
51
|
result = []
|
45
|
-
@client.send(resource).index.each { |record|
|
46
|
-
|
47
|
-
}
|
48
|
-
return result
|
52
|
+
@client.send(resource).index.each { |record| result.push(record.raw) }
|
53
|
+
result
|
49
54
|
end
|
50
55
|
|
51
56
|
def show(resource, resource_id, *args)
|
52
57
|
if args.count > 0
|
53
58
|
result = []
|
54
|
-
records = @client.send(resource).index(
|
55
|
-
|
56
|
-
|
57
|
-
}
|
59
|
+
records = @client.send(resource).index(id: resource_id)
|
60
|
+
.show.send(args[0]).index
|
61
|
+
records.each { |record| result.push(record.raw) }
|
58
62
|
@logger.info("Resource count: #{result.count}.")
|
59
63
|
else
|
60
|
-
result = @client.send(resource).index(
|
64
|
+
result = @client.send(resource).index(id: resource_id).show.raw
|
61
65
|
end
|
62
|
-
|
66
|
+
result
|
63
67
|
end
|
64
68
|
|
65
69
|
def create(resource, params)
|
66
|
-
|
70
|
+
if params[:cloud]
|
71
|
+
resource = @client.clouds(id: params[:cloud]).show.send("#{resource}s")
|
72
|
+
.create(params)
|
73
|
+
else
|
74
|
+
resource = @client.send("#{resource}s").create(params)
|
75
|
+
end
|
67
76
|
@logger.info("Created #{resource.href}.")
|
77
|
+
resource.href
|
68
78
|
end
|
69
79
|
|
70
80
|
def destroy(resource, resource_id)
|
71
|
-
resource = @client.send("#{resource}s").index(
|
81
|
+
resource = @client.send("#{resource}s").index(id: resource_id)
|
72
82
|
resource.destroy
|
73
83
|
@logger.info("Deleted #{resource.href}.")
|
74
84
|
end
|
@@ -19,66 +19,66 @@ require 'rightscale_cli/client'
|
|
19
19
|
require 'rightscale_cli/logger'
|
20
20
|
|
21
21
|
class RightScaleCLI
|
22
|
+
# Represents Clouds
|
22
23
|
class Clouds < Thor
|
23
24
|
namespace :clouds
|
24
25
|
|
25
26
|
def initialize(*args)
|
26
27
|
super
|
27
28
|
@client = RightScaleCLI::Client.new(options)
|
28
|
-
@logger = RightScaleCLI::Logger.new
|
29
|
+
@logger = RightScaleCLI::Logger.new
|
29
30
|
end
|
30
31
|
|
31
32
|
# include render options
|
32
33
|
eval(IO.read("#{File.dirname(File.expand_path(__FILE__))}/render_options.rb"), binding)
|
33
34
|
|
34
|
-
desc
|
35
|
-
option :basic, :
|
36
|
-
def list
|
35
|
+
desc 'list', 'lists all clouds connected to the account'
|
36
|
+
option :basic, type: :boolean, required: false
|
37
|
+
def list
|
37
38
|
clouds = @client.get('clouds')
|
38
39
|
|
39
40
|
if options[:basic]
|
40
|
-
basic_fields =
|
41
|
+
basic_fields = %w('display_name', 'cloud_type', 'name')
|
41
42
|
basic_clouds = []
|
42
|
-
@client.get('clouds').each
|
43
|
-
cloud_id = cloud['links'].select { |link| link['rel'] == 'instances' }
|
44
|
-
|
43
|
+
@client.get('clouds').each do |cloud|
|
44
|
+
cloud_id = cloud['links'].select { |link| link['rel'] == 'instances' }
|
45
|
+
.first['href'].split('/')[3]
|
46
|
+
c = cloud.select { |x| basic_fields.include?(x) }
|
45
47
|
c['cloud_id'] = cloud_id.to_i
|
46
48
|
basic_clouds.push(c)
|
47
|
-
|
49
|
+
end
|
48
50
|
clouds = basic_clouds
|
49
51
|
end
|
50
52
|
|
51
53
|
@client.render(clouds, 'clouds')
|
52
54
|
end
|
53
55
|
|
54
|
-
desc
|
56
|
+
desc 'show', 'shows a cloud'
|
55
57
|
def show(cloud_id)
|
56
58
|
@client.render(@client.show('clouds', cloud_id), 'cloud')
|
57
59
|
end
|
58
60
|
|
59
|
-
desc
|
60
|
-
option :name, :
|
61
|
-
option :cloud_type, :
|
62
|
-
option :description, :
|
63
|
-
def search
|
61
|
+
desc 'search', 'searches for clouds by cloud type, name, description'
|
62
|
+
option :name, type: :string, required: false
|
63
|
+
option :cloud_type, type: :string, required: false
|
64
|
+
option :description, type: :string, required: false
|
65
|
+
def search
|
64
66
|
filter = []
|
65
67
|
filter.push("name==#{options[:name]}") if options[:name]
|
66
68
|
filter.push("cloud_type==#{options[:cloud_type]}") if options[:cloud_type]
|
67
69
|
filter.push("description==#{options[:cloud]}") if options[:description]
|
68
70
|
|
69
|
-
@logger.info
|
71
|
+
@logger.info 'Searching for clouds!'
|
70
72
|
|
71
73
|
puts "filter: #{filter}" if options[:debug]
|
72
|
-
|
73
|
-
clouds = []
|
74
|
-
@client.client.clouds.index(:filter => filter).each { |cloud|
|
75
|
-
clouds.push(cloud.raw)
|
76
|
-
}
|
77
74
|
|
75
|
+
clouds = []
|
76
|
+
@client.client.clouds.index(filter: filter)
|
77
|
+
.each { |cloud| clouds.push(cloud.raw) }
|
78
78
|
@client.render(clouds, 'clouds')
|
79
79
|
end
|
80
80
|
|
81
|
-
def self.banner(task,
|
81
|
+
def self.banner(task, _namespace = true, subcommand = false)
|
82
82
|
"#{basename} #{task.formatted_usage(self, true, subcommand)}"
|
83
83
|
end
|
84
84
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Author:: Chris Fordham (<chris@fordham-nagy.id.au>)
|
2
|
-
# Copyright:: Copyright (c) 2013 Chris Fordham
|
2
|
+
# Copyright:: Copyright (c) 2013-2105 Chris Fordham
|
3
3
|
# License:: Apache License, Version 2.0
|
4
4
|
#
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -18,27 +18,32 @@ require 'yaml'
|
|
18
18
|
require 'fileutils'
|
19
19
|
|
20
20
|
class RightScaleCLI
|
21
|
+
# Represents a RightScale CLI configuration
|
21
22
|
class Config
|
22
23
|
attr_accessor :template_path, :config_home, :config_path, :directives
|
23
24
|
|
24
|
-
def initialize(*
|
25
|
-
@template_path = File.join(File.dirname(__FILE__),
|
25
|
+
def initialize(*)
|
26
|
+
@template_path = File.join(File.dirname(__FILE__),
|
27
|
+
'..', 'templates', 'right_api_client.yml.erb')
|
26
28
|
@config_home = File.join(ENV['HOME'], '.rightscale')
|
27
29
|
@config_path = File.join(@config_home, 'right_api_client.yml')
|
28
30
|
|
29
|
-
Dir.mkdir(@config_home) unless File.
|
31
|
+
Dir.mkdir(@config_home) unless File.exist?(@config_home)
|
30
32
|
FileUtils.touch(@config_path)
|
31
|
-
|
33
|
+
|
32
34
|
# write a fresh file if it does not load/parse
|
33
35
|
unless YAML.load_file(@config_path)
|
34
36
|
@directives = {
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
account_id: '',
|
38
|
+
email: nil,
|
39
|
+
password_base64: '',
|
40
|
+
access_token: '',
|
41
|
+
api_url: 'https://us-4.rightscale.com',
|
42
|
+
api_version: '1.5'
|
40
43
|
}
|
41
|
-
File.open(@config_path, 'w')
|
44
|
+
File.open(@config_path, 'w') do |f|
|
45
|
+
f.write(ERB.new(IO.read(@template_path)).result(binding))
|
46
|
+
end
|
42
47
|
end
|
43
48
|
|
44
49
|
# load/reload the directives from the file
|
@@ -20,6 +20,7 @@ require 'rightscale_cli/config'
|
|
20
20
|
require 'rightscale_cli/logger'
|
21
21
|
require 'rightscale_cli/client'
|
22
22
|
require 'ask_pass'
|
23
|
+
require 'yesno'
|
23
24
|
require 'erb'
|
24
25
|
require "base64"
|
25
26
|
|
@@ -58,6 +59,18 @@ class RightScaleCLI
|
|
58
59
|
update_conf
|
59
60
|
end
|
60
61
|
|
62
|
+
desc "oauth", "Configure the RightScale OAuth access token for the client."
|
63
|
+
def oauth()
|
64
|
+
@directives.merge!( { :access_token => ask("RightScale API access token:") })
|
65
|
+
update_conf
|
66
|
+
end
|
67
|
+
|
68
|
+
desc 'refresh', 'configure the RightScale refresh token for the client'
|
69
|
+
def refresh()
|
70
|
+
@directives.merge!( { :refresh_token => ask('RightScale API refresh token:') })
|
71
|
+
update_conf
|
72
|
+
end
|
73
|
+
|
61
74
|
desc "api", "Configure the RightScale API version used by the client."
|
62
75
|
def api()
|
63
76
|
@directives.merge!( { :api_version => ask("RightScale API version (e.g. 1.5):") })
|
@@ -66,7 +79,8 @@ class RightScaleCLI
|
|
66
79
|
|
67
80
|
desc "shard", "Configure the RightScale shard used by the client."
|
68
81
|
def shard()
|
69
|
-
|
82
|
+
shard = ask("RightScale shard (e.g. us-4.rightscale.com):")
|
83
|
+
@directives.merge!( { :api_url => "https://#{shard}", :token_endpoint => "https://#{shard}/api/oauth2" })
|
70
84
|
update_conf
|
71
85
|
end
|
72
86
|
|
@@ -81,9 +95,18 @@ class RightScaleCLI
|
|
81
95
|
account
|
82
96
|
user
|
83
97
|
password
|
98
|
+
# do not ask for the api access token as we'll generate this from the refresh token
|
99
|
+
# oauth
|
100
|
+
refresh
|
84
101
|
shard
|
85
102
|
api
|
86
103
|
puts 'Configuration saved.'
|
104
|
+
if @directives[:refresh_token]
|
105
|
+
puts 'Refresh API token now? (y/n):'
|
106
|
+
if yesno
|
107
|
+
system 'rs refresh token'
|
108
|
+
end
|
109
|
+
end
|
87
110
|
end
|
88
111
|
|
89
112
|
#default_task :all
|
@@ -18,44 +18,61 @@ require 'thor'
|
|
18
18
|
require 'yaml'
|
19
19
|
require 'rightscale_cli/client'
|
20
20
|
require 'rightscale_cli/logger'
|
21
|
+
require 'rightscale_cli/config'
|
22
|
+
require 'deployment_cat'
|
23
|
+
require 'right_api_client'
|
21
24
|
|
22
25
|
class RightScaleCLI
|
26
|
+
# Represents RightScale Deployments
|
23
27
|
class Deployments < Thor
|
24
28
|
namespace :deployments
|
25
29
|
|
26
30
|
def initialize(*args)
|
27
31
|
super
|
28
32
|
@client = RightScaleCLI::Client.new(options)
|
29
|
-
@logger = RightScaleCLI::Logger.new
|
33
|
+
@logger = RightScaleCLI::Logger.new
|
34
|
+
@config = RightScaleCLI::Config.new.directives
|
30
35
|
end
|
31
36
|
|
32
|
-
|
33
|
-
|
37
|
+
class_option :xml,
|
38
|
+
type: :boolean,
|
39
|
+
default: false,
|
40
|
+
aliases: '-X',
|
41
|
+
required: false,
|
42
|
+
desc: 'returns xml'
|
43
|
+
class_option :json,
|
44
|
+
type: :boolean,
|
45
|
+
default: false,
|
46
|
+
aliases: '-J',
|
47
|
+
required: false,
|
48
|
+
desc: 'returns json'
|
34
49
|
|
35
|
-
desc
|
36
|
-
def list
|
50
|
+
desc 'list', 'Lists all deployments'
|
51
|
+
def list
|
37
52
|
@client.render(@client.get('deployments'), 'deployments')
|
38
53
|
end
|
39
54
|
|
40
|
-
desc
|
55
|
+
desc 'create', 'Creates a deployment'
|
41
56
|
def create(name, description)
|
42
|
-
@client.create('deployment',
|
57
|
+
@client.create('deployment', name: name, description: description)
|
43
58
|
end
|
44
59
|
|
45
|
-
desc
|
60
|
+
desc 'destroy', 'Deletes a deployment'
|
46
61
|
def destroy(deployment_id)
|
47
62
|
@client.destroy('deployment', deployment_id)
|
48
63
|
end
|
49
64
|
|
50
|
-
desc
|
65
|
+
desc 'servers', 'Lists servers in a deployment'
|
51
66
|
def servers(deployment)
|
52
67
|
@logger.info("Retrieving all servers in deployment, #{deployment}...")
|
53
|
-
@client.render(@client.show('deployments', deployment, 'servers'),
|
68
|
+
@client.render(@client.show('deployments', deployment, 'servers'),
|
69
|
+
'servers')
|
54
70
|
end
|
55
71
|
|
56
|
-
desc
|
72
|
+
desc 'terminate', 'Terminates all servers in a deployment'
|
57
73
|
def terminate(deployment_id)
|
58
|
-
@client.client.deployments.index(:
|
74
|
+
@client.client.deployments.index(id: deployment_id)
|
75
|
+
.show.servers.index.each do |server|
|
59
76
|
unless server.state == 'inactive'
|
60
77
|
@logger.info "Terminating #{server.href} (state: #{server.state}.)"
|
61
78
|
server.terminate
|
@@ -63,7 +80,14 @@ class RightScaleCLI
|
|
63
80
|
end
|
64
81
|
end
|
65
82
|
|
66
|
-
|
83
|
+
desc 'export', 'Exports a deployment into a CAT file'
|
84
|
+
def export(deployment_id)
|
85
|
+
@config[:timeout] = nil
|
86
|
+
@client = RightApi::Client.new(@config)
|
87
|
+
deployment_to_cat_file @client, deployment_id, nil, nil
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.banner(task, _namespace = true, subcommand = false)
|
67
91
|
"#{basename} #{task.formatted_usage(self, true, subcommand)}"
|
68
92
|
end
|
69
93
|
end
|
@@ -22,6 +22,17 @@ class RightScaleCLI
|
|
22
22
|
class Instances < Thor
|
23
23
|
namespace :instances
|
24
24
|
|
25
|
+
def initialize(*args)
|
26
|
+
super
|
27
|
+
@client = RightScaleCLI::Client.new(options)
|
28
|
+
@logger = RightScaleCLI::Logger.new()
|
29
|
+
end
|
30
|
+
|
31
|
+
# include render options
|
32
|
+
eval(IO.read(
|
33
|
+
"#{File.dirname(File.expand_path(__FILE__))}/render_options.rb"), binding
|
34
|
+
)
|
35
|
+
|
25
36
|
desc "list", "Lists all instances for a given cloud."
|
26
37
|
option :cloud, :desc => 'The cloud to filter on.', :type => :string, :required => true
|
27
38
|
option :datacenter, :desc => 'The href of the datacenter to filter on', :type => :string, :required => false
|
@@ -37,9 +48,7 @@ class RightScaleCLI
|
|
37
48
|
option :server_template, :desc => 'The href of the ServerTemplate to filter on.', :type => :string, :required => false
|
38
49
|
option :state, :desc => 'The state of Instances to filter on.', :type => :string, :required => false
|
39
50
|
def list()
|
40
|
-
instances = []
|
41
51
|
filter = []
|
42
|
-
|
43
52
|
filter.push("datacenter_href==#{options[:datacenter]}") if options[:datacenter]
|
44
53
|
filter.push("deployment_href==#{options[:deployment]}") if options[:deployment]
|
45
54
|
filter.push("name==#{options[:private_ip]}") if options[:name]
|
@@ -55,7 +64,21 @@ class RightScaleCLI
|
|
55
64
|
|
56
65
|
@logger.debug "filter: #{filter}" if options[:debug]
|
57
66
|
|
58
|
-
|
67
|
+
instances = []
|
68
|
+
@client.client.clouds(:id => options[:cloud]).show.instances(:filter => filter).index.each do |instance|
|
69
|
+
instance_href = instance.href
|
70
|
+
instance = instance.raw
|
71
|
+
instance['href'] = instance_href
|
72
|
+
instances.push(instance)
|
73
|
+
end
|
74
|
+
@client.render(instances, 'instances')
|
75
|
+
end
|
76
|
+
|
77
|
+
desc "show", "Shows attributes of a single instance."
|
78
|
+
option :cloud, :desc => 'The cloud to filter on.', :type => :string, :required => true
|
79
|
+
def show(instance_id)
|
80
|
+
filter = []
|
81
|
+
@client.render(@client.client.clouds(:id => options[:cloud]).show.instances.index(:id => instance_id).show.raw, 'instance')
|
59
82
|
end
|
60
83
|
|
61
84
|
desc "run-exec", "Runs a chef recipe or rightscript on instances of a given cloud."
|
@@ -18,15 +18,17 @@ require 'logger'
|
|
18
18
|
require 'rightscale_cli/config'
|
19
19
|
|
20
20
|
class RightScaleCLI
|
21
|
+
# Represents a RightScale CLI Logger
|
21
22
|
class Logger
|
22
23
|
attr_accessor :log
|
23
|
-
|
24
|
-
def initialize(*
|
24
|
+
|
25
|
+
def initialize(*)
|
25
26
|
@log_init_msg = 'Initializing Logging using '
|
26
27
|
|
27
28
|
if ENV['RIGHT_API_CLIENT_LOG']
|
28
|
-
if File.
|
29
|
-
file = File.open(ENV['RIGHT_API_CLIENT_LOG'],
|
29
|
+
if File.exist?(ENV['RIGHT_API_CLIENT_LOG'])
|
30
|
+
file = File.open(ENV['RIGHT_API_CLIENT_LOG'],
|
31
|
+
File::WRONLY | File::APPEND)
|
30
32
|
else
|
31
33
|
file = ENV['RIGHT_API_CLIENT_LOG']
|
32
34
|
end
|
@@ -38,14 +40,14 @@ class RightScaleCLI
|
|
38
40
|
end
|
39
41
|
end
|
40
42
|
|
41
|
-
def init_message
|
43
|
+
def init_message
|
42
44
|
@log.info @log_init_msg
|
43
45
|
end
|
44
46
|
|
45
47
|
def info(msg)
|
46
48
|
@log.info msg
|
47
49
|
end
|
48
|
-
|
50
|
+
|
49
51
|
def debug(msg)
|
50
52
|
@log.debug msg
|
51
53
|
end
|