morpheus-cli 5.5.1.4 → 5.5.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/api_client.rb +25 -0
- data/lib/morpheus/api/archive_buckets_interface.rb +1 -1
- data/lib/morpheus/api/body_io.rb +22 -0
- data/lib/morpheus/api/catalog_item_types_interface.rb +5 -1
- data/lib/morpheus/api/clients_interface.rb +41 -0
- data/lib/morpheus/api/clouds_interface.rb +21 -0
- data/lib/morpheus/api/instances_interface.rb +8 -1
- data/lib/morpheus/api/integrations_interface.rb +30 -0
- data/lib/morpheus/api/library_instance_types_interface.rb +15 -3
- data/lib/morpheus/api/network_pool_server_types_interface.rb +9 -0
- data/lib/morpheus/api/plugins_interface.rb +22 -0
- data/lib/morpheus/api/roles_interface.rb +20 -1
- data/lib/morpheus/api/security_package_types_interface.rb +9 -0
- data/lib/morpheus/api/security_packages_interface.rb +9 -0
- data/lib/morpheus/api/security_scans_interface.rb +9 -0
- data/lib/morpheus/api/servers_interface.rb +17 -17
- data/lib/morpheus/api/storage_providers_interface.rb +1 -1
- data/lib/morpheus/api/virtual_images_interface.rb +1 -23
- data/lib/morpheus/cli/cli_command.rb +81 -7
- data/lib/morpheus/cli/commands/apps.rb +28 -2
- data/lib/morpheus/cli/commands/archives_command.rb +2 -2
- data/lib/morpheus/cli/commands/blueprints_command.rb +16 -0
- data/lib/morpheus/cli/commands/catalog_item_types_command.rb +34 -2
- data/lib/morpheus/cli/commands/clients_command.rb +338 -0
- data/lib/morpheus/cli/commands/clouds.rb +127 -1
- data/lib/morpheus/cli/commands/clusters.rb +42 -12
- data/lib/morpheus/cli/commands/curl_command.rb +114 -135
- data/lib/morpheus/cli/commands/hosts.rb +108 -11
- data/lib/morpheus/cli/commands/instances.rb +115 -14
- data/lib/morpheus/cli/commands/integrations_command.rb +215 -4
- data/lib/morpheus/cli/commands/invoices_command.rb +20 -11
- data/lib/morpheus/cli/commands/jobs_command.rb +299 -190
- data/lib/morpheus/cli/commands/library_cluster_layouts_command.rb +16 -2
- data/lib/morpheus/cli/commands/library_container_scripts_command.rb +14 -0
- data/lib/morpheus/cli/commands/library_container_templates_command.rb +131 -48
- data/lib/morpheus/cli/commands/library_container_types_command.rb +17 -4
- data/lib/morpheus/cli/commands/library_instance_types_command.rb +85 -7
- data/lib/morpheus/cli/commands/library_layouts_command.rb +32 -1
- data/lib/morpheus/cli/commands/library_option_lists_command.rb +30 -18
- data/lib/morpheus/cli/commands/library_option_types_command.rb +31 -14
- data/lib/morpheus/cli/commands/library_spec_templates_command.rb +14 -0
- data/lib/morpheus/cli/commands/library_upgrades_command.rb +2 -2
- data/lib/morpheus/cli/commands/network_pool_server_types.rb +20 -0
- data/lib/morpheus/cli/commands/network_pool_servers_command.rb +55 -158
- data/lib/morpheus/cli/commands/network_pools_command.rb +49 -23
- data/lib/morpheus/cli/commands/networks_command.rb +262 -45
- data/lib/morpheus/cli/commands/plugins.rb +213 -0
- data/lib/morpheus/cli/commands/price_sets_command.rb +40 -10
- data/lib/morpheus/cli/commands/prices_command.rb +17 -5
- data/lib/morpheus/cli/commands/processes_command.rb +2 -1
- data/lib/morpheus/cli/commands/remote.rb +7 -10
- data/lib/morpheus/cli/commands/roles.rb +924 -335
- data/lib/morpheus/cli/commands/search_command.rb +2 -0
- data/lib/morpheus/cli/commands/security_groups.rb +72 -84
- data/lib/morpheus/cli/commands/security_package_types.rb +32 -0
- data/lib/morpheus/cli/commands/security_packages.rb +84 -0
- data/lib/morpheus/cli/commands/security_scans.rb +107 -0
- data/lib/morpheus/cli/commands/service_plans_command.rb +16 -14
- data/lib/morpheus/cli/commands/subnets_command.rb +15 -1
- data/lib/morpheus/cli/commands/tasks.rb +34 -1
- data/lib/morpheus/cli/commands/tenants_command.rb +1 -1
- data/lib/morpheus/cli/commands/user_settings_command.rb +11 -2
- data/lib/morpheus/cli/commands/users.rb +50 -9
- data/lib/morpheus/cli/commands/virtual_images.rb +14 -0
- data/lib/morpheus/cli/commands/workflows.rb +14 -0
- data/lib/morpheus/cli/mixins/accounts_helper.rb +6 -5
- data/lib/morpheus/cli/mixins/infrastructure_helper.rb +79 -0
- data/lib/morpheus/cli/mixins/jobs_helper.rb +4 -5
- data/lib/morpheus/cli/mixins/library_helper.rb +2 -0
- data/lib/morpheus/cli/mixins/logs_helper.rb +3 -0
- data/lib/morpheus/cli/mixins/monitoring_helper.rb +1 -1
- data/lib/morpheus/cli/mixins/print_helper.rb +29 -4
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +38 -9
- data/lib/morpheus/cli/mixins/rest_command.rb +106 -8
- data/lib/morpheus/cli/mixins/secondary_rest_command.rb +6 -2
- data/lib/morpheus/cli/option_types.rb +94 -25
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/formatters.rb +10 -1
- metadata +15 -2
|
@@ -42,27 +42,28 @@ class Morpheus::Cli::JobsCommand
|
|
|
42
42
|
opts.on("--stats [true|false]", String, "Hide Execution Stats. Job statistics are displayed by default.") do |val|
|
|
43
43
|
options[:show_stats] = (val.to_s != "false")
|
|
44
44
|
end
|
|
45
|
+
opts.on('-l', '--labels LABEL', String, "Filter by labels, can match any of the values") do |val|
|
|
46
|
+
add_query_parameter(params, 'labels', parse_labels(val))
|
|
47
|
+
end
|
|
48
|
+
opts.on('--all-labels LABEL', String, "Filter by labels, must match all of the values") do |val|
|
|
49
|
+
add_query_parameter(params, 'allLabels', parse_labels(val))
|
|
50
|
+
end
|
|
45
51
|
build_standard_list_options(opts, options)
|
|
46
52
|
opts.footer = "List jobs."
|
|
47
53
|
end
|
|
48
54
|
optparse.parse!(args)
|
|
49
55
|
connect(options)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
56
|
+
# verify_args!(args:args, optparse:optparse, count:0)
|
|
57
|
+
if args.count > 0
|
|
58
|
+
options[:phrase] = args.join(" ")
|
|
53
59
|
end
|
|
54
|
-
|
|
55
|
-
|
|
56
60
|
params.merge!(parse_list_options(options))
|
|
57
|
-
|
|
58
61
|
if !options[:source].nil?
|
|
59
62
|
if !['all', 'user', 'discovered', 'sync'].include?(options[:source])
|
|
60
|
-
|
|
61
|
-
exit 1
|
|
63
|
+
raise_command_error("Invalid source filter #{options[:source]}", args, optparse)
|
|
62
64
|
end
|
|
63
65
|
params['itemSource'] = options[:source] == 'discovered' ? 'sync' : options[:source]
|
|
64
66
|
end
|
|
65
|
-
|
|
66
67
|
@jobs_interface.setopts(options)
|
|
67
68
|
if options[:dry_run]
|
|
68
69
|
print_dry_run @jobs_interface.dry.list(params)
|
|
@@ -85,6 +86,7 @@ class Morpheus::Cli::JobsCommand
|
|
|
85
86
|
"ID" => 'id',
|
|
86
87
|
"Type" => lambda {|job| job['type'] ? job['type']['name'] : '' },
|
|
87
88
|
"Name" => 'name',
|
|
89
|
+
"Labels" => lambda {|it| format_list(it['labels'], '', 3) rescue '' },
|
|
88
90
|
"Details" => lambda {|job| job['jobSummary'] },
|
|
89
91
|
"Enabled" => lambda {|job| "#{job['enabled'] ? '' : yellow}#{format_boolean(job['enabled'])}#{cyan}" },
|
|
90
92
|
# "Date Created" => lambda {|job| format_local_dt(job['dateCreated']) },
|
|
@@ -135,41 +137,32 @@ class Morpheus::Cli::JobsCommand
|
|
|
135
137
|
raise_command_error "wrong number of arguments, expected 1-N and got (#{args.count}) #{args}\n#{optparse}"
|
|
136
138
|
end
|
|
137
139
|
connect(options)
|
|
138
|
-
return _get(args[0], args.count > 1 ? args[1].to_i : nil
|
|
140
|
+
return _get(args[0], options, args.count > 1 ? args[1].to_i : nil)
|
|
139
141
|
end
|
|
140
142
|
|
|
141
|
-
def _get(job_id,
|
|
142
|
-
|
|
143
|
-
@jobs_interface.setopts(options)
|
|
144
|
-
|
|
145
|
-
if !(job_id.to_s =~ /\A\d{1,}\Z/)
|
|
146
|
-
job = find_by_name_or_id('job', job_id)
|
|
147
|
-
|
|
148
|
-
if !job
|
|
149
|
-
print_red_alert "Job #{job_id} not found"
|
|
150
|
-
exit 1
|
|
151
|
-
end
|
|
152
|
-
job_id = job['id']
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
max_execs = 3 if max_execs.nil?
|
|
143
|
+
def _get(job_id, options = {}, max_execs = 3)
|
|
144
|
+
@jobs_interface.setopts(options)
|
|
156
145
|
|
|
157
|
-
|
|
146
|
+
if !(job_id.to_s =~ /\A\d{1,}\Z/)
|
|
147
|
+
job = find_by_name_or_id('job', job_id)
|
|
158
148
|
|
|
159
|
-
if
|
|
160
|
-
|
|
161
|
-
|
|
149
|
+
if !job
|
|
150
|
+
print_red_alert "Job #{job_id} not found"
|
|
151
|
+
exit 1
|
|
162
152
|
end
|
|
163
|
-
|
|
153
|
+
job_id = job['id']
|
|
154
|
+
end
|
|
164
155
|
|
|
165
|
-
|
|
166
|
-
return 0 if render_result
|
|
156
|
+
max_execs = 3 if max_execs.nil?
|
|
167
157
|
|
|
168
|
-
|
|
169
|
-
subtitles = []
|
|
170
|
-
subtitles += parse_list_subtitles(options)
|
|
171
|
-
print_h1 title, subtitles
|
|
158
|
+
params = {'includeExecCount' => max_execs}
|
|
172
159
|
|
|
160
|
+
if options[:dry_run]
|
|
161
|
+
print_dry_run @jobs_interface.dry.get(job_id, params)
|
|
162
|
+
return
|
|
163
|
+
end
|
|
164
|
+
json_response = @jobs_interface.get(job_id, params)
|
|
165
|
+
render_response(json_response, options, 'job') do
|
|
173
166
|
job = json_response['job']
|
|
174
167
|
schedule_name = ''
|
|
175
168
|
if !job['scheduleMode'].nil?
|
|
@@ -189,27 +182,44 @@ class Morpheus::Cli::JobsCommand
|
|
|
189
182
|
end
|
|
190
183
|
end
|
|
191
184
|
end
|
|
192
|
-
|
|
185
|
+
title = "Morpheus Job"
|
|
186
|
+
subtitles = []
|
|
187
|
+
subtitles += parse_list_subtitles(options)
|
|
188
|
+
print_h1 title, subtitles
|
|
193
189
|
print cyan
|
|
194
|
-
description_cols =
|
|
195
|
-
"ID" => lambda {|it| it['id'] },
|
|
196
|
-
"Name" => lambda {|it| it['name']},
|
|
197
|
-
"
|
|
198
|
-
"
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
190
|
+
description_cols = [
|
|
191
|
+
{"ID" => lambda {|it| it['id'] } },
|
|
192
|
+
{"Name" => lambda {|it| it['name']} },
|
|
193
|
+
{"Labels" => lambda {|it| format_list(it['labels'], '', 3) rescue '' } },
|
|
194
|
+
{"Job Type" => lambda {|it| it['type']['name']} },
|
|
195
|
+
{"Enabled" => lambda {|it| format_boolean(it['enabled'])} },
|
|
196
|
+
job['workflow'] ? {'Task' => lambda {|it| (it['workflow']['name'] rescue nil) || it['jobSummary']} } : nil,
|
|
197
|
+
job['task'] ? {'Workflow' => lambda {|it| (it['task']['name'] rescue nil) || it['jobSummary']} } : nil,
|
|
198
|
+
job['securityPackage'] ? {'Security Package' => lambda {|it| (it['securityPackage']['name'] rescue nil) || it['jobSummary']} } : nil,
|
|
199
|
+
job['securityPackage'] ? {'Scan Checklist' => lambda {|it| (it['scanPath'] rescue nil)} } : nil,
|
|
200
|
+
job['securityPackage'] ? {'Security Profile' => lambda {|it| (it['securityProfile'] rescue nil)} } : nil,
|
|
201
|
+
{"Schedule" => lambda {|it| schedule_name} }
|
|
202
|
+
].compact
|
|
202
203
|
|
|
203
204
|
if job['targetType']
|
|
204
|
-
description_cols
|
|
205
|
+
description_cols << {"Context Type" => lambda {|it| it['targetType'] == 'appliance' ? 'None' : it['targetType'] } }
|
|
205
206
|
|
|
206
207
|
if job['targetType'] != 'appliance'
|
|
207
|
-
description_cols
|
|
208
|
+
description_cols << {"Context #{job['targetType'].capitalize}#{job['targets'].count > 1 ? 's' : ''}" => lambda {|it| it['targets'].collect {|it| it['name']}.join(', ')} }
|
|
208
209
|
end
|
|
209
210
|
end
|
|
210
211
|
|
|
211
212
|
print_description_list(description_cols, job)
|
|
212
213
|
|
|
214
|
+
# config = job['config']
|
|
215
|
+
# # prune config of stuff that user does not xare about
|
|
216
|
+
# config = config.reject! {|k,v| ["configList","workflowId","taskId","securitySpecId", "customConfig"].include?(k) }
|
|
217
|
+
# if config && !config.empty?
|
|
218
|
+
# print_h2 "Configuration"
|
|
219
|
+
# print_description_list(config.keys, config)
|
|
220
|
+
# print reset,"\n"
|
|
221
|
+
# end
|
|
222
|
+
|
|
213
223
|
if max_execs != 0
|
|
214
224
|
print_h2 "Recent Executions"
|
|
215
225
|
print_job_executions(json_response['executions']['jobExecutions'], options)
|
|
@@ -219,11 +229,8 @@ class Morpheus::Cli::JobsCommand
|
|
|
219
229
|
end
|
|
220
230
|
end
|
|
221
231
|
print reset,"\n"
|
|
222
|
-
return 0
|
|
223
|
-
rescue RestClient::Exception => e
|
|
224
|
-
print_rest_exception(e, options)
|
|
225
|
-
exit 1
|
|
226
232
|
end
|
|
233
|
+
return 0, nil
|
|
227
234
|
end
|
|
228
235
|
|
|
229
236
|
def add(args)
|
|
@@ -234,22 +241,44 @@ class Morpheus::Cli::JobsCommand
|
|
|
234
241
|
opts.on("--name NAME", String, "Updates job name") do |val|
|
|
235
242
|
params['name'] = val.to_s
|
|
236
243
|
end
|
|
244
|
+
opts.on('-l', '--labels [LIST]', String, "Labels") do |val|
|
|
245
|
+
params['labels'] = parse_labels(val)
|
|
246
|
+
end
|
|
237
247
|
opts.on('-a', '--active [on|off]', String, "Can be used to enable / disable the job. Default is on") do |val|
|
|
238
248
|
params['enabled'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
|
|
239
249
|
end
|
|
240
|
-
opts.on('
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
250
|
+
opts.on('--type [TYPE]', String, "Job Type Name, Code or ID. The available types are \"Security Scan Job\", \"Task Job\", and \"Workflow Job\"") do |val|
|
|
251
|
+
options[:type] = val
|
|
252
|
+
end
|
|
253
|
+
opts.on('-t', '--task [TASK]', String, "Task ID or name, assigns task to job. This sets the job type to \"Task Job\".") do |val|
|
|
254
|
+
if options[:workflow]
|
|
244
255
|
raise_command_error "Options --task and --workflow are incompatible"
|
|
256
|
+
elsif options[:security_package]
|
|
257
|
+
raise_command_error "Options --task and --security-package are incompatible"
|
|
258
|
+
else
|
|
259
|
+
options[:task] = val
|
|
245
260
|
end
|
|
261
|
+
options[:type] = 'morpheus.task'
|
|
246
262
|
end
|
|
247
|
-
opts.on('-w', '--workflow [WORKFLOW]', String, "Workflow ID or
|
|
248
|
-
if options[:task]
|
|
263
|
+
opts.on('-w', '--workflow [WORKFLOW]', String, "Workflow ID or name, assigns workflow to job. This sets the job type to \"Workflow Job\".") do |val|
|
|
264
|
+
if options[:task]
|
|
265
|
+
raise_command_error "Options --workflow and --task are incompatible"
|
|
266
|
+
elsif options[:security_package]
|
|
267
|
+
raise_command_error "Options --workflow and --security-package are incompatible"
|
|
268
|
+
else
|
|
249
269
|
options[:workflow] = val
|
|
270
|
+
end
|
|
271
|
+
options[:type] = 'morpheus.workflow'
|
|
272
|
+
end
|
|
273
|
+
opts.on('--security-package [PACKAGE]', String, "Security Package ID or name, assigns security package to job. This sets the job type to \"Security Scan Job\".") do |val|
|
|
274
|
+
if options[:workflow]
|
|
275
|
+
raise_command_error "Options --security-package and --workflow are incompatible"
|
|
276
|
+
elsif options[:task]
|
|
277
|
+
raise_command_error "Options --security-package and --workflow are incompatible"
|
|
250
278
|
else
|
|
251
|
-
|
|
279
|
+
options[:security_package] = val
|
|
252
280
|
end
|
|
281
|
+
options[:type] = 'morpheus.securityScan'
|
|
253
282
|
end
|
|
254
283
|
opts.on('--context-type [TYPE]', String, "Context type (instance|server|none). Default is none") do |val|
|
|
255
284
|
params['targetType'] = (val == 'none' ? 'appliance' : val)
|
|
@@ -275,6 +304,16 @@ class Morpheus::Cli::JobsCommand
|
|
|
275
304
|
options[:schedule] = 'dateTime'
|
|
276
305
|
params['dateTime'] = val.to_s
|
|
277
306
|
end
|
|
307
|
+
opts.on('--scan-checklist [VALUE]', String, "Scan Checklist. Only applicable to the security scan job type") do |val|
|
|
308
|
+
# params['config'] ||= {}
|
|
309
|
+
# params['config']['scanPath'] = val.to_s
|
|
310
|
+
params['scanPath'] = val.to_s
|
|
311
|
+
end
|
|
312
|
+
opts.on('--security-profile [VALUE]', String, "Security Profile. Only applicable to the security scan job type") do |val|
|
|
313
|
+
# params['config'] ||= {}
|
|
314
|
+
# params['config']['securityProfile'] = val.to_s
|
|
315
|
+
params['securityProfile'] = val.to_s
|
|
316
|
+
end
|
|
278
317
|
build_standard_add_options(opts, options)
|
|
279
318
|
opts.footer = "Create job."
|
|
280
319
|
end
|
|
@@ -282,150 +321,196 @@ class Morpheus::Cli::JobsCommand
|
|
|
282
321
|
connect(options)
|
|
283
322
|
verify_args!(args:args, optparse:optparse, max:1)
|
|
284
323
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
324
|
+
if options[:payload]
|
|
325
|
+
payload = parse_payload(options, 'job')
|
|
326
|
+
else
|
|
327
|
+
apply_options(params, options)
|
|
328
|
+
|
|
329
|
+
# name
|
|
330
|
+
params['name'] = params['name'] || args[0] || name = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => 'Job Name', 'required' => true, 'description' => 'Job Name.'}],options[:options],@api_client,{})['name']
|
|
331
|
+
|
|
332
|
+
# type id is needed to load the options right now..
|
|
333
|
+
job_type = nil
|
|
334
|
+
job_types = get_available_job_types()
|
|
335
|
+
if options[:task].nil? && options[:workflow].nil? && options[:security_package].nil?
|
|
336
|
+
if options[:type]
|
|
337
|
+
# match on value (id) or code or name
|
|
338
|
+
job_type = job_types.find {|it| it['value'].to_s.downcase == options[:type].to_s.downcase } || job_types.find {|it| it['code'].to_s.downcase == options[:type].to_s.downcase } || job_types.find {|it| it['name'].to_s.downcase == options[:type].to_s.downcase || it['name'].to_s.downcase == "#{options[:type]} Job".to_s.downcase }
|
|
339
|
+
if job_type.nil?
|
|
340
|
+
raise_command_error "Job Type not found for '#{options[:type]}'"
|
|
341
|
+
end
|
|
342
|
+
else
|
|
343
|
+
# prompt
|
|
296
344
|
job_type_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'jobType', 'type' => 'select', 'fieldLabel' => 'Job Type', 'selectOptions' => job_types, 'required' => true, 'description' => 'Select Job Type.'}],options[:options],@api_client,{})['jobType']
|
|
297
345
|
job_type = job_types.find {|it| it['value'] == job_type_id}
|
|
346
|
+
end
|
|
298
347
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
end
|
|
348
|
+
# prompt task / workflow / securityPackage
|
|
349
|
+
if job_type['code'] == 'morpheus.task'
|
|
350
|
+
params['task'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'task.id', 'fieldLabel' => 'Task', 'type' => 'select', 'required' => true, 'optionSource' => 'tasks'}], options[:options], @api_client, {})['task']
|
|
351
|
+
elsif job_type['code'] == 'morpheus.workflow'
|
|
352
|
+
params['workflow'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'workflow.id', 'fieldLabel' => 'Workflow', 'type' => 'select', 'required' => true, 'optionSource' => 'operationTaskSets'}], options[:options], @api_client, {})['workflow']
|
|
353
|
+
elsif job_type['code'] == 'morpheus.securityScan'
|
|
354
|
+
params['securityPackage'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'securityPackage.id', 'fieldLabel' => 'Security Package', 'type' => 'select', 'required' => true, 'optionSource' => 'securityPackages'}], options[:options], @api_client, {})['securityPackage']
|
|
307
355
|
end
|
|
356
|
+
# type is not even included in the payload? lol
|
|
357
|
+
# params["type"] = {"code" => job_type["code"]}
|
|
358
|
+
end
|
|
308
359
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
360
|
+
# task
|
|
361
|
+
if !options[:task].nil?
|
|
362
|
+
task = find_by_name_or_id('task', options[:task])
|
|
312
363
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
end
|
|
317
|
-
params['task'] = {'id' => task['id']}
|
|
318
|
-
job_type_id = load_job_type_id_by_code('morpheus.task.jobType') || load_job_type_id_by_code('morpheus.task')
|
|
364
|
+
if task.nil?
|
|
365
|
+
print_red_alert "Task #{options[:task]} not found"
|
|
366
|
+
exit 1
|
|
319
367
|
end
|
|
368
|
+
params['task'] = {'id' => task['id']}
|
|
369
|
+
job_type = job_types.find {|it| it['code'] == 'morpheus.task' } # || raise_command_error "Unable to find job type for 'morpheus.task'"
|
|
370
|
+
end
|
|
320
371
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
372
|
+
# workflow
|
|
373
|
+
task_set = nil
|
|
374
|
+
if !options[:workflow].nil?
|
|
375
|
+
task_set = find_by_name_or_id('task_set', options[:workflow])
|
|
325
376
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
end
|
|
330
|
-
params['workflow'] = {'id' => task_set['id']}
|
|
331
|
-
job_type_id = load_job_type_id_by_code('morpheus.workflow.jobType') || load_job_type_id_by_code('morpheus.workflow')
|
|
332
|
-
end
|
|
333
|
-
# load workflow if we havent yet
|
|
334
|
-
if (params['workflow'] && params['workflow']['id']) && task_set.nil?
|
|
335
|
-
task_set = find_by_name_or_id('task_set', params['workflow']['id'])
|
|
336
|
-
if task_set.nil?
|
|
337
|
-
print_red_alert "Workflow #{params['workflow']['id']} not found"
|
|
338
|
-
exit 1
|
|
339
|
-
end
|
|
377
|
+
if task_set.nil?
|
|
378
|
+
print_red_alert "Workflow #{options[:workflow]} not found"
|
|
379
|
+
exit 1
|
|
340
380
|
end
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
custom_options = Morpheus::Cli::OptionTypes.prompt(custom_option_types, options[:options], @api_client, {})
|
|
351
|
-
params['customOptions'] = custom_options['customOptions']
|
|
381
|
+
params['workflow'] = {'id' => task_set['id']}
|
|
382
|
+
job_type = job_types.find {|it| it['code'] == 'morpheus.workflow' } # || raise_command_error "Unable to find job type for 'morpheus.workflow'"
|
|
383
|
+
end
|
|
384
|
+
# load workflow if we havent yet
|
|
385
|
+
if (params['workflow'] && params['workflow']['id']) && task_set.nil?
|
|
386
|
+
task_set = find_by_name_or_id('task_set', params['workflow']['id'])
|
|
387
|
+
if task_set.nil?
|
|
388
|
+
print_red_alert "Workflow #{params['workflow']['id']} not found"
|
|
389
|
+
exit 1
|
|
352
390
|
end
|
|
391
|
+
end
|
|
392
|
+
# prompt for custom options for workflow
|
|
393
|
+
custom_option_types = task_set ? task_set['optionTypes'] : nil
|
|
394
|
+
if custom_option_types && custom_option_types.size() > 0
|
|
395
|
+
# they are all returned in a single array right now, so skip prompting for the jobType optionTypes
|
|
396
|
+
custom_option_types.reject! { |it| it['code'] && it['code'].include?('job.type') }
|
|
397
|
+
custom_option_types = custom_option_types.collect {|it|
|
|
398
|
+
it['fieldContext'] = 'customOptions'
|
|
399
|
+
it
|
|
400
|
+
}
|
|
401
|
+
custom_options = Morpheus::Cli::OptionTypes.prompt(custom_option_types, options[:options], @api_client, {})
|
|
402
|
+
params['customOptions'] = custom_options['customOptions']
|
|
403
|
+
end
|
|
353
404
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
405
|
+
# security package
|
|
406
|
+
if !options[:security_package].nil?
|
|
407
|
+
# task = find_by_name_or_id('securityPackage', options[:security_package])
|
|
408
|
+
security_package = nil
|
|
409
|
+
if (options[:security_package].to_s =~ /\A\d{1,}\Z/)
|
|
410
|
+
security_package = @options_interface.options_for_source('securityPackages', {})['data'].find {|it| it['value'] == options[:security_package].to_i }['value']
|
|
411
|
+
else
|
|
412
|
+
security_package = @options_interface.options_for_source('securityPackages', {})['data'].find {|it| it['name'].to_s.downcase == options[:security_package].to_s.downcase }['value']
|
|
362
413
|
end
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
targets = []
|
|
367
|
-
if params['targetType'] == 'instance'
|
|
368
|
-
avail_targets = @instances_interface.list({max:10000})['instances'].collect {|it| {'name' => it['name'], 'value' => it['id']}}
|
|
369
|
-
else
|
|
370
|
-
avail_targets = @servers_interface.list({max:10000, 'vmHypervisor' => nil, 'containerHypervisor' => nil})['servers'].collect {|it| {'name' => it['name'], 'value' => it['id']}}
|
|
371
|
-
end
|
|
372
|
-
target_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'target', 'fieldLabel' => "Context #{params['targetType'].capitalize}", 'type' => 'select', 'required' => true, 'selectOptions' => avail_targets}], options[:options], @api_client, {}, options[:no_prompt], true)['target']
|
|
373
|
-
targets << target_id
|
|
374
|
-
avail_targets.reject! {|it| it['value'] == target_id}
|
|
375
|
-
|
|
376
|
-
while !target_id.nil? && !avail_targets.empty? && Morpheus::Cli::OptionTypes.confirm("Add another context #{params['targetType']}?", {:default => false})
|
|
377
|
-
target_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'target', 'fieldLabel' => "Context #{params['targetType'].capitalize}", 'type' => 'select', 'required' => false, 'selectOptions' => avail_targets}], options[:options], @api_client, {}, options[:no_prompt], true)['target']
|
|
378
|
-
|
|
379
|
-
if !target_id.nil?
|
|
380
|
-
targets << target_id
|
|
381
|
-
avail_targets.reject! {|it| it['value'] == target_id}
|
|
382
|
-
end
|
|
383
|
-
end
|
|
384
|
-
params['targets'] = targets.collect {|it| {'refId' => it}}
|
|
414
|
+
if security_package.nil?
|
|
415
|
+
print_red_alert "Security Package #{options[:security_package]} not found"
|
|
416
|
+
exit 1
|
|
385
417
|
end
|
|
386
|
-
|
|
387
|
-
#
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
418
|
+
params['securityPackage'] = {'id' => security_package['id']}
|
|
419
|
+
job_type = job_types.find {|it| it['code'] == 'morpheus.securityScan' } # || raise_command_error "Unable to find job type for 'morpheus.workflow'"
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
# load options based upon job type + task / workflow / securityPackage
|
|
423
|
+
job_options = @jobs_interface.options(job_type['id'], {'taskId' => params['task'] ? params['task']['id'] : nil, 'workflowId' => params['workflow'] ? params['workflow']['id'] : nil, 'securityPackageId' => params['securityPackage'] ? params['securityPackage']['id'] : nil})
|
|
424
|
+
|
|
425
|
+
# securityScan options
|
|
426
|
+
if job_type['code'] == 'morpheus.securityScan'
|
|
427
|
+
job_type_option_types = [
|
|
428
|
+
{'fieldName' => 'scanPath', 'fieldLabel' => "Scan Checklist", 'type' => 'text', 'required' => true, 'displayOrder' => 20},
|
|
429
|
+
{'fieldName' => 'securityProfile', 'fieldLabel' => "Security Profile", 'type' => 'text', 'required' => true, 'displayOrder' => 21}
|
|
430
|
+
]
|
|
431
|
+
#job_type_option_types = job_options['optionTypes'] || []
|
|
432
|
+
# reject securitySpecId because already prompted for securityPackage above,
|
|
433
|
+
# and it is passed as securityPackage.id instead of config.securitySpecId
|
|
434
|
+
# also remove config context because jobs return this configuration at the level too
|
|
435
|
+
#job_type_option_types.reject! {|it| it['fieldName'] == "securitySpecId" }
|
|
436
|
+
#job_type_option_types = job_type_option_types.collect {|it| it.delete("fieldContext") if it["fieldContext"] == "config" }
|
|
437
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt(job_type_option_types, options[:options], @api_client, {})
|
|
438
|
+
v_prompt.deep_compact!
|
|
439
|
+
params.deep_merge!(v_prompt)
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
# context type
|
|
443
|
+
if params['targetType'].nil?
|
|
444
|
+
params['targetType'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'targetType', 'fieldLabel' => 'Context Type', 'type' => 'select', 'required' => true, 'selectOptions' => job_options['targetTypes'], 'defaultValue' => job_options['targetTypes'].first['name']}], options[:options], @api_client, {})['targetType']
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
# contexts
|
|
448
|
+
if ['instance', 'server'].include?(params['targetType']) && (params['targets'].nil? || params['targets'].empty?)
|
|
449
|
+
targets = []
|
|
450
|
+
if params['targetType'] == 'instance'
|
|
451
|
+
avail_targets = @instances_interface.list({max:10000})['instances'].collect {|it| {'name' => it['name'], 'value' => it['id']}}
|
|
452
|
+
else
|
|
453
|
+
avail_targets = @servers_interface.list({max:10000, 'vmHypervisor' => nil, 'containerHypervisor' => nil})['servers'].collect {|it| {'name' => it['name'], 'value' => it['id']}}
|
|
391
454
|
end
|
|
455
|
+
target_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'target', 'fieldLabel' => "Context #{params['targetType'].capitalize}", 'type' => 'select', 'required' => true, 'selectOptions' => avail_targets}], options[:options], @api_client, {}, options[:no_prompt], true)['target']
|
|
456
|
+
targets << target_id
|
|
457
|
+
avail_targets.reject! {|it| it['value'] == target_id}
|
|
392
458
|
|
|
393
|
-
|
|
394
|
-
#
|
|
395
|
-
elsif options[:schedule].to_s.downcase == 'datetime'
|
|
396
|
-
# prompt for dateTime
|
|
397
|
-
if params['dateTime'].nil?
|
|
398
|
-
params['dateTime'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'dateTime', 'fieldLabel' => "Date and Time", 'type' => 'text', 'required' => true}], options[:options], @api_client, {}, options[:no_prompt], true)['dateTime']
|
|
399
|
-
end
|
|
400
|
-
elsif options[:schedule].to_s != ''
|
|
401
|
-
# ok they passed a schedule name or id
|
|
402
|
-
schedule = job_options['schedules'].find {|it| it['name'] == options[:schedule] || it['value'] == options[:schedule].to_i}
|
|
459
|
+
while !target_id.nil? && !avail_targets.empty? && Morpheus::Cli::OptionTypes.confirm("Add another context #{params['targetType']}?", {:default => false})
|
|
460
|
+
target_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'target', 'fieldLabel' => "Context #{params['targetType'].capitalize}", 'type' => 'select', 'required' => false, 'selectOptions' => avail_targets}], options[:options], @api_client, {}, options[:no_prompt], true)['target']
|
|
403
461
|
|
|
404
|
-
if
|
|
405
|
-
|
|
406
|
-
|
|
462
|
+
if !target_id.nil?
|
|
463
|
+
targets << target_id
|
|
464
|
+
avail_targets.reject! {|it| it['value'] == target_id}
|
|
407
465
|
end
|
|
408
|
-
options[:schedule] = schedule['value']
|
|
409
466
|
end
|
|
467
|
+
params['targets'] = targets.collect {|it| {'refId' => it}}
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
# schedule
|
|
471
|
+
if options[:schedule].nil?
|
|
472
|
+
options[:schedule] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'scheduleMode', 'fieldLabel' => "Schedule", 'type' => 'select', 'required' => true, 'selectOptions' => job_options['schedules'], 'defaultValue' => job_options['schedules'].first['name']}], options[:options], @api_client, {})['scheduleMode']
|
|
410
473
|
params['scheduleMode'] = options[:schedule]
|
|
474
|
+
end
|
|
411
475
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
476
|
+
if options[:schedule] == 'manual'
|
|
477
|
+
# cool
|
|
478
|
+
elsif options[:schedule].to_s.downcase == 'datetime'
|
|
479
|
+
# prompt for dateTime
|
|
480
|
+
if params['dateTime'].nil?
|
|
481
|
+
params['dateTime'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'dateTime', 'fieldLabel' => "Date and Time", 'type' => 'text', 'required' => true}], options[:options], @api_client, {}, options[:no_prompt], true)['dateTime']
|
|
415
482
|
end
|
|
416
|
-
|
|
417
|
-
|
|
483
|
+
elsif options[:schedule].to_s != ''
|
|
484
|
+
# ok they passed a schedule name or id
|
|
485
|
+
schedule = job_options['schedules'].find {|it| it['name'] == options[:schedule] || it['value'] == options[:schedule].to_i}
|
|
418
486
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
487
|
+
if schedule.nil?
|
|
488
|
+
print_red_alert "Schedule #{options[:schedule]} not found"
|
|
489
|
+
exit 1
|
|
490
|
+
end
|
|
491
|
+
options[:schedule] = schedule['value']
|
|
423
492
|
end
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
493
|
+
params['scheduleMode'] = options[:schedule]
|
|
494
|
+
|
|
495
|
+
# custom config
|
|
496
|
+
if params['customConfig'].nil? && job_options['allowCustomConfig']
|
|
497
|
+
params['customConfig'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'config', 'fieldLabel' => "Custom Config", 'type' => 'text', 'required' => false}], options[:options], @api_client, {})['config']
|
|
428
498
|
end
|
|
499
|
+
payload = {'job' => params}
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
@jobs_interface.setopts(options)
|
|
503
|
+
if options[:dry_run]
|
|
504
|
+
print_dry_run @jobs_interface.dry.create(payload)
|
|
505
|
+
return
|
|
506
|
+
end
|
|
507
|
+
json_response = @jobs_interface.create(payload)
|
|
508
|
+
job = json_response['job'] || payload["job"] # api only recently started returning job!
|
|
509
|
+
render_response(json_response, options, 'job') do
|
|
510
|
+
# print_green_success "Job created"
|
|
511
|
+
print_green_success "Added job #{job['name']}"
|
|
512
|
+
_get(job['id'], options)
|
|
513
|
+
end
|
|
429
514
|
end
|
|
430
515
|
|
|
431
516
|
def update(args)
|
|
@@ -436,21 +521,37 @@ class Morpheus::Cli::JobsCommand
|
|
|
436
521
|
opts.on("--name NAME", String, "Updates job name") do |val|
|
|
437
522
|
params['name'] = val.to_s
|
|
438
523
|
end
|
|
524
|
+
opts.on('-l', '--labels [LIST]', String, "Labels") do |val|
|
|
525
|
+
params['labels'] = parse_labels(val)
|
|
526
|
+
end
|
|
439
527
|
opts.on('-a', '--active [on|off]', String, "Can be used to enable / disable the job. Default is on") do |val|
|
|
440
528
|
params['enabled'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
|
|
441
529
|
end
|
|
442
|
-
opts.on('-t', '--task [TASK]', String, "Task ID or
|
|
443
|
-
if options[:workflow]
|
|
444
|
-
options[:task] = val
|
|
445
|
-
else
|
|
530
|
+
opts.on('-t', '--task [TASK]', String, "Task ID or name, assigns task to job. Only compatible with workflow job type.") do |val|
|
|
531
|
+
if options[:workflow]
|
|
446
532
|
raise_command_error "Options --task and --workflow are incompatible"
|
|
533
|
+
elsif options[:security_package]
|
|
534
|
+
raise_command_error "Options --task and --security-package are incompatible"
|
|
535
|
+
else
|
|
536
|
+
options[:task] = val
|
|
447
537
|
end
|
|
448
538
|
end
|
|
449
|
-
opts.on('-w', '--workflow [WORKFLOW]', String, "Workflow ID or
|
|
450
|
-
if options[:task]
|
|
539
|
+
opts.on('-w', '--workflow [WORKFLOW]', String, "Workflow ID or name, assigns workflow to job. Only compatible with security scan job type.") do |val|
|
|
540
|
+
if options[:task]
|
|
541
|
+
raise_command_error "Options --workflow and --task are incompatible"
|
|
542
|
+
elsif options[:security_package]
|
|
543
|
+
raise_command_error "Options --workflow and --security-package are incompatible"
|
|
544
|
+
else
|
|
451
545
|
options[:workflow] = val
|
|
546
|
+
end
|
|
547
|
+
end
|
|
548
|
+
opts.on('--security-package [PACKAGE]', String, "Security Package ID or name, assigns security package to job. Only compatible with security scan job type.") do |val|
|
|
549
|
+
if options[:workflow]
|
|
550
|
+
raise_command_error "Options --security-package and --workflow are incompatible"
|
|
551
|
+
elsif options[:task]
|
|
552
|
+
raise_command_error "Options --security-package and --workflow are incompatible"
|
|
452
553
|
else
|
|
453
|
-
|
|
554
|
+
options[:security_package] = val
|
|
454
555
|
end
|
|
455
556
|
end
|
|
456
557
|
opts.on('--context-type [TYPE]', String, "Context type (instance|server|none). Default is none") do |val|
|
|
@@ -590,9 +691,10 @@ class Morpheus::Cli::JobsCommand
|
|
|
590
691
|
return
|
|
591
692
|
end
|
|
592
693
|
json_response = @jobs_interface.update(job['id'], payload)
|
|
694
|
+
job = json_response["job"] || job # api only recently started returning job!
|
|
593
695
|
render_response(json_response, options, 'job') do
|
|
594
|
-
print_green_success
|
|
595
|
-
_get(job['id'],
|
|
696
|
+
print_green_success "Updated job #{job['name']}"
|
|
697
|
+
_get(job['id'], options)
|
|
596
698
|
end
|
|
597
699
|
end
|
|
598
700
|
|
|
@@ -636,7 +738,7 @@ class Morpheus::Cli::JobsCommand
|
|
|
636
738
|
elsif !options[:quiet]
|
|
637
739
|
if json_response['success']
|
|
638
740
|
print_green_success "Job queued for execution"
|
|
639
|
-
_get(job['id'],
|
|
741
|
+
_get(job['id'], options)
|
|
640
742
|
else
|
|
641
743
|
print_red_alert "Error executing job: #{json_response['msg'] || json_response['errors']}"
|
|
642
744
|
end
|
|
@@ -831,8 +933,15 @@ class Morpheus::Cli::JobsCommand
|
|
|
831
933
|
|
|
832
934
|
private
|
|
833
935
|
|
|
834
|
-
def
|
|
835
|
-
@options_interface.options_for_source('jobTypes', {})['data']
|
|
936
|
+
def get_available_job_types()
|
|
937
|
+
job_types = @options_interface.options_for_source('jobTypes', {})['data']
|
|
938
|
+
# the jobType code has this ".jobType" added to the end for ui translation or something..
|
|
939
|
+
job_types.each do |jt|
|
|
940
|
+
# jt['msgCode'] = jt['code']
|
|
941
|
+
jt['code'] = jt['code'].sub(/\.jobType\Z/,'')
|
|
942
|
+
# set id for convenience
|
|
943
|
+
jt['id'] = jt['value'] if !jt['id'] && jt['value']
|
|
944
|
+
end
|
|
945
|
+
job_types
|
|
836
946
|
end
|
|
837
|
-
|
|
838
947
|
end
|