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 +4 -4
- data/lib/morpheus/cli.rb +4 -0
- data/lib/morpheus/cli/clouds.rb +9 -2
- data/lib/morpheus/cli/commands/standard/exit_command.rb +36 -0
- data/lib/morpheus/cli/error_handler.rb +6 -0
- data/lib/morpheus/cli/expression_parser.rb +149 -0
- data/lib/morpheus/cli/hosts.rb +55 -29
- data/lib/morpheus/cli/instances.rb +3 -3
- data/lib/morpheus/cli/shell.rb +70 -102
- data/lib/morpheus/cli/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3638fa392f4c3276cb8f7dcfcde61cde875325a
|
4
|
+
data.tar.gz: 00c82492c99d9cedd9b0c9920bdace3d7ed8ed3c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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'
|
data/lib/morpheus/cli/clouds.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/morpheus/cli/hosts.rb
CHANGED
@@ -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("
|
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
|
935
|
-
|
936
|
-
|
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
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
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?
|
957
|
-
|
958
|
-
|
959
|
-
|
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'],
|
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'],
|
992
|
+
json_response = @servers_interface.workflow(host['id'],workflow['id'], payload)
|
970
993
|
if options[:json]
|
971
|
-
print
|
972
|
-
|
973
|
-
elsif
|
974
|
-
|
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
|
1876
|
-
|
1877
|
-
|
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])
|
data/lib/morpheus/cli/shell.rb
CHANGED
@@ -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 =
|
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 =
|
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
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
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
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
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
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
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
|
-
|
328
|
+
# previous_command = flow_cmd
|
317
329
|
end
|
318
|
-
|
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 "
|
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
|
-
|
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
|
504
|
+
return execute(old_input)
|
537
505
|
end
|
538
506
|
|
539
507
|
elsif input == "insecure"
|
data/lib/morpheus/cli/version.rb
CHANGED
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.
|
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
|