morpheus-cli 5.3.2 → 5.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/accounts_interface.rb +4 -30
  4. data/lib/morpheus/api/api_client.rb +12 -0
  5. data/lib/morpheus/api/instances_interface.rb +18 -5
  6. data/lib/morpheus/api/load_balancer_pools_interface.rb +9 -0
  7. data/lib/morpheus/api/load_balancer_types_interface.rb +9 -0
  8. data/lib/morpheus/api/load_balancer_virtual_servers_interface.rb +9 -0
  9. data/lib/morpheus/api/load_balancers_interface.rb +4 -53
  10. data/lib/morpheus/api/network_routers_interface.rb +56 -0
  11. data/lib/morpheus/api/secondary_read_interface.rb +25 -0
  12. data/lib/morpheus/api/secondary_rest_interface.rb +42 -0
  13. data/lib/morpheus/api/virtual_images_interface.rb +23 -2
  14. data/lib/morpheus/cli/apps.rb +3 -2
  15. data/lib/morpheus/cli/cli_command.rb +21 -14
  16. data/lib/morpheus/cli/cli_registry.rb +55 -2
  17. data/lib/morpheus/cli/cloud_resource_pools_command.rb +169 -133
  18. data/lib/morpheus/cli/clusters.rb +51 -33
  19. data/lib/morpheus/cli/instances.rb +292 -174
  20. data/lib/morpheus/cli/invoices_command.rb +79 -99
  21. data/lib/morpheus/cli/library_cluster_layouts_command.rb +20 -0
  22. data/lib/morpheus/cli/load_balancer_types.rb +37 -0
  23. data/lib/morpheus/cli/load_balancers.rb +149 -314
  24. data/lib/morpheus/cli/log_settings_command.rb +7 -3
  25. data/lib/morpheus/cli/mixins/load_balancers_helper.rb +156 -0
  26. data/lib/morpheus/cli/mixins/print_helper.rb +11 -0
  27. data/lib/morpheus/cli/mixins/provisioning_helper.rb +123 -101
  28. data/lib/morpheus/cli/mixins/rest_command.rb +657 -0
  29. data/lib/morpheus/cli/monitoring_checks_command.rb +2 -0
  30. data/lib/morpheus/cli/network_routers_command.rb +1183 -185
  31. data/lib/morpheus/cli/networks_command.rb +194 -101
  32. data/lib/morpheus/cli/option_parser.rb +25 -17
  33. data/lib/morpheus/cli/option_types.rb +42 -45
  34. data/lib/morpheus/cli/tenants_command.rb +18 -20
  35. data/lib/morpheus/cli/vdi_pools_command.rb +4 -1
  36. data/lib/morpheus/cli/version.rb +1 -1
  37. data/lib/morpheus/cli/virtual_images.rb +249 -29
  38. data/lib/morpheus/cli.rb +1 -0
  39. data/lib/morpheus/ext/string.rb +41 -0
  40. data/lib/morpheus/formatters.rb +4 -0
  41. data/morpheus-cli.gemspec +1 -1
  42. metadata +13 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e776b0e0602c4de27079e0796d270009b0d3a592d571a41721ac936c7537b94e
4
- data.tar.gz: 10f231de3b75dc6a16d5433af35a1cbde2931d1871bf2106040ee49a0812091c
3
+ metadata.gz: ca7c939828b8f72ee86c663a19d01743a56d5c6c209cecc5e17b44046caeb484
4
+ data.tar.gz: e13fccbf1a45e8980e818e4cc1f0dba98d65e405c79f5d90e13c33341ad2e514
5
5
  SHA512:
6
- metadata.gz: 893fd73648238a2a360273fef6a44654b0011075401743f6be61ba5f32c487f8bc5c5936f440cbcbb6be99ce05888ef99e453545e7f435e834a54ade3062851a
7
- data.tar.gz: c558b3e31997546f382b44339183431e6d855be77b82e6c85be623c32dcf3333ae0a34c725b19fb317e308d8ec3ca2963de9f51ceba5b3bd658c7ad013ae9d67
6
+ metadata.gz: 5965b1af85499cadcae249f3109b6fd0ea4bee6af38192196fd6091bdcdb452f75c1f4025f372ec3d5f47ab1531e04acbf5fb2ed17848ba023087c309fdd06ee
7
+ data.tar.gz: 56bc2124de4e6b28c0c4484b5fa40beef19287708edd98ee1fdddbe334bc7854b853153209d1531793502aa8034d1c8ccf9fca643ec13aebf3eb8353cdd1f72e
data/Dockerfile CHANGED
@@ -1,5 +1,5 @@
1
1
  FROM ruby:2.5.1
2
2
 
3
- RUN gem install morpheus-cli -v 5.3.2
3
+ RUN gem install morpheus-cli -v 5.3.3
4
4
 
5
5
  ENTRYPOINT ["morpheus"]
@@ -1,35 +1,9 @@
1
- require 'morpheus/api/api_client'
1
+ require 'morpheus/api/rest_interface'
2
2
 
3
- class Morpheus::AccountsInterface < Morpheus::APIClient
4
-
5
- def get(id)
6
- raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
7
- url = "#{@base_url}/api/accounts/#{id}"
8
- headers = { params: {}, authorization: "Bearer #{@access_token}" }
9
- execute(method: :get, url: url, headers: headers)
10
- end
11
-
12
- def list(params={})
13
- url = "#{@base_url}/api/accounts"
14
- headers = { params: params, authorization: "Bearer #{@access_token}" }
15
- execute(method: :get, url: url, headers: headers)
16
- end
3
+ class Morpheus::AccountsInterface < Morpheus::RestInterface
17
4
 
18
- def create(payload)
19
- url = "#{@base_url}/api/accounts"
20
- headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
21
- execute(method: :post, url: url, headers: headers, payload: payload.to_json)
5
+ def base_path
6
+ "/api/accounts"
22
7
  end
23
8
 
24
- def update(id, payload)
25
- url = "#{@base_url}/api/accounts/#{id}"
26
- headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
27
- execute(method: :put, url: url, headers: headers, payload: payload.to_json)
28
- end
29
-
30
- def destroy(id)
31
- url = "#{@base_url}/api/accounts/#{id}"
32
- headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
33
- execute(method: :delete, url: url, headers: headers)
34
- end
35
9
  end
@@ -474,6 +474,18 @@ class Morpheus::APIClient
474
474
  Morpheus::LoadBalancersInterface.new(common_interface_options).setopts(@options)
475
475
  end
476
476
 
477
+ def load_balancer_types
478
+ Morpheus::LoadBalancerTypesInterface.new(common_interface_options).setopts(@options)
479
+ end
480
+
481
+ def load_balancer_virtual_servers
482
+ Morpheus::LoadBalancerVirtualServersInterface.new(common_interface_options).setopts(@options)
483
+ end
484
+
485
+ def load_balancer_pools
486
+ Morpheus::LoadBalancerPoolsInterface.new(common_interface_options).setopts(@options)
487
+ end
488
+
477
489
  def tasks
478
490
  Morpheus::TasksInterface.new(common_interface_options).setopts(@options)
479
491
  end
@@ -57,11 +57,24 @@ class Morpheus::InstancesInterface < Morpheus::APIClient
57
57
  execute(opts)
58
58
  end
59
59
 
60
- def cancel_removal(id, params = {})
61
- url = "#{@base_url}/api/instances/#{id}/cancel-removal"
62
- headers = {:params => params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
63
- opts = {method: :put, url: url, headers: headers}
64
- execute(opts)
60
+ def extend_shutdown(id, params={}, payload={}, headers={})
61
+ execute(method: :put, url: "#{base_path}/#{id}/extend-shutdown", params: params, payload: payload, headers: headers)
62
+ end
63
+
64
+ def extend_expiration(id, params={}, payload={}, headers={})
65
+ execute(method: :put, url: "#{base_path}/#{id}/extend-expiration", params: params, payload: payload, headers: headers)
66
+ end
67
+
68
+ def cancel_shutdown(id, params={}, payload={}, headers={})
69
+ execute(method: :put, url: "#{base_path}/#{id}/cancel-shutdown", params: params, payload: payload, headers: headers)
70
+ end
71
+
72
+ def cancel_expiration(id, params={}, payload={}, headers={})
73
+ execute(method: :put, url: "#{base_path}/#{id}/cancel-expiration", params: params, payload: payload, headers: headers)
74
+ end
75
+
76
+ def cancel_removal(id, params={}, payload={}, headers={})
77
+ execute(method: :put, url: "#{base_path}/#{id}/cancel-removal", params: params, payload: payload, headers: headers)
65
78
  end
66
79
 
67
80
  def stop(id, params={})
@@ -0,0 +1,9 @@
1
+ require 'morpheus/api/secondary_rest_interface'
2
+
3
+ class Morpheus::LoadBalancerPoolsInterface < Morpheus::SecondaryRestInterface
4
+
5
+ def base_path(load_balancer_id)
6
+ "/api/load-balancers/#{load_balancer_id}/pools"
7
+ end
8
+
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'morpheus/api/read_interface'
2
+
3
+ class Morpheus::LoadBalancerTypesInterface < Morpheus::ReadInterface
4
+
5
+ def base_path
6
+ "/api/load-balancer-types"
7
+ end
8
+
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'morpheus/api/secondary_rest_interface'
2
+
3
+ class Morpheus::LoadBalancerVirtualServersInterface < Morpheus::SecondaryRestInterface
4
+
5
+ def base_path(load_balancer_id)
6
+ "/api/load-balancers/#{load_balancer_id}/virtual-servers"
7
+ end
8
+
9
+ end
@@ -1,58 +1,9 @@
1
- require 'morpheus/api/api_client'
1
+ require 'morpheus/api/rest_interface'
2
2
 
3
- class Morpheus::LoadBalancersInterface < Morpheus::APIClient
3
+ class Morpheus::LoadBalancersInterface < Morpheus::RestInterface
4
4
 
5
- def load_balancer_types(options={})
6
- url = "#{@base_url}/api/load-balancer-types"
7
- headers = { params: {}, authorization: "Bearer #{@access_token}" }
8
- if options.is_a?(Hash)
9
- headers[:params].merge!(options)
10
- elsif options.is_a?(Numeric)
11
- url = "#{@base_url}/api/load-balancer-types/#{options}"
12
- elsif options.is_a?(String)
13
- headers[:params]['name'] = options
14
- end
15
- execute(method: :get, url: url, headers: headers)
5
+ def base_path
6
+ "/api/load-balancers"
16
7
  end
17
8
 
18
- def list(params={})
19
- url = "#{@base_url}/api/load-balancers"
20
- headers = { params: params, authorization: "Bearer #{@access_token}" }
21
- opts = {method: :get, url: url, headers: headers}
22
- execute(opts)
23
- end
24
-
25
- def get(options=nil)
26
- url = "#{@base_url}/api/load-balancers"
27
- headers = { params: {}, authorization: "Bearer #{@access_token}" }
28
- if options.is_a?(Hash)
29
- headers[:params].merge!(options)
30
- elsif options.is_a?(Numeric)
31
- url = "#{@base_url}/api/load-balancers/#{options}"
32
- elsif options.is_a?(String)
33
- headers[:params]['name'] = options
34
- end
35
- execute(method: :get, url: url, headers: headers)
36
- end
37
-
38
- def update(id, options)
39
- url = "#{@base_url}/api/load-balancers/#{id}"
40
- headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
41
- payload = options
42
- execute(method: :put, url: url, headers: headers, payload: payload.to_json)
43
- end
44
-
45
-
46
- def create(options)
47
- url = "#{@base_url}/api/load-balancers"
48
- headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
49
- payload = options
50
- execute(method: :post, url: url, headers: headers, payload: payload.to_json)
51
- end
52
-
53
- def destroy(id)
54
- url = "#{@base_url}/api/load-balancers/#{id}"
55
- headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
56
- execute(method: :delete, url: url, headers: headers)
57
- end
58
9
  end
@@ -59,6 +59,34 @@ class Morpheus::NetworkRoutersInterface < Morpheus::APIClient
59
59
  execute(opts)
60
60
  end
61
61
 
62
+ def list_firewall_rule_groups(router_id, params={})
63
+ url = "#{@base_url}/api/networks/routers/#{router_id}/firewall-rule-groups"
64
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json', params: params }
65
+ opts = {method: :get, url: url, headers: headers}
66
+ execute(opts)
67
+ end
68
+
69
+ def create_firewall_rule_group(router_id, payload={})
70
+ url = "#{@base_url}/api/networks/routers/#{router_id}/firewall-rule-groups"
71
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
72
+ opts = {method: :post, url: url, headers: headers, payload: payload.to_json}
73
+ execute(opts)
74
+ end
75
+
76
+ def update_firewall_rule_group(router_id, group_id, payload={})
77
+ url = "#{@base_url}/api/networks/routers/#{router_id}/firewall-rule-groups/#{group_id}"
78
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
79
+ opts = {method: :put, url: url, headers: headers, payload: payload.to_json}
80
+ execute(opts)
81
+ end
82
+
83
+ def destroy_firewall_rule_group(router_id, group_id, payload={})
84
+ url = "#{@base_url}/api/networks/routers/#{router_id}/firewall-rule-groups/#{group_id}"
85
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
86
+ opts = {method: :delete, url: url, headers: headers, payload: payload.to_json}
87
+ execute(opts)
88
+ end
89
+
62
90
  def create_firewall_rule(router_id, payload={})
63
91
  url = "#{@base_url}/api/networks/routers/#{router_id}/firewall-rules"
64
92
  headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
@@ -66,6 +94,13 @@ class Morpheus::NetworkRoutersInterface < Morpheus::APIClient
66
94
  execute(opts)
67
95
  end
68
96
 
97
+ def update_firewall_rule(router_id, rule_id, payload={})
98
+ url = "#{@base_url}/api/networks/routers/#{router_id}/firewall-rules/#{rule_id}"
99
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
100
+ opts = {method: :put, url: url, headers: headers, payload: payload.to_json}
101
+ execute(opts)
102
+ end
103
+
69
104
  def destroy_firewall_rule(router_id, rule_id, payload={})
70
105
  url = "#{@base_url}/api/networks/routers/#{router_id}/firewall-rules/#{rule_id}"
71
106
  headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
@@ -87,6 +122,27 @@ class Morpheus::NetworkRoutersInterface < Morpheus::APIClient
87
122
  execute(opts)
88
123
  end
89
124
 
125
+ def create_nat(router_id, payload={})
126
+ url = "#{@base_url}/api/networks/routers/#{router_id}/nats"
127
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
128
+ opts = {method: :post, url: url, headers: headers, payload: payload.to_json}
129
+ execute(opts)
130
+ end
131
+
132
+ def update_nat(router_id, nat_id, payload={})
133
+ url = "#{@base_url}/api/networks/routers/#{router_id}/nats/#{nat_id}"
134
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
135
+ opts = {method: :put, url: url, headers: headers, payload: payload.to_json}
136
+ execute(opts)
137
+ end
138
+
139
+ def destroy_nat(router_id, nat_id, payload={})
140
+ url = "#{@base_url}/api/networks/routers/#{router_id}/nats/#{nat_id}"
141
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
142
+ opts = {method: :delete, url: url, headers: headers, payload: payload.to_json}
143
+ execute(opts)
144
+ end
145
+
90
146
  def update_permissions(router_id, payload)
91
147
  url = "#{@base_url}/api/networks/routers/#{router_id}/permissions"
92
148
  headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
@@ -0,0 +1,25 @@
1
+ require 'morpheus/api/api_client'
2
+
3
+ # Interface class to be subclassed by interfaces that provide CRUD endpoints
4
+ # for objects underneath another resource
5
+ # Subclasses must override the base_path(resource_id) method
6
+ class Morpheus::SecondaryReadInterface < Morpheus::APIClient
7
+
8
+ # subclasses should override in your interface
9
+ # Example: "/api/things/#{resource_id}/widgets"
10
+ def base_path(resource_id)
11
+ raise "#{self.class} has not defined base_path(resource_id)!"
12
+ end
13
+
14
+ def list(resource_id, params={}, headers={})
15
+ validate_id!(resource_id)
16
+ execute(method: :get, url: "#{base_path(resource_id)}", params: params, headers: headers)
17
+ end
18
+
19
+ def get(resource_id, id, params={}, headers={})
20
+ validate_id!(resource_id)
21
+ validate_id!(id)
22
+ execute(method: :get, url: "#{base_path(resource_id)}/#{id}", params: params, headers: headers)
23
+ end
24
+
25
+ end
@@ -0,0 +1,42 @@
1
+ require 'morpheus/api/api_client'
2
+
3
+ # Interface class to be subclassed by interfaces that provide CRUD endpoints
4
+ # for objects underneath another resource
5
+ # Subclasses must override the base_path(resource_id) method
6
+ class Morpheus::SecondaryRestInterface < Morpheus::APIClient
7
+
8
+ # subclasses should override in your interface
9
+ # Example: "/api/things/#{resource_id}/widgets"
10
+ def base_path(resource_id)
11
+ raise "#{self.class} has not defined base_path(resource_id)!"
12
+ end
13
+
14
+ def list(resource_id, params={}, headers={})
15
+ validate_id!(resource_id)
16
+ execute(method: :get, url: "#{base_path(resource_id)}", params: params, headers: headers)
17
+ end
18
+
19
+ def get(resource_id, id, params={}, headers={})
20
+ validate_id!(resource_id)
21
+ validate_id!(id)
22
+ execute(method: :get, url: "#{base_path(resource_id)}/#{id}", params: params, headers: headers)
23
+ end
24
+
25
+ def create(resource_id, payload, params={}, headers={})
26
+ validate_id!(resource_id)
27
+ execute(method: :post, url: "#{base_path(resource_id)}", params: params, payload: payload, headers: headers)
28
+ end
29
+
30
+ def update(resource_id, id, payload, params={}, headers={})
31
+ validate_id!(resource_id)
32
+ validate_id!(id)
33
+ execute(method: :put, url: "#{base_path(resource_id)}/#{id}", params: params, payload: payload, headers: headers)
34
+ end
35
+
36
+ def destroy(resource_id, id, params = {}, headers={})
37
+ validate_id!(resource_id)
38
+ validate_id!(id)
39
+ execute(method: :delete, url: "#{base_path(resource_id)}/#{id}", params: params, headers: headers)
40
+ end
41
+
42
+ end
@@ -45,10 +45,10 @@ class Morpheus::VirtualImagesInterface < Morpheus::APIClient
45
45
  execute(method: :put, url: url, headers: headers, payload: payload.to_json)
46
46
  end
47
47
 
48
- def destroy(id)
48
+ def destroy(id, params={})
49
49
  url = "#{@base_url}/api/virtual-images/#{id}"
50
50
  headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
51
- execute(method: :delete, url: url, headers: headers)
51
+ execute(method: :delete, url: url, params: params, headers: headers)
52
52
  end
53
53
 
54
54
  # multipart file upload
@@ -165,4 +165,25 @@ class Morpheus::VirtualImagesInterface < Morpheus::APIClient
165
165
  execute(method: :delete, url: url, headers: headers)
166
166
  end
167
167
 
168
+ def location_base_path(resource_id)
169
+ "/api/virtual-images/#{resource_id}/locations"
170
+ end
171
+
172
+ def list_locations(resource_id, params={}, headers={})
173
+ validate_id!(resource_id)
174
+ execute(method: :get, url: "#{location_base_path(resource_id)}", params: params, headers: headers)
175
+ end
176
+
177
+ def get_location(resource_id, id, params={}, headers={})
178
+ validate_id!(resource_id)
179
+ validate_id!(id)
180
+ execute(method: :get, url: "#{location_base_path(resource_id)}/#{id}", params: params, headers: headers)
181
+ end
182
+
183
+ def destroy_location(resource_id, id, params = {}, headers={})
184
+ validate_id!(resource_id)
185
+ validate_id!(id)
186
+ execute(method: :delete, url: "#{location_base_path(resource_id)}/#{id}", params: params, headers: headers)
187
+ end
188
+
168
189
  end
@@ -438,7 +438,7 @@ class Morpheus::Cli::Apps
438
438
  instance_prompt_options[:default_cloud] = cloud ? cloud['name'] : nil
439
439
  instance_prompt_options[:environment] = selected_environment ? selected_environment['code'] : nil
440
440
  instance_prompt_options[:default_security_groups] = scoped_instance_config['securityGroups'] ? scoped_instance_config['securityGroups'] : nil
441
-
441
+
442
442
  instance_prompt_options[:no_prompt] = options[:no_prompt]
443
443
  #instance_prompt_options[:always_prompt] = options[:no_prompt] != true # options[:always_prompt]
444
444
  instance_prompt_options[:options] = scoped_instance_config # meh, actually need to make these default values instead..
@@ -460,8 +460,9 @@ class Morpheus::Cli::Apps
460
460
  instance_prompt_options[:locked_fields] = scoped_instance_config['lockedFields']
461
461
  instance_prompt_options[:for_app] = true
462
462
  # this provisioning helper method handles all (most) of the parsing and prompting
463
+ scoped_instance_config = Marshal.load( Marshal.dump(scoped_instance_config) )
463
464
  instance_config_payload = prompt_new_instance(instance_prompt_options)
464
-
465
+
465
466
  # strip all empty string and nil
466
467
  instance_config_payload.deep_compact!
467
468
  # use the blueprint config as the base
@@ -18,11 +18,11 @@ module Morpheus
18
18
  # todo: use delegate
19
19
  module CliCommand
20
20
 
21
- def self.included(klass)
22
- klass.send :include, Morpheus::Cli::PrintHelper
23
- klass.send :include, Morpheus::Benchmarking::HasBenchmarking
24
- klass.extend ClassMethods
25
- Morpheus::Cli::CliRegistry.add(klass, klass.command_name)
21
+ def self.included(base)
22
+ base.send :include, Morpheus::Cli::PrintHelper
23
+ base.send :include, Morpheus::Benchmarking::HasBenchmarking
24
+ base.extend ClassMethods
25
+ Morpheus::Cli::CliRegistry.add(base, base.command_name)
26
26
  end
27
27
 
28
28
  # the beginning of instance variables from optparse !
@@ -983,10 +983,11 @@ module Morpheus
983
983
  out = ""
984
984
  out << usage.to_s.strip if usage
985
985
  out << "\n"
986
- if !subcommands.empty?
986
+ my_subcommands = visible_subcommands
987
+ if !my_subcommands.empty?
987
988
  out << "Commands:"
988
989
  out << "\n"
989
- subcommands.sort.each {|subcmd, method|
990
+ my_subcommands.sort.each {|subcmd, method|
990
991
  desc = get_subcommand_description(subcmd)
991
992
  out << "\t#{subcmd.to_s}"
992
993
  out << "\t#{desc}" if desc
@@ -1201,11 +1202,18 @@ module Morpheus
1201
1202
  # this could go be done in optparse.parse instead perhaps
1202
1203
  def verify_args!(opts={})
1203
1204
  args = opts[:args] || []
1204
- if opts[:count]
1205
- if args.count < opts[:count]
1206
- raise_args_error("not enough arguments, expected #{opts[:count]} and got #{args.count == 0 ? '0' : args.count.to_s + ': '}#{args.join(', ')}", args, opts[:optparse])
1207
- elsif args.count > opts[:count]
1208
- raise_args_error("too many arguments, expected #{opts[:count]} and got #{args.count == 0 ? '0' : args.count.to_s + ': '}#{args.join(', ')}", args, opts[:optparse])
1205
+ count = opts[:count]
1206
+ # simplify output for verify_args!(min:2, max:2) or verify_args!(max:0)
1207
+ if opts[:min] && opts[:max] && opts[:min] == opts[:max]
1208
+ count = opts[:min]
1209
+ elsif opts[:max] == 0
1210
+ count = 0
1211
+ end
1212
+ if count
1213
+ if args.count < count
1214
+ raise_args_error("not enough arguments, expected #{count} and got #{args.count == 0 ? '0' : args.count.to_s + ': '}#{args.join(', ')}", args, opts[:optparse])
1215
+ elsif args.count > count
1216
+ raise_args_error("too many arguments, expected #{count} and got #{args.count == 0 ? '0' : args.count.to_s + ': '}#{args.join(', ')}", args, opts[:optparse])
1209
1217
  end
1210
1218
  else
1211
1219
  if opts[:min]
@@ -1527,8 +1535,7 @@ module Morpheus
1527
1535
  cmds = subcommands.clone
1528
1536
  if @hidden_subcommands && !@hidden_subcommands.empty?
1529
1537
  @hidden_subcommands.each do |hidden_cmd|
1530
- cmds.delete(hidden_cmd.to_s)
1531
- cmds.delete(hidden_cmd.to_sym)
1538
+ cmds.delete(hidden_cmd.to_s.gsub('_', '-'))
1532
1539
  end
1533
1540
  end
1534
1541
  cmds