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 +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
|