morpheus-cli 2.10.0 → 2.10.1

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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/bin/morpheus +27 -32
  3. data/lib/morpheus/api/accounts_interface.rb +36 -47
  4. data/lib/morpheus/api/api_client.rb +141 -110
  5. data/lib/morpheus/api/app_templates_interface.rb +56 -72
  6. data/lib/morpheus/api/apps_interface.rb +111 -132
  7. data/lib/morpheus/api/auth_interface.rb +30 -0
  8. data/lib/morpheus/api/clouds_interface.rb +71 -76
  9. data/lib/morpheus/api/custom_instance_types_interface.rb +21 -46
  10. data/lib/morpheus/api/dashboard_interface.rb +10 -17
  11. data/lib/morpheus/api/deploy_interface.rb +60 -72
  12. data/lib/morpheus/api/deployments_interface.rb +53 -71
  13. data/lib/morpheus/api/groups_interface.rb +55 -45
  14. data/lib/morpheus/api/instance_types_interface.rb +19 -23
  15. data/lib/morpheus/api/instances_interface.rb +179 -177
  16. data/lib/morpheus/api/key_pairs_interface.rb +11 -17
  17. data/lib/morpheus/api/license_interface.rb +18 -23
  18. data/lib/morpheus/api/load_balancers_interface.rb +54 -69
  19. data/lib/morpheus/api/logs_interface.rb +25 -29
  20. data/lib/morpheus/api/options_interface.rb +13 -17
  21. data/lib/morpheus/api/provision_types_interface.rb +19 -22
  22. data/lib/morpheus/api/roles_interface.rb +75 -94
  23. data/lib/morpheus/api/security_group_rules_interface.rb +28 -37
  24. data/lib/morpheus/api/security_groups_interface.rb +39 -51
  25. data/lib/morpheus/api/servers_interface.rb +113 -115
  26. data/lib/morpheus/api/setup_interface.rb +31 -0
  27. data/lib/morpheus/api/task_sets_interface.rb +36 -38
  28. data/lib/morpheus/api/tasks_interface.rb +56 -69
  29. data/lib/morpheus/api/users_interface.rb +67 -76
  30. data/lib/morpheus/api/virtual_images_interface.rb +61 -61
  31. data/lib/morpheus/api/whoami_interface.rb +12 -15
  32. data/lib/morpheus/cli.rb +71 -60
  33. data/lib/morpheus/cli/accounts.rb +254 -315
  34. data/lib/morpheus/cli/alias_command.rb +219 -0
  35. data/lib/morpheus/cli/app_templates.rb +264 -272
  36. data/lib/morpheus/cli/apps.rb +608 -671
  37. data/lib/morpheus/cli/cli_command.rb +259 -21
  38. data/lib/morpheus/cli/cli_registry.rb +99 -14
  39. data/lib/morpheus/cli/clouds.rb +599 -372
  40. data/lib/morpheus/cli/config_file.rb +126 -0
  41. data/lib/morpheus/cli/credentials.rb +141 -117
  42. data/lib/morpheus/cli/dashboard_command.rb +48 -56
  43. data/lib/morpheus/cli/deployments.rb +254 -268
  44. data/lib/morpheus/cli/deploys.rb +150 -142
  45. data/lib/morpheus/cli/error_handler.rb +38 -0
  46. data/lib/morpheus/cli/groups.rb +551 -179
  47. data/lib/morpheus/cli/hosts.rb +862 -617
  48. data/lib/morpheus/cli/instance_types.rb +103 -95
  49. data/lib/morpheus/cli/instances.rb +1335 -1009
  50. data/lib/morpheus/cli/key_pairs.rb +82 -90
  51. data/lib/morpheus/cli/library.rb +498 -499
  52. data/lib/morpheus/cli/license.rb +83 -101
  53. data/lib/morpheus/cli/load_balancers.rb +314 -300
  54. data/lib/morpheus/cli/login.rb +66 -44
  55. data/lib/morpheus/cli/logout.rb +47 -46
  56. data/lib/morpheus/cli/mixins/accounts_helper.rb +69 -31
  57. data/lib/morpheus/cli/mixins/infrastructure_helper.rb +106 -0
  58. data/lib/morpheus/cli/mixins/print_helper.rb +181 -17
  59. data/lib/morpheus/cli/mixins/provisioning_helper.rb +535 -458
  60. data/lib/morpheus/cli/mixins/whoami_helper.rb +2 -2
  61. data/lib/morpheus/cli/option_parser.rb +35 -0
  62. data/lib/morpheus/cli/option_types.rb +232 -192
  63. data/lib/morpheus/cli/recent_activity_command.rb +61 -65
  64. data/lib/morpheus/cli/remote.rb +446 -199
  65. data/lib/morpheus/cli/roles.rb +884 -906
  66. data/lib/morpheus/cli/security_group_rules.rb +213 -203
  67. data/lib/morpheus/cli/security_groups.rb +237 -192
  68. data/lib/morpheus/cli/shell.rb +338 -231
  69. data/lib/morpheus/cli/tasks.rb +326 -308
  70. data/lib/morpheus/cli/users.rb +457 -462
  71. data/lib/morpheus/cli/version.rb +1 -1
  72. data/lib/morpheus/cli/version_command.rb +16 -18
  73. data/lib/morpheus/cli/virtual_images.rb +526 -345
  74. data/lib/morpheus/cli/whoami.rb +125 -111
  75. data/lib/morpheus/cli/workflows.rb +338 -185
  76. data/lib/morpheus/formatters.rb +8 -1
  77. data/lib/morpheus/logging.rb +1 -1
  78. data/lib/morpheus/rest_client.rb +17 -8
  79. metadata +9 -3
  80. data/lib/morpheus/api/custom_instance_types.rb +0 -55
@@ -0,0 +1,126 @@
1
+ require 'fileutils'
2
+ require 'time'
3
+ require 'morpheus/cli/cli_registry'
4
+ require 'morpheus/logging'
5
+ require 'term/ansicolor'
6
+
7
+ class Morpheus::Cli::ConfigFile
8
+ include Term::ANSIColor
9
+ class << self
10
+ def init(filename=nil)
11
+ @instance ||= Morpheus::Cli::ConfigFile.new(filename)
12
+ end
13
+
14
+ def instance
15
+ #@instance ||= init(Morpheus::Cli.config_filename)
16
+ @instance or raise "#{self}.init() must be called!"
17
+ end
18
+
19
+ end
20
+
21
+ attr_reader :filename
22
+ attr_reader :config
23
+
24
+ def initialize(fn)
25
+ @config = {}
26
+ # only create the file if we're using the default, otherwise error
27
+ if fn
28
+ @filename = File.expand_path(fn)
29
+ else
30
+ @filename = File.expand_path(Morpheus::Cli.config_filename)
31
+ if !Dir.exists?(File.dirname(@filename))
32
+ FileUtils.mkdir_p(File.dirname(@filename))
33
+ end
34
+ if !File.exists?(@filename)
35
+ print "#{Term::ANSIColor.dark}Initializing default config file#{Term::ANSIColor.reset}\n" if Morpheus::Logging.debug?
36
+ FileUtils.touch(@filename)
37
+ save_file()
38
+ end
39
+ end
40
+ load_file()
41
+ end
42
+
43
+ def load_file
44
+ #puts "loading config #{@filename}"
45
+ @config = {}
46
+ if !@filename
47
+ return false
48
+ end
49
+ if !File.exist?(@filename)
50
+ raise "Morpheus cli config file not found: #{@filename}"
51
+ end
52
+ file_contents = File.read(@filename)
53
+ file_contents.split
54
+ config_text = File.open(@filename).read
55
+ config_lines = config_text.split(/\n/)
56
+ #config_lines = config_lines.reject {|line| line =~ /^\#/} # strip comments
57
+ config_lines.each_with_index do |line, line_index|
58
+ line_num = line_index + 1
59
+ line = line.strip
60
+ #puts "parsing config line #{line_num} : #{line}"
61
+ next if line.empty?
62
+ next if line =~ /^\#/ # skip comments
63
+
64
+ if line =~ /^alias\s+/
65
+ alias_name, command_string = Morpheus::Cli::CliRegistry.parse_alias_definition(line)
66
+ if alias_name.empty? || command_string.empty?
67
+ print "#{dark} #=> bad config line #{line_num} invalid alias definition: #{line}\n" if Morpheus::Logging.debug?
68
+ else
69
+ # @config[:aliases] ||= []
70
+ # @config[:aliases] << {name: alias_name, command: command_string}
71
+ begin
72
+ Morpheus::Cli::CliRegistry.instance.add_alias(alias_name, command_string)
73
+ rescue => err
74
+ print "#{dark} #=> #{err.message}#{reset}\n" if Morpheus::Logging.debug?
75
+ end
76
+ end
77
+ elsif line =~ /^colorize/
78
+ Term::ANSIColor::coloring = true
79
+ elsif line =~ /^uncolorize/
80
+ Term::ANSIColor::coloring = false
81
+ # what else shall we make configurable?
82
+ else
83
+ print "#{dark} #=> config line #{line_num} unrecognized: #{line}#{reset}\n" if Morpheus::Logging.debug?
84
+ end
85
+ end
86
+
87
+ # if @config[:aliases]
88
+ # @config[:aliases].each do |it|
89
+ # Morpheus::Cli::CliRegistry.instance.add_alias(it[:name], it[:command])
90
+ # end
91
+ # end
92
+
93
+ #puts "done loading config from #{}"
94
+
95
+ return @config
96
+ end
97
+
98
+ def save_file
99
+ if !@filename
100
+ print "#{Term::ANSIColor.dark}Skipping config file save because filename has not been set#{Term::ANSIColor.reset}\n" if Morpheus::Logging.debug?
101
+ return false
102
+ end
103
+ print "#{dark} #=> Saving config file #{@filename}#{reset}\n" if Morpheus::Logging.debug?
104
+ out = ""
105
+ out << "# .morpheusrc file #{@filename}"
106
+ out << "\n"
107
+ out << "# Auto-generated by morpheus #{Morpheus::Cli::VERSION} on #{Time.now.iso8601}"
108
+ out << "\n\n"
109
+ out << "# aliases"
110
+ out << "\n"
111
+ Morpheus::Cli::CliRegistry.instance.all_aliases.each do |k, v|
112
+ out << "alias #{k}='#{v}'"
113
+ out << "\n"
114
+ end
115
+ out << "\n"
116
+ File.open(@filename, 'w') {|f| f.write(out) }
117
+ return true
118
+ end
119
+
120
+ # this will load any local changes, and then resave everything in the config (shell)
121
+ def reload_file
122
+ load_file
123
+ save_file
124
+ end
125
+
126
+ end
@@ -1,134 +1,158 @@
1
+ require 'fileutils'
1
2
  require 'yaml'
3
+ require 'json'
2
4
  require 'io/console'
3
5
  require 'rest_client'
4
- #require 'optparse'
6
+ require 'morpheus/logging'
5
7
  require 'morpheus/cli/mixins/print_helper'
6
- require 'json'
7
8
 
8
9
  module Morpheus
9
- module Cli
10
- class Credentials
11
- include Morpheus::Cli::PrintHelper
10
+ module Cli
11
+ class Credentials
12
+ include Morpheus::Cli::PrintHelper
13
+
14
+ @@saved_credentials_map = nil
12
15
 
13
- def initialize(appliance_name, appliance_url)
14
- @appliance_url = appliance_url
15
- @appliance_name = appliance_name
16
- end
17
-
18
- def request_credentials(opts = {})
19
- username = nil
20
- password = nil
21
- creds = nil
22
- skip_save = false
23
- # We should return an access Key for Morpheus CLI Here
24
- if !opts[:remote_username].nil?
25
- username = opts[:remote_username]
26
- password = opts[:remote_password]
27
- skip_save = opts[:remote_url] ? true : false
28
- else
29
- creds = load_saved_credentials
30
- end
31
- if !creds
32
- print "\nEnter Morpheus Credentials for #{@appliance_name} - #{@appliance_url}\n\n",reset
33
- if username.nil? || username.empty?
34
- # print "Username: "
35
- print "Username: #{required_blue_prompt} "
36
- username = $stdin.gets.chomp!
37
- end
38
- if password.nil? || password.empty?
39
- # print "Password: "
40
- print "Password: #{required_blue_prompt} "
41
- password = STDIN.noecho(&:gets).chomp!
42
- print "\n"
43
- end
16
+ def initialize(appliance_name, appliance_url)
17
+ @appliance_url = appliance_url
18
+ @appliance_name = appliance_name
19
+ end
20
+
21
+ def request_credentials(opts = {})
22
+ #puts "request_credentials(#{opts})"
23
+ username = nil
24
+ password = nil
25
+ creds = nil
26
+ skip_save = false
27
+ # We should return an access Key for Morpheus CLI Here
28
+ if !opts[:remote_username].nil?
29
+ username = opts[:remote_username]
30
+ password = opts[:remote_password]
31
+ skip_save = opts[:remote_url] ? true : false
32
+ else
33
+ creds = load_saved_credentials
34
+ end
35
+ if creds
36
+ return creds
37
+ end
38
+ unless opts[:quiet] || opts[:no_prompt]
39
+ # if username.empty? || password.empty?
40
+ print "Enter Morpheus Credentials for #{display_appliance(@appliance_name, @appliance_url)}\n",reset
41
+ # end
42
+ if username.empty?
43
+ print "Username: #{required_blue_prompt} "
44
+ username = $stdin.gets.chomp!
45
+ else
46
+ print "Username: #{required_blue_prompt} #{username}\n"
47
+ end
48
+ if password.empty?
49
+ print "Password: #{required_blue_prompt} "
50
+ password = STDIN.noecho(&:gets).chomp!
51
+ print "\n"
52
+ else
53
+ print "Password: #{required_blue_prompt} \n"
54
+ end
55
+ end
56
+ if username.empty? || password.empty?
57
+ print_red_alert "Username and password are required to login."
58
+ return nil
59
+ end
60
+ begin
61
+ auth_interface = Morpheus::AuthInterface.new(@appliance_url)
62
+ json_response = auth_interface.login(username, password)
63
+ if opts[:json]
64
+ print JSON.pretty_generate(json_response)
65
+ print reset, "\n"
66
+ end
67
+ access_token = json_response['access_token']
68
+ if !access_token.empty?
69
+ unless skip_save
70
+ save_credentials(@appliance_name, access_token)
71
+ end
72
+ return access_token
73
+ else
74
+ print_red_alert "Credentials not verified."
75
+ return nil
76
+ end
77
+ rescue ::RestClient::Exception => e
78
+ #raise e
79
+ if (e.response && e.response.code == 400)
80
+ print_red_alert "Credentials not verified."
81
+ if opts[:json]
82
+ json_response = JSON.parse(e.response.to_s)
83
+ print JSON.pretty_generate(json_response)
84
+ print reset, "\n"
85
+ end
86
+ else
87
+ print_rest_exception(e, opts)
88
+ end
89
+ end
44
90
 
45
- oauth_url = File.join(@appliance_url, "/oauth/token")
46
- begin
47
- authorize_response = Morpheus::RestClient.execute(method: :post, url: oauth_url, headers:{ params: {grant_type: 'password', scope:'write', client_id: 'morph-cli', username: username}}, payload: {password: password},verify_ssl: false, timeout: 10)
91
+ end
48
92
 
49
- json_response = JSON.parse(authorize_response.to_s)
50
- access_token = json_response['access_token']
51
- if !access_token.empty?
52
- save_credentials(access_token) unless skip_save
53
- return access_token
54
- else
55
- print_red_alert "Credentials not verified."
56
- return nil
57
- end
58
- rescue ::RestClient::Exception => e
59
- if (e.response && e.response.code == 400)
60
- print_red_alert "Credentials not verified."
61
- if opts[:json]
62
- json_response = JSON.parse(e.response.to_s)
63
- print JSON.pretty_generate(json_response)
64
- print reset, "\n\n"
65
- end
66
- else
67
- print_rest_exception(e, opts)
68
- end
69
- exit 1
70
- rescue => e
71
- print_red_alert "Error Communicating with the Appliance. #{e}"
72
- exit 1
73
- end
74
- else
75
- return creds
76
- end
77
- end
93
+ def login(opts = {})
94
+ clear_saved_credentials(@appliance_name)
95
+ request_credentials(opts)
96
+ end
78
97
 
79
- def login(opts = {})
80
- clear_saved_credentials
81
- request_credentials(opts)
82
- end
98
+ def logout()
99
+ clear_saved_credentials(@appliance_name)
100
+ end
83
101
 
84
- def logout()
85
- clear_saved_credentials
86
- end
102
+ def clear_saved_credentials(appliance_name)
103
+ @@saved_credentials_map = load_credentials_file || {}
104
+ @@saved_credentials_map.delete(appliance_name)
105
+ print "#{dark} #=> updating credentials file #{credentials_file_path}#{reset}\n" if Morpheus::Logging.debug?
106
+ File.open(credentials_file_path, 'w') {|f| f.write @@saved_credentials_map.to_yaml } #Store
107
+ end
87
108
 
88
- def clear_saved_credentials()
89
- credential_map = load_credential_file
90
- if credential_map.nil?
91
- credential_map = {}
92
- end
93
- credential_map.delete(@appliance_name)
94
- File.open(credentials_file_path, 'w') {|f| f.write credential_map.to_yaml } #Store
95
- end
109
+ def load_saved_credentials(reload=false)
110
+ if saved_credentials_map && !reload
111
+ return saved_credentials_map
112
+ end
113
+ @@saved_credentials_map = load_credentials_file || {}
114
+ return @@saved_credentials_map[@appliance_name]
115
+ end
96
116
 
97
- def load_saved_credentials()
98
- credential_map = load_credential_file
99
- if credential_map.nil?
100
- return nil
101
- else
102
- return credential_map[@appliance_name]
103
- end
104
- end
117
+ # Provides the current credential information, simply :appliance_name => "access_token"
118
+ def saved_credentials_map
119
+ if !defined?(@@saved_credentials_map)
120
+ @@saved_credentials_map = load_credentials_file
121
+ end
122
+ return @@saved_credentials_map ? @@saved_credentials_map[@appliance_name.to_sym] : nil
123
+ end
105
124
 
106
- def load_credential_file
107
- creds_file = credentials_file_path
108
- if File.exist? creds_file
109
- return YAML.load_file(creds_file)
110
- else
111
- return nil
112
- end
113
- end
125
+ def load_credentials_file
126
+ fn = credentials_file_path
127
+ if File.exist? fn
128
+ print "#{dark} #=> loading credentials file #{fn}#{reset}\n" if Morpheus::Logging.debug?
129
+ return YAML.load_file(fn)
130
+ else
131
+ return nil
132
+ end
133
+ end
114
134
 
115
- def credentials_file_path
116
- home_dir = Dir.home
117
- morpheus_dir = File.join(home_dir,".morpheus")
118
- if !Dir.exist?(morpheus_dir)
119
- Dir.mkdir(morpheus_dir)
120
- end
121
- return File.join(morpheus_dir,"credentials")
122
- end
135
+ def credentials_file_path
136
+ File.join(Morpheus::Cli.home_directory, "credentials")
137
+ end
123
138
 
124
- def save_credentials(token)
125
- credential_map = load_credential_file
126
- if credential_map.nil?
127
- credential_map = {}
128
- end
129
- credential_map[@appliance_name] = token
130
- File.open(credentials_file_path, 'w') {|f| f.write credential_map.to_yaml } #Store
131
- end
132
- end
133
- end
139
+ def save_credentials(app_name, token)
140
+ # credential_map = saved_credentials_map
141
+ # reloading file is better for now, otherwise you can lose credentials with multiple shells.
142
+ credential_map = load_credentials_file || {}
143
+ if credential_map.nil?
144
+ credential_map = {}
145
+ end
146
+ credential_map[app_name] = token
147
+ begin
148
+ fn = credentials_file_path
149
+ print "#{dark} #=> adding credentials to #{fn}#{reset}\n" if Morpheus::Logging.debug?
150
+ File.open(fn, 'w') {|f| f.write credential_map.to_yaml } #Store
151
+ FileUtils.chmod(0600, fn)
152
+ rescue => e
153
+ puts "failed to save #{fn}. #{e}" if Morpheus::Logging.debug?
154
+ end
155
+ end
156
+ end
157
+ end
134
158
  end
@@ -4,70 +4,62 @@ require 'json'
4
4
 
5
5
  class Morpheus::Cli::DashboardCommand
6
6
  include Morpheus::Cli::CliCommand
7
-
8
- cli_command_name :dashboard
9
- cli_command_hidden # remove once this is done
10
-
11
- def initialize()
12
- @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
13
- end
7
+ set_command_name :dashboard
8
+ set_command_hidden # remove once this is done
14
9
 
15
- def connect(opts)
16
- @access_token = Morpheus::Cli::Credentials.new(@appliance_name,@appliance_url).load_saved_credentials()
17
- if @access_token.empty?
18
- print_red_alert "Invalid Credentials. Unable to acquire access token. Please verify your credentials and try again."
19
- exit 1
20
- end
21
- @api_client = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url)
22
- @dashboard_interface = @api_client.dashboard
23
- end
10
+ def initialize()
11
+ # @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
12
+ end
24
13
 
25
- def usage
26
- "Usage: morpheus dashboard"
27
- end
14
+ def connect(opts)
15
+ @api_client = establish_remote_appliance_connection(opts)
16
+ @dashboard_interface = @api_client.dashboard
17
+ end
28
18
 
29
- def handle(args)
30
- show(args)
31
- end
32
-
33
- def show(args)
34
- options = {}
35
- optparse = OptionParser.new do|opts|
36
- opts.banner = usage
37
- build_common_options(opts, options, [:json]) # todo: support :account
38
- end
39
- optparse.parse(args)
19
+ def usage
20
+ "Usage: morpheus #{command_name}"
21
+ end
40
22
 
41
- connect(options)
42
- begin
43
-
44
- params = {}
23
+ def handle(args)
24
+ show(args)
25
+ end
26
+
27
+ def show(args)
28
+ options = {}
29
+ optparse = OptionParser.new do|opts|
30
+ opts.banner = usage
31
+ build_common_options(opts, options, [:json, :dry_run]) # todo: support :account
32
+ end
33
+ optparse.parse!(args)
45
34
 
46
- json_response = @dashboard_interface.get(params)
47
-
48
- if options[:json]
49
- print JSON.pretty_generate(json_response)
50
- print "\n"
51
- else
52
-
35
+ connect(options)
36
+ begin
37
+ params = {}
38
+ if options[:dry_run]
39
+ print_dry_run @dashboard_interface.dry.get(params)
40
+ return
41
+ end
42
+ json_response = @dashboard_interface.get(params)
43
+ if options[:json]
44
+ print JSON.pretty_generate(json_response)
45
+ print "\n"
46
+ else
53
47
 
54
- # todo: impersonate command and show that info here
48
+ # todo: impersonate command and show that info here
55
49
 
56
- print "\n" ,cyan, bold, "Dashboard\n","==================", reset, "\n\n"
57
- print cyan
58
-
59
- print "\n"
60
- puts "Coming soon.... see --json"
61
- print "\n"
50
+ print "\n" ,cyan, bold, "Dashboard\n","==================", reset, "\n\n"
51
+ print cyan
52
+ print "\n"
53
+ puts "Coming soon.... see --json"
54
+ print "\n"
62
55
 
63
- print reset,"\n"
56
+ print reset,"\n"
64
57
 
65
- end
66
- rescue RestClient::Exception => e
67
- print_rest_exception(e, options)
68
- exit 1
69
- end
70
- end
71
-
58
+ end
59
+ rescue RestClient::Exception => e
60
+ print_rest_exception(e, options)
61
+ exit 1
62
+ end
63
+ end
72
64
 
73
65
  end