morpheus-cli 3.3.2.5 → 3.3.2.6

Sign up to get free protection for your applications and to get access to all the features.
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