morpheus-cli 5.2.1 → 5.2.2
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/Dockerfile +1 -1
- data/lib/morpheus/api/invoices_interface.rb +12 -3
- data/lib/morpheus/cli/budgets_command.rb +389 -319
- data/lib/morpheus/cli/commands/standard/curl_command.rb +25 -10
- data/lib/morpheus/cli/commands/standard/history_command.rb +6 -2
- data/lib/morpheus/cli/dashboard_command.rb +260 -20
- data/lib/morpheus/cli/invoices_command.rb +63 -1
- data/lib/morpheus/cli/jobs_command.rb +94 -92
- data/lib/morpheus/cli/library_option_types_command.rb +5 -3
- data/lib/morpheus/cli/mixins/print_helper.rb +13 -6
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/formatters.rb +21 -11
- metadata +2 -2
@@ -28,6 +28,7 @@ class Morpheus::Cli::JobsCommand
|
|
28
28
|
|
29
29
|
def list(args)
|
30
30
|
options = {}
|
31
|
+
options[:show_stats] = true
|
31
32
|
params = {}
|
32
33
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
33
34
|
opts.banner = subcommand_usage()
|
@@ -37,7 +38,10 @@ class Morpheus::Cli::JobsCommand
|
|
37
38
|
opts.on("--internal [true|false]", String, "Filters job based on internal flag. Internal jobs are excluded by default.") do |val|
|
38
39
|
params["internalOnly"] = (val.to_s != "false")
|
39
40
|
end
|
40
|
-
|
41
|
+
opts.on("--stats [true|false]", String, "Hide Execution Stats. Job statistics are displayed by default.") do |val|
|
42
|
+
options[:show_stats] = (val.to_s != "false")
|
43
|
+
end
|
44
|
+
build_standard_list_options(opts, options)
|
41
45
|
opts.footer = "List jobs."
|
42
46
|
end
|
43
47
|
optparse.parse!(args)
|
@@ -47,78 +51,72 @@ class Morpheus::Cli::JobsCommand
|
|
47
51
|
return 1
|
48
52
|
end
|
49
53
|
|
50
|
-
begin
|
51
|
-
params.merge!(parse_list_options(options))
|
52
54
|
|
53
|
-
|
54
|
-
if !['all', 'user', 'discovered', 'sync'].include?(options[:source])
|
55
|
-
print_red_alert "Invalid source filter #{options[:source]}"
|
56
|
-
exit 1
|
57
|
-
end
|
58
|
-
params['itemSource'] = options[:source] == 'discovered' ? 'sync' : options[:source]
|
59
|
-
end
|
55
|
+
params.merge!(parse_list_options(options))
|
60
56
|
|
61
|
-
|
62
|
-
if options[:
|
63
|
-
|
64
|
-
|
57
|
+
if !options[:source].nil?
|
58
|
+
if !['all', 'user', 'discovered', 'sync'].include?(options[:source])
|
59
|
+
print_red_alert "Invalid source filter #{options[:source]}"
|
60
|
+
exit 1
|
65
61
|
end
|
66
|
-
|
67
|
-
|
68
|
-
render_result = render_with_format(json_response, options, 'jobs')
|
69
|
-
return 0 if render_result
|
62
|
+
params['itemSource'] = options[:source] == 'discovered' ? 'sync' : options[:source]
|
63
|
+
end
|
70
64
|
|
65
|
+
@jobs_interface.setopts(options)
|
66
|
+
if options[:dry_run]
|
67
|
+
print_dry_run @jobs_interface.dry.list(params)
|
68
|
+
return
|
69
|
+
end
|
70
|
+
json_response = @jobs_interface.list(params)
|
71
|
+
jobs = json_response['jobs']
|
72
|
+
render_response(json_response, options, 'jobs') do
|
71
73
|
title = "Morpheus Jobs"
|
72
74
|
subtitles = []
|
73
75
|
subtitles += parse_list_subtitles(options)
|
74
76
|
if params["internalOnly"]
|
75
77
|
subtitles << "internalOnly: #{params['internalOnly']}"
|
76
78
|
end
|
77
|
-
print_h1 title, subtitles
|
78
|
-
|
79
|
-
jobs = json_response['jobs']
|
80
|
-
|
79
|
+
print_h1 title, subtitles, options
|
81
80
|
if jobs.empty?
|
82
81
|
print cyan,"No jobs found.",reset,"\n"
|
83
82
|
else
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
}
|
95
|
-
|
96
|
-
columns
|
97
|
-
:id, :type, :name, :details, :enabled, :lastRun, :nextRun, :lastResult
|
98
|
-
]
|
99
|
-
print as_pretty_table(rows, columns, options)
|
83
|
+
columns = {
|
84
|
+
"ID" => 'id',
|
85
|
+
"Type" => lambda {|job| job['type'] ? job['type']['name'] : '' },
|
86
|
+
"Name" => 'name',
|
87
|
+
"Details" => lambda {|job| job['jobSummary'] },
|
88
|
+
"Enabled" => lambda {|job| "#{job['enabled'] ? '' : yellow}#{format_boolean(job['enabled'])}#{cyan}" },
|
89
|
+
# "Date Created" => lambda {|job| format_local_dt(job['dateCreated']) },
|
90
|
+
# "Last Updated" => lambda {|job| format_local_dt(job['lastUpdated']) },
|
91
|
+
"Last Run" => lambda {|job| format_local_dt(job['lastRun']) },
|
92
|
+
"Next Run" => lambda {|job| job['enabled'] && job['scheduleMode'] && job['scheduleMode'] != 'manual' ? format_local_dt(job['nextFire']) : '' },
|
93
|
+
"Last Result" => lambda {|job| format_status(job['lastResult']) },
|
94
|
+
}
|
95
|
+
print as_pretty_table(jobs, columns.upcase_keys!, options)
|
100
96
|
print_results_pagination(json_response)
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
97
|
+
if options[:show_stats]
|
98
|
+
if stats = json_response['stats']
|
99
|
+
label_width = 17
|
100
|
+
|
101
|
+
print_h2 "Execution Stats - Last 7 Days"
|
102
|
+
print cyan
|
103
|
+
|
104
|
+
print "Jobs".rjust(label_width, ' ') + ": #{stats['jobCount']}\n"
|
105
|
+
print "Executions Today".rjust(label_width, ' ') + ": #{stats['todayCount']}\n"
|
106
|
+
print "Daily Executions".rjust(label_width, ' ') + ": " + stats['executionsPerDay'].join(' | ') + "\n"
|
107
|
+
print "Total Executions".rjust(label_width, ' ') + ": #{stats['execCount']}\n"
|
108
|
+
print "Completed".rjust(label_width, ' ') + ": " + generate_usage_bar(stats['execSuccessRate'].to_f, 100, {bar_color:green}) + "#{stats['execSuccess']}".rjust(15, ' ') + " of " + "#{stats['execCount']}".ljust(15, ' ') + "\n#{cyan}"
|
109
|
+
print "Failed".rjust(label_width, ' ') + ": " + generate_usage_bar(stats['execFailedRate'].to_f, 100, {bar_color:red}) + "#{stats['execFailed']}".rjust(15, ' ') + " of " + "#{stats['execCount']}".ljust(15, ' ') + "\n#{cyan}"
|
110
|
+
end
|
111
|
+
print reset,"\n"
|
114
112
|
end
|
115
|
-
print reset,"\n"
|
116
113
|
end
|
117
114
|
print reset,"\n"
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
115
|
+
end
|
116
|
+
if jobs.empty?
|
117
|
+
return 1, "no jobs found"
|
118
|
+
else
|
119
|
+
return 0, nil
|
122
120
|
end
|
123
121
|
end
|
124
122
|
|
@@ -735,53 +733,57 @@ class Morpheus::Cli::JobsCommand
|
|
735
733
|
params = {}
|
736
734
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
737
735
|
opts.banner = subcommand_usage("[job]")
|
738
|
-
|
736
|
+
opts.on('--job JOB', String, "Filter by Job ID or name.") do |val|
|
737
|
+
options[:job] = val
|
738
|
+
end
|
739
|
+
opts.on("--internal [true|false]", String, "Filters executions based on internal flag. Internal executions are excluded by default.") do |val|
|
740
|
+
params["internalOnly"] = (val.to_s != "false")
|
741
|
+
end
|
742
|
+
build_standard_list_options(opts, options)
|
739
743
|
opts.footer = "List job executions.\n" +
|
740
744
|
"[job] is optional. Job ID or name to filter executions."
|
741
745
|
|
742
746
|
end
|
743
747
|
optparse.parse!(args)
|
744
748
|
connect(options)
|
745
|
-
|
746
|
-
|
747
|
-
|
749
|
+
# verify_args!(args:args, optparse:optparse, max:1)
|
750
|
+
if args.count > 0
|
751
|
+
options[:job] = args.join(" ")
|
748
752
|
end
|
749
753
|
|
750
|
-
|
751
|
-
params.merge!(parse_list_options(options))
|
752
|
-
|
753
|
-
if args.count > 0
|
754
|
-
job = find_by_name_or_id('job', args[0])
|
754
|
+
params.merge!(parse_list_options(options))
|
755
755
|
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
params['jobId'] = job['id']
|
761
|
-
end
|
762
|
-
|
763
|
-
@jobs_interface.setopts(options)
|
764
|
-
if options[:dry_run]
|
765
|
-
print_dry_run @jobs_interface.dry.list_executions(params)
|
766
|
-
return
|
756
|
+
if options[:job]
|
757
|
+
job = find_by_name_or_id('job', options[:job])
|
758
|
+
if job.nil?
|
759
|
+
raise_command_error "Job #{options[:job]} not found"
|
767
760
|
end
|
768
|
-
|
769
|
-
|
770
|
-
render_result = render_with_format(json_response, options, 'jobExecutions')
|
771
|
-
return 0 if render_result
|
761
|
+
params['jobId'] = job['id']
|
762
|
+
end
|
772
763
|
|
764
|
+
@jobs_interface.setopts(options)
|
765
|
+
if options[:dry_run]
|
766
|
+
print_dry_run @jobs_interface.dry.list_executions(params)
|
767
|
+
return
|
768
|
+
end
|
769
|
+
json_response = @jobs_interface.list_executions(params)
|
770
|
+
job_executions = json_response['jobExecutions']
|
771
|
+
render_response(json_response, options, 'jobExecutions') do
|
773
772
|
title = "Morpheus Job Executions"
|
774
773
|
subtitles = job ? ["Job: #{job['name']}"] : []
|
775
774
|
subtitles += parse_list_subtitles(options)
|
776
|
-
|
777
|
-
|
778
|
-
|
775
|
+
if params["internalOnly"]
|
776
|
+
subtitles << "internalOnly: #{params['internalOnly']}"
|
777
|
+
end
|
778
|
+
print_h1 title, subtitles, options
|
779
|
+
print_job_executions(job_executions, options)
|
779
780
|
print_results_pagination(json_response)
|
780
781
|
print reset,"\n"
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
782
|
+
end
|
783
|
+
if job_executions.empty?
|
784
|
+
return 3, "no executions found"
|
785
|
+
else
|
786
|
+
return 0, nil
|
785
787
|
end
|
786
788
|
end
|
787
789
|
|
@@ -812,7 +814,7 @@ class Morpheus::Cli::JobsCommand
|
|
812
814
|
end
|
813
815
|
json_response = @jobs_interface.get_execution(args[0], params)
|
814
816
|
|
815
|
-
render_result = render_with_format(json_response, options, '
|
817
|
+
render_result = render_with_format(json_response, options, 'jobExecution')
|
816
818
|
return 0 if render_result
|
817
819
|
|
818
820
|
title = "Morpheus Job Execution"
|
@@ -845,7 +847,7 @@ class Morpheus::Cli::JobsCommand
|
|
845
847
|
description_cols = {
|
846
848
|
"Process ID" => lambda {|it| it[:id]},
|
847
849
|
"Description" => lambda {|it| it[:description]},
|
848
|
-
"Start
|
850
|
+
"Start Date" => lambda {|it| it[:start_date]},
|
849
851
|
"Created By" => lambda {|it| it[:created_by]},
|
850
852
|
"Duration" => lambda {|it| it[:duration]},
|
851
853
|
"Status" => lambda {|it| it[:status]}
|
@@ -919,7 +921,7 @@ class Morpheus::Cli::JobsCommand
|
|
919
921
|
description_cols = {
|
920
922
|
"ID" => lambda {|it| it[:id]},
|
921
923
|
"Description" => lambda {|it| it[:description]},
|
922
|
-
"Start
|
924
|
+
"Start Date" => lambda {|it| it[:start_date]},
|
923
925
|
"Created By" => lambda {|it| it[:created_by]},
|
924
926
|
"Duration" => lambda {|it| it[:duration]},
|
925
927
|
"Status" => lambda {|it| it[:status]}
|
@@ -974,7 +976,7 @@ class Morpheus::Cli::JobsCommand
|
|
974
976
|
job: ex['job'] ? ex['job']['name'] : '',
|
975
977
|
description: ex['description'] || ex['job'] ? ex['job']['description'] : '',
|
976
978
|
type: ex['job'] && ex['job']['type'] ? (ex['job']['type']['code'] == 'morpheus.workflow' ? 'Workflow' : 'Task') : '',
|
977
|
-
|
979
|
+
start: format_local_dt(ex['startDate']),
|
978
980
|
duration: ex['duration'] ? format_human_duration(ex['duration'] / 1000.0) : '',
|
979
981
|
status: format_status(ex['status']),
|
980
982
|
error: truncate_string(ex['process'] && (ex['process']['message'] || ex['process']['error']) ? ex['process']['message'] || ex['process']['error'] : '', 32)
|
@@ -982,7 +984,7 @@ class Morpheus::Cli::JobsCommand
|
|
982
984
|
end
|
983
985
|
|
984
986
|
columns = [
|
985
|
-
:id, :job, :type, :
|
987
|
+
:id, :job, :type, {'START DATE' => :start}, {'ETA/TIME' => :duration}, :status, :error
|
986
988
|
]
|
987
989
|
print as_pretty_table(rows, columns, options)
|
988
990
|
print reset,"\n"
|
@@ -140,6 +140,7 @@ class Morpheus::Cli::LibraryOptionTypesCommand
|
|
140
140
|
"Type" => lambda {|it| it['type'].to_s.capitalize },
|
141
141
|
"Option List" => lambda {|it| it['optionList'] ? it['optionList']['name'] : nil },
|
142
142
|
"Placeholder" => 'placeHolder',
|
143
|
+
"Help Block" => 'helpBlock',
|
143
144
|
"Default Value" => 'defaultValue',
|
144
145
|
"Required" => lambda {|it| format_boolean(it['required']) },
|
145
146
|
"Export As Tag" => lambda {|it| it['exportMeta'].nil? ? '' : format_boolean(it['exportMeta']) },
|
@@ -297,9 +298,10 @@ class Morpheus::Cli::LibraryOptionTypesCommand
|
|
297
298
|
{'fieldName' => 'optionList', 'fieldLabel' => 'Option List', 'type' => 'select', 'optionSource' => 'optionTypeLists', 'required' => true, 'dependsOnCode' => 'optionType.type:select', 'description' => "The Option List to be the source of options when type is 'select'.", 'displayOrder' => 5},
|
298
299
|
{'fieldName' => 'fieldLabel', 'fieldLabel' => 'Field Label', 'type' => 'text', 'required' => true, 'description' => 'This is the input label that shows typically to the left of a custom option.', 'displayOrder' => 6},
|
299
300
|
{'fieldName' => 'placeHolder', 'fieldLabel' => 'Placeholder', 'type' => 'text', 'displayOrder' => 7},
|
300
|
-
{'fieldName' => '
|
301
|
-
{'fieldName' => '
|
302
|
-
{'fieldName' => '
|
301
|
+
{'fieldName' => 'helpBlock', 'fieldLabel' => 'Help Block', 'type' => 'text', 'description' => 'This is the explaination of the input that shows typically underneath the option.', 'displayOrder' => 8},
|
302
|
+
{'fieldName' => 'defaultValue', 'fieldLabel' => 'Default Value', 'type' => 'text', 'displayOrder' => 9},
|
303
|
+
{'fieldName' => 'required', 'fieldLabel' => 'Required', 'type' => 'checkbox', 'defaultValue' => false, 'displayOrder' => 10},
|
304
|
+
{'fieldName' => 'exportMeta', 'fieldLabel' => 'Export As Tag', 'type' => 'checkbox', 'defaultValue' => false, 'description' => 'Export as Tag.', 'displayOrder' => 11},
|
303
305
|
]
|
304
306
|
end
|
305
307
|
|
@@ -448,7 +448,8 @@ module Morpheus::Cli::PrintHelper
|
|
448
448
|
|
449
449
|
if opts[:bar_color] == :rainbow
|
450
450
|
rainbow_bar = ""
|
451
|
-
cur_rainbow_color =
|
451
|
+
cur_rainbow_color = reset # default terminal color
|
452
|
+
rainbow_bar << cur_rainbow_color
|
452
453
|
bars.each_with_index {|bar, i|
|
453
454
|
reached_percent = (i / max_bars.to_f) * 100
|
454
455
|
new_bar_color = cur_rainbow_color
|
@@ -458,6 +459,8 @@ module Morpheus::Cli::PrintHelper
|
|
458
459
|
new_bar_color = yellow
|
459
460
|
elsif reached_percent > 10
|
460
461
|
new_bar_color = cyan
|
462
|
+
else
|
463
|
+
new_bar_color = reset
|
461
464
|
end
|
462
465
|
if cur_rainbow_color != new_bar_color
|
463
466
|
cur_rainbow_color = new_bar_color
|
@@ -471,7 +474,7 @@ module Morpheus::Cli::PrintHelper
|
|
471
474
|
#rainbow_bar << " " * padding
|
472
475
|
end
|
473
476
|
rainbow_bar << reset
|
474
|
-
bar_display =
|
477
|
+
bar_display = cyan + "[" + rainbow_bar + cyan + "]" + " #{cur_rainbow_color}#{percent_label}#{reset}"
|
475
478
|
out << bar_display
|
476
479
|
elsif opts[:bar_color] == :solid
|
477
480
|
bar_color = cyan
|
@@ -479,12 +482,16 @@ module Morpheus::Cli::PrintHelper
|
|
479
482
|
bar_color = red
|
480
483
|
elsif percent > 50
|
481
484
|
bar_color = yellow
|
485
|
+
elsif percent > 10
|
486
|
+
bar_color = cyan
|
487
|
+
else
|
488
|
+
bar_color = reset
|
482
489
|
end
|
483
|
-
bar_display =
|
490
|
+
bar_display = cyan + "[" + bar_color + bars.join.ljust(max_bars, ' ') + cyan + "]" + " #{percent_label}" + reset
|
484
491
|
out << bar_display
|
485
492
|
else
|
486
|
-
bar_color = opts[:bar_color] ||
|
487
|
-
bar_display =
|
493
|
+
bar_color = opts[:bar_color] || reset
|
494
|
+
bar_display = cyan + "[" + bar_color + bars.join.ljust(max_bars, ' ') + cyan + "]" + " #{percent_label}" + reset
|
488
495
|
out << bar_display
|
489
496
|
end
|
490
497
|
return out
|
@@ -504,7 +511,7 @@ module Morpheus::Cli::PrintHelper
|
|
504
511
|
out << cyan + "Max CPU".rjust(label_width, ' ') + ": " + generate_usage_bar(cpu_usage.to_f, 100) + "\n"
|
505
512
|
end
|
506
513
|
if opts[:include].include?(:avg_cpu)
|
507
|
-
cpu_usage = stats['cpuUsageAvg']
|
514
|
+
cpu_usage = stats['cpuUsageAvg'] || stats['cpuUsageAverage']
|
508
515
|
out << cyan + "Avg. CPU".rjust(label_width, ' ') + ": " + generate_usage_bar(cpu_usage.to_f, 100) + "\n"
|
509
516
|
end
|
510
517
|
if opts[:include].include?(:cpu)
|
data/lib/morpheus/cli/version.rb
CHANGED
data/lib/morpheus/formatters.rb
CHANGED
@@ -15,11 +15,7 @@ def parse_time(dt, format=nil)
|
|
15
15
|
elsif dt.is_a?(String)
|
16
16
|
result = nil
|
17
17
|
err = nil
|
18
|
-
|
19
|
-
result = Time.parse(dt)
|
20
|
-
rescue => e
|
21
|
-
err = e
|
22
|
-
end
|
18
|
+
|
23
19
|
if !result
|
24
20
|
format ||= DEFAULT_TIME_FORMAT
|
25
21
|
if format
|
@@ -29,12 +25,26 @@ def parse_time(dt, format=nil)
|
|
29
25
|
err = e
|
30
26
|
end
|
31
27
|
end
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
28
|
+
end
|
29
|
+
if !result
|
30
|
+
begin
|
31
|
+
result = Time.strptime(dt, ALTERNATE_TIME_FORMAT)
|
32
|
+
rescue => e
|
33
|
+
# err = e
|
34
|
+
end
|
35
|
+
end
|
36
|
+
if !result
|
37
|
+
begin
|
38
|
+
result = Time.strptime(dt, DEFAULT_DATE_FORMAT)
|
39
|
+
rescue => e
|
40
|
+
# err = e
|
41
|
+
end
|
42
|
+
end
|
43
|
+
if !result
|
44
|
+
begin
|
45
|
+
result = Time.parse(dt)
|
46
|
+
rescue => e
|
47
|
+
err = e
|
38
48
|
end
|
39
49
|
end
|
40
50
|
if result
|
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: 5.2.
|
4
|
+
version: 5.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Estes
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2020-
|
14
|
+
date: 2020-12-15 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|