morpheus-cli 3.3.2.5 → 3.3.2.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 43f88e813f909c4b50f7b18219e0a3a4b298cc44
4
- data.tar.gz: c5f23f5767cb4d60bae998e21f82abf78d452239
3
+ metadata.gz: a3638fa392f4c3276cb8f7dcfcde61cde875325a
4
+ data.tar.gz: 00c82492c99d9cedd9b0c9920bdace3d7ed8ed3c
5
5
  SHA512:
6
- metadata.gz: a012937d6dc02d54e8b204b34b1ae98297017c66c70df6c114be7ccd98e49282202ca379ad7b6c2e4efa677a051dfe4a3fbd62d744d19e38ff5ed385b365f479
7
- data.tar.gz: ca965fcec4b654400a983975e6905275f12c2ef973932d0ec1ce76bb9126868f4970f3beac3a503c369c735746cf80c154e18ef7bb6ef1f0b06d33b6d6b90e69
6
+ metadata.gz: a45a54a113a42d63ad41177a236ab9e5997937b29553464b0f607d7d8a7b5fc990b724901432b028d227ddf15de0a3d32f4f833a2f1b36c398d6f44f8395541c
7
+ data.tar.gz: 59710c0c7075f01223d6a02f4594af4978860c8cf390cb850f8d63eb2c800c5d9ab5d646e1895de30bdbec33c7b3010ea6ea5990fb42e7ee7fa202ebf22d81d7
data/lib/morpheus/cli.rb CHANGED
@@ -53,6 +53,7 @@ module Morpheus
53
53
 
54
54
  # utilites
55
55
  require 'morpheus/cli/cli_registry.rb'
56
+ require 'morpheus/cli/expression_parser.rb'
56
57
  require 'morpheus/cli/dot_file.rb'
57
58
  require 'morpheus/cli/command_error'
58
59
 
@@ -60,6 +61,9 @@ module Morpheus
60
61
  load 'morpheus/cli/option_types.rb'
61
62
  load 'morpheus/cli/credentials.rb'
62
63
 
64
+ # all standard commands
65
+ Dir[File.dirname(__FILE__) + "/cli/commands/standard/**/*.rb"].each {|file| load file }
66
+
63
67
  # shell scripting commands
64
68
  load 'morpheus/cli/source_command.rb'
65
69
  load 'morpheus/cli/echo_command.rb'
@@ -177,8 +177,15 @@ class Morpheus::Cli::Clouds
177
177
  server_counts = json_response['serverCounts'] # legacy
178
178
  end
179
179
  if options[:json]
180
- print JSON.pretty_generate(json_response), "\n"
181
- return
180
+ puts as_json(json_response, options, 'zone')
181
+ return 0
182
+ elsif options[:yaml]
183
+ puts as_yaml(json_response, options, 'zone')
184
+ return 0
185
+ end
186
+ if options[:csv]
187
+ puts records_as_csv([json_response['zone']], options)
188
+ return 0
182
189
  end
183
190
  cloud_type = cloud_type_for_id(cloud['zoneTypeId'])
184
191
  print_h1 "Cloud Details"
@@ -0,0 +1,36 @@
1
+ require 'morpheus/cli/cli_command'
2
+ require 'term/ansicolor'
3
+ require 'json'
4
+
5
+ # Utility command for exiting a shell
6
+ class Morpheus::Cli::ExitCommand
7
+ include Morpheus::Cli::CliCommand
8
+
9
+ set_command_name :'exit'
10
+
11
+ set_command_hidden
12
+
13
+ def handle(args)
14
+ exit_code = 0
15
+ options = {}
16
+ filename = Morpheus::Cli::DotFile.morpheus_profile_filename
17
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
18
+ opts.banner = "Usage: exit [code]"
19
+ build_common_options(opts, options, [])
20
+ opts.footer = "Exit morpheus . Default is 0"
21
+ end
22
+ optparse.parse!(args)
23
+
24
+ if args[0]
25
+ exit_code = args[0].to_i
26
+ end
27
+
28
+ if options[:debug]
29
+ puts "#{dark}exit #{exit_code}#{reset}"
30
+ end
31
+
32
+ exit exit_code
33
+
34
+ end
35
+
36
+ end
@@ -5,6 +5,7 @@ require 'rest_client'
5
5
  require 'net/https'
6
6
  require 'morpheus/logging'
7
7
  require 'morpheus/cli/command_error'
8
+ require 'morpheus/cli/expression_parser'
8
9
 
9
10
  class Morpheus::Cli::ErrorHandler
10
11
  include Term::ANSIColor
@@ -35,6 +36,11 @@ class Morpheus::Cli::ErrorHandler
35
36
  puts_angry_error err.message
36
37
  do_print_stacktrace = false
37
38
  # @stderr.puts "Try -h for help with this command."
39
+ when Morpheus::Cli::ExpressionParser::InvalidExpression
40
+ # @stderr.puts "#{red}#{err.message}#{reset}"
41
+ puts_angry_error err.message
42
+ do_print_stacktrace = false
43
+ exit_code = 99
38
44
  when SocketError
39
45
  @stderr.puts "#{red}Error Communicating with the Appliance.#{reset}"
40
46
  @stderr.puts "#{red}#{err.message}#{reset}"
@@ -0,0 +1,149 @@
1
+ require 'shellwords'
2
+
3
+ # Provides parsing of user input into an Array of expressions for execution
4
+ # Syntax currently supports the && and || operators, and the use of parenthesis
5
+ # returns an Array of objects.
6
+ # Each object might be a command (String), an operator (well known String)
7
+ # or another expression (Array)
8
+ module Morpheus::Cli::ExpressionParser
9
+
10
+ # An error class for invalid expressions
11
+ class InvalidExpression < StandardError
12
+ end
13
+
14
+ ## constants used internally for parsing morpheus expressions
15
+ # note: the space padding in the _TOKEN strings is important for splitting
16
+
17
+ OPEN_PARENTHESIS = "("
18
+ OPEN_PARENTHESIS_TOKEN = "___+++OPEN_PARENTHESIS+++___"
19
+ OPEN_PARENTHESIS_REGEX = /(\()(?=(?:[^"']|"[^'"]*")*$)/
20
+
21
+ CLOSED_PARENTHESIS = ")"
22
+ CLOSED_PARENTHESIS_TOKEN = "___+++CLOSED_PARENTHESIS+++___"
23
+ CLOSED_PARENTHESIS_REGEX = /(\))(?=(?:[^"']|"[^'"]*")*$)/
24
+
25
+ AND_OPERATOR = "&&"
26
+ AND_OPERATOR_TOKEN = "___+++AND_OPERATOR+++___"
27
+ AND_OPERATOR_REGEX = /(\&\&)(?=(?:[^"']|"[^'"]*")*$)/
28
+
29
+ OR_OPERATOR = "||"
30
+ OR_OPERATOR_TOKEN = "___+++OR_OPERATOR+++___"
31
+ OR_OPERATOR_REGEX = /(\|\|)(?=(?:[^"']|"[^'"]*")*$)/
32
+
33
+ PIPE_OPERATOR = "|"
34
+ PIPE_OPERATOR_TOKEN = "___+++PIPE_OPERATOR+++___"
35
+ PIPE_OPERATOR_REGEX = /(\|)(?=(?:[^"']|"[^'"]*")*$)/
36
+
37
+ COMMAND_DELIMETER = ";"
38
+ COMMAND_DELIMETER_REGEX = /(\;)(?=(?:[^"']|"[^'"]*")*$)/
39
+ COMMAND_DELIMETER_TOKEN = "___+++COMMAND_DELIMETER+++___"
40
+
41
+ # parse an expression of morpheus commands into a list of expressions
42
+ def self.parse(input)
43
+ result = []
44
+ # first, build up a temporary command string
45
+ # swap in well known tokens so we can split it safely
46
+ expression_str = input.dup.to_s
47
+ expression_str.gsub!(OPEN_PARENTHESIS_REGEX, " #{OPEN_PARENTHESIS_TOKEN} ")
48
+ expression_str.gsub!(CLOSED_PARENTHESIS_REGEX, " #{CLOSED_PARENTHESIS_TOKEN} ")
49
+ expression_str.gsub!(AND_OPERATOR_REGEX, " #{AND_OPERATOR_TOKEN} ")
50
+ expression_str.gsub!(OR_OPERATOR_REGEX, " #{OR_OPERATOR_TOKEN} ")
51
+ expression_str.gsub!(PIPE_OPERATOR_REGEX, " #{PIPE_OPERATOR_TOKEN} ")
52
+ expression_str.gsub!(COMMAND_DELIMETER_REGEX, " #{COMMAND_DELIMETER_TOKEN} ")
53
+ # split on unquoted whitespace
54
+ tokens = expression_str.split(/(\s)(?=(?:[^"']|"[^'"]*")*$)/).collect {|it| it.to_s.strip }.select {|it| !it.empty? }.compact
55
+ # swap back for nice looking tokens
56
+ tokens = tokens.map do |t|
57
+ case t
58
+ when OPEN_PARENTHESIS_TOKEN then OPEN_PARENTHESIS
59
+ when CLOSED_PARENTHESIS_TOKEN then CLOSED_PARENTHESIS
60
+ when AND_OPERATOR_TOKEN then AND_OPERATOR
61
+ when OR_OPERATOR_TOKEN then OR_OPERATOR
62
+ when PIPE_OPERATOR_TOKEN then PIPE_OPERATOR
63
+ when COMMAND_DELIMETER_TOKEN then COMMAND_DELIMETER
64
+ else
65
+ t
66
+ end
67
+ end
68
+
69
+ # result = parse_expression_from_tokens(tokens)
70
+ begin
71
+ result = parse_expression_from_tokens(tokens)
72
+ rescue InvalidExpression => ex
73
+ raise InvalidExpression.new("#{ex}. Invalid Expression: #{input}")
74
+ end
75
+ return result
76
+ end
77
+
78
+ # turn a flat list of tokens into an array of expressions
79
+ def self.parse_expression_from_tokens(tokens)
80
+ result = []
81
+ remaining_tokens = tokens.dup
82
+ current_command_tokens = []
83
+ while !remaining_tokens.empty?
84
+ token = remaining_tokens.shift
85
+ if token == CLOSED_PARENTHESIS
86
+ raise InvalidExpression.new("Encountered a closed parenthesis ')' with no matching open parenthesis '('")
87
+ elsif token == OPEN_PARENTHESIS
88
+ # add the command
89
+ if current_command_tokens.size != 0
90
+ result << current_command_tokens.join(" ")
91
+ current_command_tokens = []
92
+ end
93
+ # start of a new sub expression
94
+ # find the index of the matching closed parenthesis
95
+ cur_expression_index = 0
96
+ closed_parenthesis_index = nil
97
+ remaining_tokens.each_with_index do |t, index|
98
+ if t == OPEN_PARENTHESIS
99
+ cur_expression_index += 1
100
+ elsif t == CLOSED_PARENTHESIS
101
+ if cur_expression_index == 0
102
+ closed_parenthesis_index = index
103
+ break
104
+ else
105
+ cur_expression_index -= 1
106
+ end
107
+ end
108
+ if cur_expression_index < 0
109
+ raise InvalidExpression.new("Encountered a closed parenthesis ')' with no matching open parenthesis '('")
110
+ end
111
+ end
112
+ if !closed_parenthesis_index
113
+ raise InvalidExpression.new("Encountered an open parenthesis '(' with no matching closed parenthesis ')'")
114
+ end
115
+ # ok, parse a subexpression for the tokens up that index
116
+ sub_tokens = remaining_tokens[0..closed_parenthesis_index-1]
117
+ result << parse_expression_from_tokens(sub_tokens)
118
+ # continue on parsing the remaining tokens
119
+ remaining_tokens = remaining_tokens[closed_parenthesis_index + 1..remaining_tokens.size - 1]
120
+ elsif token == AND_OPERATOR || token == OR_OPERATOR || token == PIPE_OPERATOR
121
+ # add the command
122
+ if current_command_tokens.size != 0
123
+ result << current_command_tokens.join(" ")
124
+ current_command_tokens = []
125
+ end
126
+ # add the operator
127
+ result << token
128
+ elsif token == COMMAND_DELIMETER
129
+ # add the command
130
+ if current_command_tokens.size != 0
131
+ result << current_command_tokens.join(" ")
132
+ current_command_tokens = []
133
+ end
134
+ else
135
+ # everything else is assumed to be part of a command, inject it
136
+ current_command_tokens << token
137
+ end
138
+ end
139
+
140
+ # add the command
141
+ if current_command_tokens.size != 0
142
+ result << current_command_tokens.join(" ")
143
+ current_command_tokens = []
144
+ end
145
+
146
+ return result
147
+ end
148
+
149
+ end
@@ -925,54 +925,80 @@ class Morpheus::Cli::Hosts
925
925
  end
926
926
 
927
927
  def run_workflow(args)
928
+ params = {}
928
929
  options = {}
929
- optparse = OptionParser.new do|opts|
930
- opts.banner = subcommand_usage("run-workflow", "[name]", "[workflow]")
931
- build_common_options(opts, options, [:json, :dry_run, :quiet, :remote])
930
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
931
+ opts.banner = subcommand_usage("[name] [workflow] [options]")
932
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :quiet, :remote])
932
933
  end
933
934
  optparse.parse!(args)
934
- if args.count < 2
935
- puts optparse
936
- exit 1
935
+ if args.count != 2
936
+ puts_error "#{Morpheus::Terminal.angry_prompt}wrong number of arguments. Expected 2 and received #{args.count} #{args.inspect}\n#{optparse}"
937
+ return 1
937
938
  end
938
939
  connect(options)
940
+
939
941
  host = find_host_by_name_or_id(args[0])
942
+ return 1 if host.nil?
940
943
  workflow = find_workflow_by_name(args[1])
941
- task_types = @tasks_interface.task_types()
942
- editable_options = []
943
- workflow['taskSetTasks'].sort{|a,b| a['taskOrder'] <=> b['taskOrder']}.each do |task_set_task|
944
- task_type_id = task_set_task['task']['taskType']['id']
945
- task_type = task_types['taskTypes'].find{ |current_task_type| current_task_type['id'] == task_type_id}
946
- task_opts = task_type['optionTypes'].select { |otype| otype['editable']}
947
- if !task_opts.nil? && !task_opts.empty?
948
- editable_options += task_opts.collect do |task_opt|
949
- new_task_opt = task_opt.clone
950
- new_task_opt['fieldContext'] = "#{task_set_task['id']}.#{new_task_opt['fieldContext']}"
944
+ return 1 if workflow.nil?
945
+
946
+ # support -O options as arbitrary params
947
+ old_option_options = (options[:options] || {}).reject {|k,v| k.is_a?(Symbol) }
948
+ params.deep_merge!(old_option_options) unless old_option_options.empty?
949
+
950
+ # the payload format is unusual
951
+ # payload example: {"taskSet": {taskSetId": {"taskSetTaskId": {"customOptions": {"dbVersion":"5.6"}}}}}
952
+ payload = nil
953
+ if options[:payload]
954
+ payload = options[:payload]
955
+ else
956
+ payload = {}
957
+ # i guess you must pass an option if there are editable options
958
+ # any option, heh
959
+ task_types = @tasks_interface.task_types()
960
+ editable_options = []
961
+ workflow['taskSetTasks'].sort{|a,b| a['taskOrder'] <=> b['taskOrder']}.each do |task_set_task|
962
+ task_type_id = task_set_task['task']['taskType']['id']
963
+ task_type = task_types['taskTypes'].find{ |current_task_type| current_task_type['id'] == task_type_id}
964
+ task_opts = task_type['optionTypes'].select { |otype| otype['editable']}
965
+ if !task_opts.nil? && !task_opts.empty?
966
+ editable_options += task_opts.collect do |task_opt|
967
+ new_task_opt = task_opt.clone
968
+ new_task_opt['fieldContext'] = "#{task_set_task['id']}.#{new_task_opt['fieldContext']}"
969
+ end
951
970
  end
952
971
  end
972
+ if params.empty? && !editable_options.empty?
973
+ puts optparse
974
+ option_lines = editable_options.collect {|it| "\t-O #{it['fieldContext'] ? (it['fieldContext'] + '.') : ''}#{it['fieldName']}=\"value\"" }.join("\n")
975
+ puts "\nAvailable Options:\n#{option_lines}\n\n"
976
+ return 1
977
+ end
978
+
953
979
  end
954
- params = options[:options] || {}
955
980
 
956
- if params.empty? && !editable_options.empty?
957
- puts optparse
958
- option_lines = editable_options.collect {|it| "\t-O #{it['fieldContext'] ? (it['fieldContext'] + '.') : ''}#{it['fieldName']}=\"value\"" }.join("\n")
959
- puts "\nAvailable Options:\n#{option_lines}\n\n"
960
- exit 1
981
+ if !params.empty?
982
+ payload['taskSet'] ||= {}
983
+ payload['taskSet']["#{workflow['id']}"] ||= {}
984
+ payload['taskSet']["#{workflow['id']}"].deep_merge!(params)
961
985
  end
962
986
 
963
- workflow_payload = {taskSet: {"#{workflow['id']}" => params }}
964
987
  begin
965
988
  if options[:dry_run]
966
- print_dry_run @servers_interface.dry.workflow(host['id'],workflow['id'], workflow_payload)
989
+ print_dry_run @servers_interface.dry.workflow(host['id'],workflow['id'], payload)
967
990
  return
968
991
  end
969
- json_response = @servers_interface.workflow(host['id'],workflow['id'], workflow_payload)
992
+ json_response = @servers_interface.workflow(host['id'],workflow['id'], payload)
970
993
  if options[:json]
971
- print JSON.pretty_generate(json_response)
972
- print "\n"
973
- elsif !options[:quiet]
974
- puts "Workflow #{workflow['name']} is running..."
994
+ print as_json(json_response, options), "\n"
995
+ return
996
+ elsif options[:quiet]
997
+ return 0
998
+ else
999
+ print_green_success "Running workflow #{workflow['name']} on host #{host['name']} ..."
975
1000
  end
1001
+ return 0
976
1002
  rescue RestClient::Exception => e
977
1003
  print_rest_exception(e, options)
978
1004
  exit 1
@@ -1872,9 +1872,9 @@ class Morpheus::Cli::Instances
1872
1872
  build_common_options(opts, options, [:options, :json, :dry_run, :remote])
1873
1873
  end
1874
1874
  optparse.parse!(args)
1875
- if args.count < 2
1876
- puts "\n#{optparse}\n\n"
1877
- exit 1
1875
+ if args.count != 2
1876
+ puts_error "#{Morpheus::Terminal.angry_prompt}wrong number of arguments. Expected 2 and received #{args.count} #{args.inspect}\n#{optparse}"
1877
+ return 1
1878
1878
  end
1879
1879
  connect(options)
1880
1880
  instance = find_instance_by_name_or_id(args[0])
@@ -8,6 +8,7 @@ require 'logger'
8
8
  require 'fileutils'
9
9
  require 'morpheus/cli/cli_command'
10
10
  require 'morpheus/cli/error_handler'
11
+ require 'morpheus/cli/expression_parser'
11
12
  require 'morpheus/terminal'
12
13
 
13
14
  #class Morpheus::Cli::Shell < Morpheus::Terminal
@@ -196,7 +197,7 @@ class Morpheus::Cli::Shell
196
197
  result = nil
197
198
  if @execute_mode_command
198
199
  # execute a single command and exit
199
- result = execute_commands(@execute_mode_command)
200
+ result = execute(@execute_mode_command)
200
201
  else
201
202
  # interactive prompt
202
203
  result = 0
@@ -209,7 +210,7 @@ class Morpheus::Cli::Shell
209
210
  input = Readline.readline(@calculated_prompt, true).to_s
210
211
  input = input.strip
211
212
 
212
- result = execute_commands(input)
213
+ result = execute(input)
213
214
  end
214
215
  end
215
216
 
@@ -256,117 +257,84 @@ class Morpheus::Cli::Shell
256
257
  def execute(input)
257
258
  # args = Shellwords.shellsplit(input)
258
259
  #cmd = args.shift
259
- execute_commands(input)
260
+ #execute_commands(input)
261
+ result = execute_commands_as_expression(input)
262
+ # skip logging of exit and !cmd
263
+ unless input.strip.empty? || (["exit", "history"].include?(input.strip)) || input.strip[0].to_s.chr == "!"
264
+ log_history_command(input.strip)
265
+ end
266
+ return result
260
267
  end
261
268
 
262
- def execute_commands(input)
263
- # input = input.to_s.sub(/^morpheus\s+/, "") # meh
264
- # split the command on unquoted semicolons.
265
- # so you can run multiple commands at once! eg hosts list; instances list
266
- # all_commands = input.gsub(/(\;)(?=(?:[^"]|"[^"]*")*$)/, '__CMDDELIM__').split('__CMDDELIM__').collect {|it| it.to_s.strip }.select {|it| !it.empty? }.compact
267
- all_commands = input.gsub(/(\;)(?=(?:[^"']|"[^'"]*")*$)/, '__CMDDELIM__').split('__CMDDELIM__').collect {|it| it.to_s.strip }.select {|it| !it.empty? }.compact
268
- #puts "executing #{all_commands.size} commands: #{all_commands}"
269
+ def execute_commands_as_expression(input)
270
+ flow = input
271
+ if input.is_a?(String)
272
+ begin
273
+ flow = Morpheus::Cli::ExpressionParser.parse(input)
274
+ rescue Morpheus::Cli::ExpressionParser::InvalidExpression => e
275
+ @history_logger.error "#{e.message}" if @history_logger
276
+ return Morpheus::Cli::ErrorHandler.new(my_terminal.stderr).handle_error(e) # lol
277
+ end
278
+ end
269
279
  final_command_result = nil
270
- all_commands.each do |cmd|
271
- flow = parse_commands_and_operators(cmd)
272
- if flow.size > 1
273
- last_command_result = nil
274
- if ['&&','||', '|'].include?(flow.first)
275
- puts_error "invalid command format, begins with an operator: #{cmd}"
276
- return 9
277
- elsif ['&&','||', '|'].include?(flow.last)
278
- puts_error "invalid command format, ends with an operator: #{cmd}"
279
- return 9
280
- # elsif ['&&','||', '|'].include?(flow.last)
281
- # puts_error "invalid command format, consecutive operators: #{cmd}"
282
- else
283
- #Morpheus::Logging::DarkPrinter.puts "Executing command flow: #{flow.inspect}" if Morpheus::Logging.debug?
284
- previous_command = nil
285
- previous_command_result = nil
286
- current_operator = nil
287
- still_executing = true
288
- flow.each do |flow_cmd|
289
- if still_executing
290
- if flow_cmd == '&&'
291
- # AND operator
292
- current_operator = flow_cmd
293
- if parse_result_exitstatus(previous_command_result) != 0
294
- still_executing = false
295
- end
296
- elsif flow_cmd == '||' # or with previous command
297
- current_operator = flow_cmd
298
- if parse_result_exitstatus(previous_command_result) == 0
299
- still_executing = false
300
- end
301
- elsif flow_cmd == '|' # or with previous command
302
- puts_error "The PIPE (|) operator is not yet supported =["
303
- previous_command_result = nil
280
+ if flow.size == 0
281
+ # no input eh?
282
+ else
283
+ last_command_result = nil
284
+ if ['&&','||', '|'].include?(flow.first)
285
+ puts_error "#{Morpheus::Terminal.angry_prompt}invalid command format, begins with an operator: #{input}"
286
+ return 99
287
+ elsif ['&&','||', '|'].include?(flow.last)
288
+ puts_error "#{Morpheus::Terminal.angry_prompt}invalid command format, ends with an operator: #{input}"
289
+ return 99
290
+ # elsif ['&&','||', '|'].include?(flow.last)
291
+ # puts_error "invalid command format, consecutive operators: #{cmd}"
292
+ else
293
+ #Morpheus::Logging::DarkPrinter.puts "Executing command flow: #{flow.inspect}" if Morpheus::Logging.debug?
294
+ previous_command = nil
295
+ previous_command_result = nil
296
+ current_operator = nil
297
+ still_executing = true
298
+ flow.each do |flow_cmd|
299
+ if still_executing
300
+ if flow_cmd == '&&'
301
+ # AND operator
302
+ current_operator = flow_cmd
303
+ if parse_result_exitstatus(previous_command_result) != 0
304
+ still_executing = false
305
+ end
306
+ elsif flow_cmd == '||' # or with previous command
307
+ current_operator = flow_cmd
308
+ if parse_result_exitstatus(previous_command_result) == 0
304
309
  still_executing = false
305
- # or just continue?
306
- else # it's a command, not an operator
307
- current_operator = nil
308
- previous_command_result = execute_command(flow_cmd)
309
310
  end
310
- previous_command = flow_cmd
311
- else
312
- #Morpheus::Logging::DarkPrinter.puts "operator skipped command: #{flow_cmd}" if Morpheus::Logging.debug?
313
- end
314
- # previous_command = flow_cmd
311
+ elsif flow_cmd == '|' # or with previous command
312
+ puts_error "The PIPE (|) operator is not yet supported =["
313
+ previous_command_result = nil
314
+ still_executing = false
315
+ # or just continue?
316
+ elsif flow_cmd.is_a?(Array)
317
+ # this is a subexpression, execute it as such
318
+ current_operator = nil
319
+ previous_command_result = execute_commands_as_expression(flow_cmd)
320
+ else # it's a command, not an operator
321
+ current_operator = nil
322
+ previous_command_result = execute_command(flow_cmd)
323
+ end
324
+ previous_command = flow_cmd
325
+ else
326
+ #Morpheus::Logging::DarkPrinter.puts "operator skipped command: #{flow_cmd}" if Morpheus::Logging.debug?
315
327
  end
316
- final_command_result = previous_command_result
328
+ # previous_command = flow_cmd
317
329
  end
318
- else
319
- # just one command, this else is not needed tho
320
- final_command_result = execute_command(cmd)
330
+ final_command_result = previous_command_result
321
331
  end
322
332
  end
323
- # skip logging of exit and !cmd
324
- unless input.strip.empty? || (["exit", "history"].include?(input.strip)) || input.strip[0].to_s.chr == "!"
325
- log_history_command(input.strip)
326
- end
327
333
  return final_command_result
328
334
  end
329
335
 
330
- # returns Array of strings like [command1, operator, command2, operator, command3]
331
- # eg. ["instances get 555", "||", "echo '%redInstance 555 is missing!'"]
332
- def parse_commands_and_operators(input)
333
- cmd = input
334
- cmd_flow = []
335
- begin
336
- and_regex = /\s+\&\&\s+/
337
- or_regex = /\s+\|\|\s+/
338
- pipe_regex = /\s+\|\s+/
339
- still_parsing = true
340
- while still_parsing
341
- and_index = cmd.index(and_regex)
342
- or_index = cmd.index(or_regex)
343
- pipe_index = cmd.index(pipe_regex)
344
- if and_index # && (or_index == nil || or_index > and_index) && (pipe_index == nil || pipe_index > and_index)
345
- cmd_flow << cmd[0..and_index-1]
346
- cmd_flow << "&&"
347
- cmd = cmd[and_index..-1].sub(and_regex, '')
348
- elsif or_index && (and_index == nil || and_index > or_index) && (pipe_index == nil || pipe_index > or_index)
349
- cmd_flow << cmd[0..or_index-1]
350
- cmd_flow << "||"
351
- cmd = cmd[or_index..-1].sub(or_regex, '')
352
- elsif pipe_index && (and_index == nil || and_index > pipe_index) && (or_index == nil || or_index > pipe_index)
353
- cmd_flow << cmd[0..pipe_index-1]
354
- cmd_flow << "|"
355
- cmd = cmd[pipe_index..-1].sub(pipe_regex, '')
356
- else
357
- cmd_flow << cmd
358
- still_parsing = false
359
- end
360
- end
361
- return cmd_flow
362
- rescue => ex
363
- puts_error "error parsing command flow: #{ex.message}"
364
- return [input]
365
- end
366
- end
367
-
368
336
  def execute_command(input)
369
- #puts "shell execute_command(#{input})"
337
+ #Morpheus::Logging::DarkPrinter.puts "Shell command: #{input}"
370
338
  input = input.to_s.strip
371
339
 
372
340
  if !input.empty?
@@ -519,7 +487,7 @@ class Morpheus::Cli::Shell
519
487
  puts "There is no previous command"
520
488
  return false
521
489
  end
522
- execute_commands(input)
490
+ return execute(input)
523
491
  elsif input =~ /^\!.+/
524
492
  cmd_number = input.sub("!", "").to_i
525
493
  if cmd_number != 0
@@ -533,7 +501,7 @@ class Morpheus::Cli::Shell
533
501
  # remove this from readline, and replace it with the old command
534
502
  Readline::HISTORY.pop
535
503
  Readline::HISTORY << old_input
536
- return execute_commands(old_input)
504
+ return execute(old_input)
537
505
  end
538
506
 
539
507
  elsif input == "insecure"
@@ -1,6 +1,6 @@
1
1
 
2
2
  module Morpheus
3
3
  module Cli
4
- VERSION = "3.3.2.5"
4
+ VERSION = "3.3.2.6"
5
5
  end
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: morpheus-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.2.5
4
+ version: 3.3.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Estes
@@ -218,6 +218,7 @@ files:
218
218
  - lib/morpheus/cli/clouds.rb
219
219
  - lib/morpheus/cli/coloring_command.rb
220
220
  - lib/morpheus/cli/command_error.rb
221
+ - lib/morpheus/cli/commands/standard/exit_command.rb
221
222
  - lib/morpheus/cli/containers_command.rb
222
223
  - lib/morpheus/cli/credentials.rb
223
224
  - lib/morpheus/cli/curl_command.rb
@@ -229,6 +230,7 @@ files:
229
230
  - lib/morpheus/cli/edit_profile_command.rb
230
231
  - lib/morpheus/cli/edit_rc_command.rb
231
232
  - lib/morpheus/cli/error_handler.rb
233
+ - lib/morpheus/cli/expression_parser.rb
232
234
  - lib/morpheus/cli/groups.rb
233
235
  - lib/morpheus/cli/hosts.rb
234
236
  - lib/morpheus/cli/image_builder_command.rb