morpheus-cli 5.5.2.2 → 5.5.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/Dockerfile +1 -1
  4. data/README.md +57 -4
  5. data/Rakefile +9 -0
  6. data/bin/morpheus +4 -4
  7. data/lib/morpheus/api/api_client.rb +20 -2
  8. data/lib/morpheus/api/appliance_settings_interface.rb +15 -0
  9. data/lib/morpheus/api/archive_buckets_interface.rb +1 -1
  10. data/lib/morpheus/api/archive_files_interface.rb +3 -3
  11. data/lib/morpheus/api/clients_interface.rb +2 -2
  12. data/lib/morpheus/api/clusters_interface.rb +8 -1
  13. data/lib/morpheus/api/containers_interface.rb +29 -16
  14. data/lib/morpheus/api/custom_instance_types_interface.rb +0 -2
  15. data/lib/morpheus/api/cypher_interface.rb +1 -2
  16. data/lib/morpheus/api/doc_interface.rb +8 -6
  17. data/lib/morpheus/api/file_copy_request_interface.rb +1 -1
  18. data/lib/morpheus/api/guidance_settings_interface.rb +17 -0
  19. data/lib/morpheus/api/health_interface.rb +1 -1
  20. data/lib/morpheus/api/image_builder_interface.rb +3 -3
  21. data/lib/morpheus/api/instances_interface.rb +25 -0
  22. data/lib/morpheus/api/logs_interface.rb +2 -4
  23. data/lib/morpheus/api/monitoring_interface.rb +6 -6
  24. data/lib/morpheus/api/monitoring_settings_interface.rb +25 -0
  25. data/lib/morpheus/api/network_server_groups_interface.rb +7 -0
  26. data/lib/morpheus/api/packages_interface.rb +1 -1
  27. data/lib/morpheus/api/reports_interface.rb +1 -1
  28. data/lib/morpheus/api/servers_interface.rb +9 -1
  29. data/lib/morpheus/api/storage_providers_interface.rb +2 -2
  30. data/lib/morpheus/api/virtual_images_interface.rb +1 -1
  31. data/lib/morpheus/api.rb +2 -0
  32. data/lib/morpheus/benchmarking.rb +1 -1
  33. data/lib/morpheus/cli/cli_command.rb +79 -37
  34. data/lib/morpheus/cli/cli_registry.rb +19 -10
  35. data/lib/morpheus/cli/commands/access_token_command.rb +1 -1
  36. data/lib/morpheus/cli/commands/appliance_settings_command.rb +57 -2
  37. data/lib/morpheus/cli/commands/apps.rb +1 -1
  38. data/lib/morpheus/cli/commands/archives_command.rb +25 -33
  39. data/lib/morpheus/cli/commands/backup_settings_command.rb +1 -1
  40. data/lib/morpheus/cli/commands/blueprints_command.rb +10 -21
  41. data/lib/morpheus/cli/commands/boot_scripts_command.rb +2 -2
  42. data/lib/morpheus/cli/commands/cat_command.rb +1 -1
  43. data/lib/morpheus/cli/commands/catalog_item_types_command.rb +18 -13
  44. data/lib/morpheus/cli/commands/clouds.rb +3 -3
  45. data/lib/morpheus/cli/commands/clusters.rb +154 -3
  46. data/lib/morpheus/cli/commands/containers_command.rb +398 -253
  47. data/lib/morpheus/cli/commands/cypher_command.rb +3 -0
  48. data/lib/morpheus/cli/commands/deployments.rb +1 -1
  49. data/lib/morpheus/cli/commands/deploys.rb +9 -9
  50. data/lib/morpheus/cli/commands/doc.rb +15 -16
  51. data/lib/morpheus/cli/commands/execution_request_command.rb +2 -2
  52. data/lib/morpheus/cli/commands/file_copy_request_command.rb +5 -5
  53. data/lib/morpheus/cli/commands/groups.rb +2 -2
  54. data/lib/morpheus/cli/commands/guidance_command.rb +2 -2
  55. data/lib/morpheus/cli/commands/guidance_settings.rb +148 -0
  56. data/lib/morpheus/cli/commands/health_command.rb +4 -4
  57. data/lib/morpheus/cli/commands/hosts.rb +43 -5
  58. data/lib/morpheus/cli/commands/image_builder_command.rb +1 -1
  59. data/lib/morpheus/cli/commands/instances.rb +419 -148
  60. data/lib/morpheus/cli/commands/integrations_command.rb +22 -20
  61. data/lib/morpheus/cli/commands/key_pairs.rb +2 -2
  62. data/lib/morpheus/cli/commands/library_container_scripts_command.rb +2 -2
  63. data/lib/morpheus/cli/commands/library_container_templates_command.rb +2 -2
  64. data/lib/morpheus/cli/commands/library_instance_types_command.rb +3 -3
  65. data/lib/morpheus/cli/commands/library_spec_templates_command.rb +2 -2
  66. data/lib/morpheus/cli/commands/log_settings_command.rb +1 -1
  67. data/lib/morpheus/cli/commands/login.rb +1 -1
  68. data/lib/morpheus/cli/commands/man_command.rb +32 -18
  69. data/lib/morpheus/cli/commands/monitoring_settings.rb +228 -0
  70. data/lib/morpheus/cli/commands/network_server_groups_command.rb +222 -0
  71. data/lib/morpheus/cli/commands/packages_command.rb +11 -11
  72. data/lib/morpheus/cli/commands/plugins.rb +1 -1
  73. data/lib/morpheus/cli/commands/policies_command.rb +4 -4
  74. data/lib/morpheus/cli/commands/preseed_scripts_command.rb +2 -2
  75. data/lib/morpheus/cli/commands/provisioning_settings_command.rb +1 -1
  76. data/lib/morpheus/cli/commands/remote.rb +1 -1
  77. data/lib/morpheus/cli/commands/reports_command.rb +13 -3
  78. data/lib/morpheus/cli/commands/security_groups.rb +1 -1
  79. data/lib/morpheus/cli/commands/shell.rb +40 -62
  80. data/lib/morpheus/cli/commands/snapshots.rb +3 -5
  81. data/lib/morpheus/cli/commands/source_command.rb +8 -16
  82. data/lib/morpheus/cli/commands/storage_providers_command.rb +7 -7
  83. data/lib/morpheus/cli/commands/tasks.rb +2 -2
  84. data/lib/morpheus/cli/commands/vdi_pools_command.rb +6 -6
  85. data/lib/morpheus/cli/commands/view.rb +5 -1
  86. data/lib/morpheus/cli/commands/whitelabel_settings_command.rb +5 -5
  87. data/lib/morpheus/cli/commands/whoami.rb +2 -2
  88. data/lib/morpheus/cli/credentials.rb +30 -8
  89. data/lib/morpheus/cli/dot_file.rb +8 -15
  90. data/lib/morpheus/cli/error_handler.rb +16 -0
  91. data/lib/morpheus/cli/errors.rb +8 -1
  92. data/lib/morpheus/cli/mixins/print_helper.rb +17 -13
  93. data/lib/morpheus/cli/mixins/provisioning_helper.rb +14 -12
  94. data/lib/morpheus/cli/mixins/rest_command.rb +23 -19
  95. data/lib/morpheus/cli/mixins/secondary_rest_command.rb +47 -24
  96. data/lib/morpheus/cli/option_parser.rb +5 -1
  97. data/lib/morpheus/cli/option_types.rb +59 -12
  98. data/lib/morpheus/cli/version.rb +1 -1
  99. data/lib/morpheus/cli.rb +26 -16
  100. data/lib/morpheus/ext/rest_client.rb +3 -2
  101. data/lib/morpheus/ext/string.rb +6 -4
  102. data/lib/morpheus/formatters.rb +1 -1
  103. data/lib/morpheus/logging.rb +4 -4
  104. data/lib/morpheus/morpkg.rb +4 -4
  105. data/lib/morpheus/rest_client.rb +2 -2
  106. data/lib/morpheus/routes.rb +41 -9
  107. data/lib/morpheus/terminal.rb +65 -16
  108. data/lib/morpheus.rb +1 -1
  109. data/morpheus-cli.gemspec +1 -0
  110. data/test/api/containers_interface_test.rb +68 -0
  111. data/test/api/doc_interface_test.rb +35 -0
  112. data/test/api/instances_interface_test.rb +22 -0
  113. data/test/api/whoami_interface_test.rb +14 -0
  114. data/test/cli/access_token_test.rb +36 -0
  115. data/test/cli/auth_test.rb +82 -0
  116. data/test/cli/cli_test.rb +48 -0
  117. data/test/cli/containers_test.rb +92 -0
  118. data/test/cli/doc_test.rb +35 -0
  119. data/test/cli/help_test.rb +25 -0
  120. data/test/cli/instances_test.rb +36 -0
  121. data/test/cli/man_test.rb +14 -0
  122. data/test/cli/remote_test.rb +89 -0
  123. data/test/cli/roles_test.rb +34 -0
  124. data/test/cli/shell_test.rb +81 -0
  125. data/test/cli/version_test.rb +23 -0
  126. data/test/cli/view_test.rb +55 -0
  127. data/test/cli/whoami_test.rb +17 -0
  128. data/test/morpheus_test.rb +16 -0
  129. data/test/test_case.rb +338 -0
  130. data/test/test_config.rb +137 -0
  131. data/test/test_data_helper.rb +97 -0
  132. metadata +67 -3
@@ -0,0 +1,81 @@
1
+ require 'morpheus_test'
2
+
3
+ class MorpheusTest::ShellTest < MorpheusTest::TestCase
4
+
5
+ def requires_remote
6
+ false
7
+ end
8
+
9
+ def requires_authentication
10
+ false
11
+ end
12
+
13
+ # class << self
14
+ # def startup
15
+ # # one time use remote and login at beginning of testsuite, not each method
16
+ # use_remote()
17
+ # login()
18
+ # end
19
+ # end
20
+
21
+ # def test_remote_use
22
+ # # use remote needed for all except test_shell_clean() , todo: exclude paradigm that yet..
23
+ # use_remote()
24
+ # end
25
+
26
+ def test_shell
27
+ with_input "exit" do
28
+ assert_execute "shell"
29
+ end
30
+ end
31
+
32
+ def test_shell_verbose
33
+ login_if_needed()
34
+ with_input "whoami", "exit" do
35
+ assert_execute "shell -V"
36
+ end
37
+ end
38
+
39
+ def test_shell_history
40
+ with_input "history", "exit" do
41
+ assert_execute "shell"
42
+ end
43
+ end
44
+
45
+ def test_shell_sleep
46
+ with_input "echo \"It is time for a rest.\"", "sleep 0.5", "echo \"OK, let's keep testing\"", "exit" do
47
+ assert_execute "shell -V"
48
+ end
49
+ end
50
+
51
+ def test_shell_temporary
52
+ with_input "remote list", "remote current", "echo this is a temporary shell with no history", "history", "exit" do
53
+ assert_execute "shell -Z -V"
54
+ end
55
+ end
56
+
57
+ def test_shell_confirmation
58
+ with_input "access-token refresh", "I'm not sure...", "no", "exit" do
59
+ assert_execute "shell -Z -V"
60
+ end
61
+ end
62
+
63
+ # todo: fix bug where appliances are not restored after clean shell.. so remote use #{@config.remote_name}" starts failing here..
64
+ =begin
65
+ def test_shell_clean
66
+ with_input "echo this is a clean shell with no remotes or history", "remote list", "remote current", "history", "exit" do
67
+ assert_execute "shell -z"
68
+ end
69
+ with_input "echo this is another clean shell", "exit" do
70
+ assert_execute "shell --clean"
71
+ end
72
+ end
73
+
74
+ def test_shell_history_again
75
+ with_input "echo back in test shell again with our remote and history", "remote get #{@config.remote_name}", "history", "exit" do
76
+ assert_execute "shell"
77
+ end
78
+ end
79
+ =end
80
+
81
+ end
@@ -0,0 +1,23 @@
1
+ require 'morpheus_test'
2
+
3
+ # Tests for Morpheus::Cli::VersionCommand
4
+ class MorpheusTest::VersionTest < MorpheusTest::TestCase
5
+
6
+ def requires_authentication
7
+ false
8
+ end
9
+
10
+ def test_version
11
+ assert_execute %(version)
12
+ end
13
+
14
+ def test_version_short
15
+ assert_execute %(version -v)
16
+ assert_execute %(version --short)
17
+ end
18
+
19
+ # def test_version_help
20
+ # assert_execute %(version --help)
21
+ # end
22
+
23
+ end
@@ -0,0 +1,55 @@
1
+ require 'morpheus_test'
2
+
3
+ # Tests for Morpheus::Cli::View class, corresponds to the `view` CLI command
4
+ class MorpheusTest::ViewTest < MorpheusTest::TestCase
5
+
6
+ def teardown
7
+ # todo: close browser after each test
8
+ end
9
+
10
+ # these all pass but it's kind of obnoxious to open so many tabs
11
+
12
+ =begin
13
+ def test_view
14
+ assert_execute %(view)
15
+ end
16
+
17
+ def test_view_login
18
+ assert_execute %(view --login)
19
+ #puts "pausing a moment while logging in with browser"
20
+ #sleep(3)
21
+
22
+ end
23
+
24
+ def test_view_login_short
25
+ assert_execute %(view -l)
26
+ #puts "pausing a moment while logging in with browser"
27
+ #sleep(3)
28
+ end
29
+
30
+ def test_view_clouds
31
+ assert_execute %(view clouds)
32
+ end
33
+
34
+ def test_view_cloud_by_id
35
+ cloud = client.clouds.list({})['zones'][0]
36
+ if cloud
37
+ assert_execute %(view cloud "#{escape_arg cloud['id']}")
38
+ else
39
+ puts "No cloud found, unable to execute test `#{__method__}`"
40
+ end
41
+ end
42
+
43
+ def test_view_instance_by_name
44
+ assert_execute %(view -l)
45
+ instance = client.instances.list({})['instances'][0]
46
+ if instance
47
+ assert_execute %(view instance "#{escape_arg instance['name']}")
48
+ else
49
+ puts "No instance found, unable to execute test `#{__method__}`"
50
+ end
51
+ end
52
+ =end
53
+ protected
54
+
55
+ end
@@ -0,0 +1,17 @@
1
+ require 'morpheus_test'
2
+
3
+ class MorpheusTest::WhoamiTest < MorpheusTest::TestCase
4
+
5
+ def test_whoami
6
+ assert_execute("whoami")
7
+ assert_execute("whoami --name")
8
+ assert_execute("whoami --permissions")
9
+ end
10
+
11
+ def test_whoami_unauthorized
12
+ without_authentication do
13
+ assert_error("whoami", "Expected error while unauthorized")
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1,16 @@
1
+ require 'test/unit'
2
+ require 'test_case'
3
+ require 'test_data_helper'
4
+
5
+ # setup at_start hook that runs once at the beginning of all tests
6
+ Test::Unit.at_start {
7
+
8
+ }
9
+
10
+ # setup at_exit hook that runs once at the end of all tests
11
+ Test::Unit.at_exit {
12
+ # always logout when all done
13
+ # logout_if_needed()
14
+ # terminal.execute("logout") if is_logged_in()
15
+ Morpheus::Terminal.instance.execute("logout")
16
+ }
data/test/test_case.rb ADDED
@@ -0,0 +1,338 @@
1
+ require 'test/unit'
2
+ # use a different default home directory when running tests
3
+ ENV['MORPHEUS_CLI_HOME'] = ENV['TEST_CLI_HOME'] || File.join(Dir.home, ".morpheus_test")
4
+ require 'morpheus'
5
+ require 'test_config'
6
+ #require 'securerandom'
7
+
8
+ module MorpheusTest
9
+
10
+ # TestCase is the base class for all unit tests to provide standard behavior
11
+ # for testing CLI commands and API interfaces.
12
+ #
13
+ # +assert_execute+ is available to execute a CLI command or expression and assert the result is a success (exit:0) by default.
14
+ #
15
+ # +terminal+ is available to execute any commands eg. +terminal.execute("source foo")+
16
+ #
17
+ # +client+ is available for executing API requests
18
+ #
19
+ class TestCase < Test::Unit::TestCase
20
+ include Morpheus::Cli::PrintHelper # printhelper for print_red_alert and what not.. should probably not be included in MorpheusTest
21
+
22
+ # execute test cases in the order they are defined.
23
+ self.test_order = :defined
24
+
25
+ # indicates the test requires the user to have a current remote
26
+ # override this to return false if your tests do not require `remote use` as part of its setup
27
+ def requires_remote
28
+ true
29
+ end
30
+
31
+ # indicates the test requires the user to be logged in and authenticated
32
+ # override this to return false if your tests do not require `login` as part of its setup
33
+ def requires_authentication
34
+ true
35
+ end
36
+
37
+ # hook at the beginning of each test
38
+ def setup()
39
+ #puts "TestCase #{self} setup()"
40
+ # @config is provided for accessing test environment settings in our tests
41
+ @config = get_config()
42
+ # use the remote and login if needed
43
+ use_remote() if requires_remote
44
+ login_if_needed() if requires_authentication
45
+ end
46
+
47
+ # hook at the end of each test
48
+ def teardown()
49
+ #puts "TestCase #{self} teardown()"
50
+ #logout_if_needed() if requires_authentication
51
+ end
52
+
53
+ # def initialize(*args)
54
+ # obj = super(*args)
55
+ # @client = establish_test_terminal()
56
+ # end
57
+
58
+ protected
59
+
60
+ # @return [Morpheus::APIClient] client for executing api requests in our tests and examining the results
61
+ def client
62
+ # todo: return terminal.get_api_client()
63
+ #@api_client ||= Morpheus::APIClient.new(url: @config.url, username: @config.username, password: @config.password_decrypted, verify_ssl: false, client_id: 'morph-api')
64
+ #@api_client.login() unless @api_client.logged_in?
65
+ # this only works while logged in, fine for now...
66
+ @client ||= Morpheus::APIClient.new(url: @config.url, username: @config.username, password: @config.password, access_token: get_access_token(), verify_ssl: false, client_id: 'morph-cli')
67
+ end
68
+
69
+
70
+
71
+ # Execute the command in the CLI terminal and assert the result
72
+ # By default, the result should be a success with exit code: 0 and error: nil
73
+ # @param [String] cmd the command or expression to be executed by the CLI terminal
74
+ # @param [Hash,String] opts. See +assert_command_result+ If passed as a String, then treated as failure message.
75
+ # @param [String] failure_message Optional message to use on failure used instead of the default "Expected command..."
76
+ #
77
+ # @example Assert a command succeeds with a 0 exit status
78
+ # assert_execute("whoami")
79
+ #
80
+ # @example Assert that a command fails with any non zero exit status
81
+ # assert_execute("foobar", success:false, "Expected unknown command to fail")
82
+ #
83
+ # @example Assert a specific exit status code
84
+ # assert_execute("foobar", exit:1, "Expected unknown command to fail")
85
+ #
86
+ def assert_execute(cmd, opts = {}, failure_message = nil)
87
+ opts = opts.is_a?(String) ? {failure_message:opts} : (opts || {})
88
+ result = terminal.execute(cmd)
89
+ assert_command_result(result, opts, failure_message)
90
+ end
91
+
92
+ # +assert_success()+ is an alias for +assert_execute()+
93
+ alias :assert_success :assert_execute
94
+
95
+ # Execute the command in the CLI terminal and assert that the result is in an error (exit non-0)
96
+ # @param [String] cmd the command or expression to be executed by the CLI terminal
97
+ # @param [Hash,String] opts. See +assert_command_result+ If passed as a String, then treated as failure message.
98
+ # @param [String] failure_message Optional message to use on failure used instead of the default "Expected command to succeed|fail..."
99
+ #
100
+ # @example Assert a command fails with an error
101
+ # assert_error("apps list --unknown-option", "Expected unknown option error")
102
+ #
103
+ def assert_error(cmd, opts = {}, failure_message = nil)
104
+ opts = opts.is_a?(String) ? {failure_message:opts} : (opts || {})
105
+ assert_execute(cmd, opts.merge(success:false), failure_message)
106
+ end
107
+
108
+
109
+ # Assert that a command is has the expected exit status and message
110
+ # The default behavior is to assert the result is successful, exit: 0, error: nil
111
+ # use {success:false} to assert non-zero
112
+ # or use {exit:1, error: "something bad happened"} to assert a specific error exit and/or message
113
+ # @param [Array] Result containing: [exit_code, error].
114
+ # @param [Hash,String] opts. Hash of options for assertion. If passed as a String, then treated as failure message.
115
+ # @option opts [true,false] :success Expect success (true) or failure (false). Default is +true+, set to +false+ to assert that an error occurs instead.
116
+ # @option opts [Integer] :exit Expect a specific exit status code. The default is 0 on success or anything but 0 for failure.
117
+ # @option opts [String] :failure Optional message to use on failure used instead of the default "Expected command to succe..."
118
+ # @param [String] failure_message Optional message to use on failure used instead of the default "Expected command to succeed|fail..."
119
+ def assert_command_result(result, opts = {}, failure_message = nil)
120
+ # determine what to assert
121
+ success = opts.key?(:success) ? opts[:success] : true
122
+ expected_code = opts[:exit] ? opts[:exit] : (success ? 0 : nil)
123
+ if expected_code && expected_code != 0
124
+ success = false
125
+ end
126
+ expected_message = opts[:error]
127
+ failure_message = failure_message || opts[:failure_message] || opts[:failure]
128
+ # parse command result
129
+ exit_code, err = Morpheus::Cli::CliRegistry.parse_command_result(result)
130
+ # result assertions
131
+ if success
132
+ assert_equal([0, nil], [exit_code, err], failure_message || "Expected command to succeed with exit code: 0 and instead got exit code: #{exit_code.inspect}, error: #{err.inspect}")
133
+ else
134
+ if expected_code
135
+ assert_equal(expected_code, exit_code, failure_message || "Expected command to fail with exit code: #{expected_code.inspect} and instead got exit code: #{exit_code.inspect}, error: #{err.inspect}")
136
+ else
137
+ assert_not_equal(0, exit_code, failure_message || "Expected command to fail with exit code: (non-zero) and instead got exit code: #{exit_code.inspect}, error: #{err.inspect}")
138
+ end
139
+ if expected_message
140
+ assert_equal(expected_message, err, "Expected command to fail with error message: #{expected_message.inspect} and instead got error message: #{err.inspect}")
141
+ end
142
+ end
143
+ end
144
+
145
+ # Get the shared CLI terminal
146
+ # @return [Morpheus::Terminal]
147
+ def terminal
148
+ # return Morpheus::Terminal.instance
149
+ establish_test_terminal()
150
+ Thread.current[:terminal]
151
+ end
152
+
153
+ # Get the shared test configuration settings
154
+ # parses environment variables and config file to establish CLI environment
155
+ # @return [Map] containing :url, :username, etc., including :password!
156
+ def get_config(refresh=false)
157
+ Thread.current[:config] ||= TestConfig.new
158
+ end
159
+
160
+ # todo: make this work IOError unitialized stream right now
161
+ QUIET = ENV['QUIET'] ? true : false unless defined?(QUIET)
162
+
163
+ # Creates a remote for executing unit tests
164
+ # This requires the user to set environment variables: TEST_URL, TEST_USERNAME and TEST_PASSWORD or TEST_CONFIG
165
+ # By default, test_config.yaml in the current directory can be used.
166
+ # The home directory can be specified with TEST_HOME, and the default TEST_CONFIG filename is ~/.morpheus_test
167
+ # returns the Morpheus::Terminal instance for executing CLI commands
168
+ def establish_test_terminal()
169
+ if Thread.current[:terminal].nil?
170
+ config = get_config()
171
+
172
+ # create the terminal scoped to our test environment
173
+ terminal_opts = {homedir: config.homedir}
174
+ Thread.current[:terminal] = Morpheus::Terminal.new(terminal_opts)
175
+
176
+ # debug is not a terminal option right now, set it this way instead...
177
+ if config.debug # || Morpheus::Logging.debug?
178
+ # Morpheus::Logging.set_log_level(Morpheus::Logging::Logger::DEBUG)
179
+ terminal.execute("debug")
180
+ else
181
+ if QUIET
182
+ #blackhole = Morpheus::Terminal::Blackhole.new('blackhole')
183
+ blackhole = File.open(Morpheus::Cli.windows? ? 'NUL:' : '/dev/null', 'w')
184
+ terminal.set_stdout(blackhole)
185
+ terminal.set_stderr(blackhole)
186
+ end
187
+ end
188
+
189
+ #terminal.execute("remote get \"#{config.remote_name}\" -q || remote add \"#{config.remote_name}\" \"#{config.url}\" --insecure --use -N")
190
+ appliance = Morpheus::Cli::Remote.load_remote(config.remote_name)
191
+ if appliance
192
+ # appliance already exists, update it if needed
193
+ if appliance[:url] != config.url
194
+ terminal.execute("remote update \"#{config.remote_name}\" --url \"#{escape_arg config.url}\" --insecure")
195
+ end
196
+ else
197
+ # create appliance
198
+ terminal.execute("remote add \"#{config.remote_name}\" \"#{escape_arg config.url}\" --insecure --use -N")
199
+ end
200
+ sleep 1
201
+ # use the remote
202
+ terminal.execute("remote use \"#{config.remote_name}\"")
203
+
204
+ # could skip this and just let the tests fail that require authentication...
205
+ # login right away to make sure credentials work before attempting to run test suite
206
+ # abort if bad credentials
207
+ #login_status_code, login_error = login()
208
+ login_results = terminal.execute(%(login "#{escape_arg config.username}" "#{escape_arg config.password_decrypted}"))
209
+ if login_results[0] == 0
210
+ #print_green_success "Established terminal to #{config.username}@#{config.url}\nStarting the test run now"
211
+ else
212
+ print_red_alert "Failed to login as #{config.username}@#{config.url}"
213
+ abort("Test run aborted")
214
+ exit 1
215
+ end
216
+ end
217
+ return Thread.current[:terminal]
218
+ end
219
+
220
+ # use the test remote quietly
221
+ def use_remote()
222
+ config = get_config()
223
+ terminal.execute %(remote use #{config.remote_name} --quiet)
224
+ end
225
+
226
+ alias :remote_use :use_remote
227
+
228
+ # @return [true|false]
229
+ def is_logged_in()
230
+ return get_access_token() != nil
231
+ end
232
+
233
+ # Fetch the access token for the current remote.
234
+ # @return [String,nil]
235
+ def get_access_token()
236
+ config = get_config()
237
+ appliance = Morpheus::Cli::Remote.load_remote(config.remote_name)
238
+ if appliance.nil?
239
+ #abort("test appliance not found #{config.remote_name}")
240
+ return nil
241
+ end
242
+ wallet = ::Morpheus::Cli::Credentials.new(appliance[:name], nil).load_saved_credentials()
243
+ if wallet.nil?
244
+ return nil
245
+ end
246
+ return wallet['access_token']
247
+ end
248
+
249
+ def login_if_needed()
250
+ if !is_logged_in()
251
+ login()
252
+ end
253
+ end
254
+
255
+ def logout_if_needed()
256
+ if is_logged_in()
257
+ logout()
258
+ end
259
+ end
260
+
261
+ # def login()
262
+ # config = get_config()
263
+ # result = terminal.execute(%(login "#{escape_arg config.username}" "#{escape_arg config.password_decrypted}" --quiet))
264
+ # exit_code, err = Morpheus::Cli::CliRegistry.parse_command_result(result)
265
+ # if exit_code != 0
266
+ # abort("Failed to login as #{config.username}@#{config.url}")
267
+ # else
268
+ # # hooray
269
+ # end
270
+ # end
271
+
272
+ def login()
273
+ config = get_config()
274
+ terminal.execute(%(login "#{escape_arg config.username}" "#{escape_arg config.password_decrypted}" --quiet))
275
+ end
276
+
277
+ def logout()
278
+ terminal.execute("logout --quiet")
279
+ end
280
+
281
+ # login to execute block and return to previous logged in/out state
282
+ def with_authentication(&block)
283
+ was_logged_out = !is_logged_in()
284
+ login_if_needed()
285
+ result = block.call()
286
+ logout_if_needed() if was_logged_out
287
+ return result
288
+ end
289
+
290
+ # logout to execute block and return to previous logged in/out state
291
+ def without_authentication(&block)
292
+ was_logged_in = is_logged_in()
293
+ logout_if_needed()
294
+ result = block.call()
295
+ login_if_needed() if was_logged_in
296
+ return result
297
+ end
298
+
299
+ # send input to CLI terminal stdin to handle interactive prompting
300
+ def with_input(*messages, &block)
301
+ with_tmpfile_input(*messages, &block)
302
+ end
303
+
304
+ # send input to CLI shell stdin to handle interactive prompting
305
+ # def with_shell_input(*messages, &block)
306
+ # with_tmpfile_input(*messages, &block)
307
+ # end
308
+
309
+ # Set stdin to a File (Readline requires a File object for input=)
310
+ # this way works for all testing purposes
311
+ def with_tmpfile_input(*messages, &block)
312
+ messages = messages.flatten #.compact
313
+ original_stdin = $stdin
314
+ #todo: use temp directory...
315
+ tmpfilename = ".cli_unit_test_shell_input_#{SecureRandom.hex(10)}.morpheus"
316
+ file = nil
317
+ begin
318
+ File.open(tmpfilename, 'w+') {|f| f.write(messages.join("\n") + "\n") }
319
+ file = File.new(tmpfilename, 'r')
320
+ Readline.input = file
321
+ $stdin = file
322
+ yield
323
+ ensure
324
+ Readline.input = original_stdin
325
+ $stdin = original_stdin
326
+ file.close if file && !file.closed?
327
+ File.delete(tmpfilename) if File.exist?(tmpfilename)
328
+ end
329
+ end
330
+
331
+ # escape double quotes for interpolating values between double quotes in your terminal command arguments
332
+ def escape_arg(value)
333
+ # escape double quotes
334
+ value.to_s.gsub("\"", "\\\"")
335
+ end
336
+
337
+ end
338
+ end
@@ -0,0 +1,137 @@
1
+ # use a different default home directory when running tests
2
+ ENV['MORPHEUS_CLI_HOME'] = ENV['TEST_CLI_HOME'] || File.join(Dir.home, ".morpheus_test") unless ENV['MORPHEUS_CLI_HOME']
3
+ require 'morpheus'
4
+
5
+ module MorpheusTest
6
+
7
+ # TestConfig handles parsing of the Morpheus test config settings
8
+ # This parses environment variables or a config file, the latter takes priority
9
+ # The default config file named +test_config.yaml+
10
+ class TestConfig
11
+ include Morpheus::Cli::PrintHelper # for parse_json_or_yaml() todo: remove this include and use a class method
12
+
13
+ # The default filename for the test config
14
+ DEFAULT_FILENAME = 'test_config.yaml'
15
+
16
+ # The environment variables that are parsed and the corresponding config property name
17
+ ENVIRONMENT_VARIABLES = {
18
+ # "TEST_CLI_HOME" => :homedir,
19
+ "TEST_REMOTE_NAME" => :remote_name,
20
+ "TEST_REMOTE_URL" => :url,
21
+ "TEST_URL" => :url,
22
+ "TEST_USERNAME" => :username,
23
+ "TEST_PASSWORD" => :password,
24
+ "TEST_DEBUG" => :debug,
25
+ "DEBUG" => :debug,
26
+ }.freeze
27
+
28
+ # The default name of the remote to create and do our unit testing in
29
+ DEFAULT_REMOTE_NAME = 'unit_test'
30
+
31
+ # use a different default home directory when running tests
32
+ # load settings from environment variables and config file
33
+ # unless ENV['MORPHEUS_CLI_HOME']
34
+ # ENV['MORPHEUS_CLI_HOME'] = File.join(Dir.home, ".morpheus_test")
35
+ # end
36
+ # The default name of the remote to create and use for unit testing
37
+ DEFAULT_HOMEDIR = Morpheus::Cli.home_directory
38
+
39
+ # raised if the config cannot be parsed or is missing required settings
40
+ class BadConfigError < StandardError
41
+ end
42
+
43
+ attr_accessor :homedir, :remote_name, :url, :username
44
+ attr_writer :password
45
+ attr_reader :debug
46
+
47
+ def initialize
48
+ # set defaults
49
+ @remote_name = DEFAULT_REMOTE_NAME # 'unit_test'
50
+ @homedir = DEFAULT_HOMEDIR # Morpheus::Cli.home_directory
51
+
52
+ # load settings from environment variable and set instance variables
53
+ #ENVIRONMENT_VARIABLES.each { |name, value| instance_variable_set("@#{name}", ENV[name]) if ENV[name] }
54
+ ENVIRONMENT_VARIABLES.each { |name, value| send("#{value}=", ENV[name]) if ENV[name] }
55
+
56
+ # load settings from config file
57
+ config_filename = nil
58
+ if ENV['TEST_CONFIG']
59
+ config_filename = ENV['TEST_CONFIG']
60
+ elsif File.exist?(DEFAULT_FILENAME)
61
+ config_filename = DEFAULT_FILENAME
62
+ elsif File.exist?(File.join(homedir, DEFAULT_FILENAME))
63
+ config_filename = File.join(@homedir, DEFAULT_FILENAME)
64
+ end
65
+ if config_filename
66
+ full_filename = File.expand_path(config_filename)
67
+ file_content = nil
68
+ if File.exist?(full_filename)
69
+ file_content = File.read(full_filename)
70
+ else
71
+ raise "Test config file not found: #{full_filename}"
72
+ end
73
+ parse_result = parse_json_or_yaml(file_content)
74
+ config_map = parse_result[:data]
75
+ if config_map.nil?
76
+ bad_config "Failed to parse test config file '#{config_filename}' as YAML or JSON. Error: #{parse_result[:error]}"
77
+ end
78
+ @homedir = config_map['homedir'] if config_map['homedir']
79
+ @remote_name = config_map['remote_name'] if config_map['remote_name']
80
+ @url = (config_map['url'] || config_map['remote_url']) if (config_map['url'] || config_map['remote_url'])
81
+ @username = config_map['username'] if config_map['username']
82
+ @password = config_map['password'] if config_map['password']
83
+ @debug = config_map['debug'].to_s.strip.downcase == "true" if config_map.key?('debug')
84
+ end
85
+
86
+ # print_h1 "Test Config"
87
+ # print cyan
88
+ # puts "homedir: #{homedir}"
89
+ # puts "remote_name: #{remote_name}"
90
+ # puts "url: #{url}"
91
+ # puts "username: #{username}"
92
+ # puts "password: #{password}"
93
+ # print reset
94
+
95
+ # validate now
96
+ # For now force user to create a test_config.yaml or use environment variables
97
+ if remote_name.to_s.empty?
98
+ bad_config "Unable to execute unit tests without specifying a remote name.\nYou must specify the environment variable TEST_REMOTE_NAME=unit_test\nor in a config file named #{config_filename || 'test_config.yaml'} like this:\n\nremote_name: unit_test\n"
99
+ end
100
+ if url.to_s.empty?
101
+ bad_config "Unable to execute unit tests without specifying a url.\nThis can be specified with the environment variable TEST_URL=https://test-appliance\nor in a config file named #{config_filename || 'test_config.yaml'} like this:\n\nurl: https://test-appliance\n"
102
+ end
103
+ if username.to_s.empty?
104
+ bad_config "Unable to execute unit tests without specifying a username.\nThis can be specified with the environment variable TEST_USERNAME=testrunner\nor in a config file named #{config_filename || 'test_config.yaml'} like this:\n\nusername: testrunner\n"
105
+ end
106
+ if password.to_s.empty?
107
+ bad_config "Unable to execute unit tests without specifying a password.\nThis can be specified with the environment variable TEST_PASSWORD='SecretPassword123$'\nor in a config file named #{config_filename || 'test_config.yaml'} like this:\n\npassword: 'SecretPassword123$'\n"
108
+ end
109
+ # debug is not a terminal method right now, set it this way to enable morpheus debugging right away
110
+ if debug
111
+ Morpheus::Logging.set_log_level(Morpheus::Logging::Logger::DEBUG)
112
+ end
113
+
114
+ # todo: test authentication now...
115
+ # config looks good
116
+ end
117
+
118
+ def password
119
+ @password ? ('*' * 12) : nil
120
+ end
121
+
122
+ def password_decrypted
123
+ @password
124
+ end
125
+
126
+ def debug=(v)
127
+ @debug = v.to_s.strip.downcase == "true"
128
+ end
129
+
130
+ def bad_config(msg)
131
+ #todo: make this work with raise but also don't run every test so just abort for now
132
+ #raise BadConfigError.new(msg)
133
+ print_red_alert msg
134
+ abort("Aborting tests...")
135
+ end
136
+ end
137
+ end