morpheus-cli 5.5.1.5 → 5.5.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/api_client.rb +25 -0
  4. data/lib/morpheus/api/archive_buckets_interface.rb +1 -1
  5. data/lib/morpheus/api/body_io.rb +22 -0
  6. data/lib/morpheus/api/catalog_item_types_interface.rb +5 -1
  7. data/lib/morpheus/api/clients_interface.rb +41 -0
  8. data/lib/morpheus/api/clouds_interface.rb +21 -0
  9. data/lib/morpheus/api/instances_interface.rb +8 -1
  10. data/lib/morpheus/api/integrations_interface.rb +30 -0
  11. data/lib/morpheus/api/library_instance_types_interface.rb +15 -3
  12. data/lib/morpheus/api/network_pool_server_types_interface.rb +9 -0
  13. data/lib/morpheus/api/plugins_interface.rb +22 -0
  14. data/lib/morpheus/api/roles_interface.rb +20 -1
  15. data/lib/morpheus/api/security_package_types_interface.rb +9 -0
  16. data/lib/morpheus/api/security_packages_interface.rb +9 -0
  17. data/lib/morpheus/api/security_scans_interface.rb +9 -0
  18. data/lib/morpheus/api/servers_interface.rb +17 -17
  19. data/lib/morpheus/api/storage_providers_interface.rb +1 -1
  20. data/lib/morpheus/api/virtual_images_interface.rb +1 -23
  21. data/lib/morpheus/cli/cli_command.rb +81 -7
  22. data/lib/morpheus/cli/commands/apps.rb +28 -2
  23. data/lib/morpheus/cli/commands/archives_command.rb +2 -2
  24. data/lib/morpheus/cli/commands/blueprints_command.rb +16 -0
  25. data/lib/morpheus/cli/commands/catalog_item_types_command.rb +34 -2
  26. data/lib/morpheus/cli/commands/clients_command.rb +338 -0
  27. data/lib/morpheus/cli/commands/clouds.rb +127 -1
  28. data/lib/morpheus/cli/commands/clusters.rb +42 -12
  29. data/lib/morpheus/cli/commands/curl_command.rb +114 -135
  30. data/lib/morpheus/cli/commands/hosts.rb +108 -11
  31. data/lib/morpheus/cli/commands/instances.rb +115 -14
  32. data/lib/morpheus/cli/commands/integrations_command.rb +215 -4
  33. data/lib/morpheus/cli/commands/invoices_command.rb +20 -11
  34. data/lib/morpheus/cli/commands/jobs_command.rb +299 -190
  35. data/lib/morpheus/cli/commands/library_cluster_layouts_command.rb +16 -2
  36. data/lib/morpheus/cli/commands/library_container_scripts_command.rb +14 -0
  37. data/lib/morpheus/cli/commands/library_container_templates_command.rb +131 -48
  38. data/lib/morpheus/cli/commands/library_container_types_command.rb +17 -4
  39. data/lib/morpheus/cli/commands/library_instance_types_command.rb +85 -7
  40. data/lib/morpheus/cli/commands/library_layouts_command.rb +32 -1
  41. data/lib/morpheus/cli/commands/library_option_lists_command.rb +30 -18
  42. data/lib/morpheus/cli/commands/library_option_types_command.rb +31 -14
  43. data/lib/morpheus/cli/commands/library_spec_templates_command.rb +14 -0
  44. data/lib/morpheus/cli/commands/library_upgrades_command.rb +2 -2
  45. data/lib/morpheus/cli/commands/network_pool_server_types.rb +20 -0
  46. data/lib/morpheus/cli/commands/network_pool_servers_command.rb +55 -158
  47. data/lib/morpheus/cli/commands/network_pools_command.rb +49 -23
  48. data/lib/morpheus/cli/commands/networks_command.rb +262 -45
  49. data/lib/morpheus/cli/commands/plugins.rb +213 -0
  50. data/lib/morpheus/cli/commands/price_sets_command.rb +27 -8
  51. data/lib/morpheus/cli/commands/prices_command.rb +17 -5
  52. data/lib/morpheus/cli/commands/processes_command.rb +2 -1
  53. data/lib/morpheus/cli/commands/remote.rb +7 -10
  54. data/lib/morpheus/cli/commands/roles.rb +924 -335
  55. data/lib/morpheus/cli/commands/search_command.rb +2 -0
  56. data/lib/morpheus/cli/commands/security_groups.rb +72 -84
  57. data/lib/morpheus/cli/commands/security_package_types.rb +32 -0
  58. data/lib/morpheus/cli/commands/security_packages.rb +84 -0
  59. data/lib/morpheus/cli/commands/security_scans.rb +107 -0
  60. data/lib/morpheus/cli/commands/service_plans_command.rb +16 -14
  61. data/lib/morpheus/cli/commands/subnets_command.rb +15 -1
  62. data/lib/morpheus/cli/commands/tasks.rb +34 -1
  63. data/lib/morpheus/cli/commands/tenants_command.rb +1 -1
  64. data/lib/morpheus/cli/commands/user_settings_command.rb +11 -2
  65. data/lib/morpheus/cli/commands/users.rb +50 -9
  66. data/lib/morpheus/cli/commands/virtual_images.rb +14 -0
  67. data/lib/morpheus/cli/commands/workflows.rb +14 -0
  68. data/lib/morpheus/cli/mixins/accounts_helper.rb +6 -5
  69. data/lib/morpheus/cli/mixins/infrastructure_helper.rb +79 -0
  70. data/lib/morpheus/cli/mixins/jobs_helper.rb +4 -5
  71. data/lib/morpheus/cli/mixins/library_helper.rb +2 -0
  72. data/lib/morpheus/cli/mixins/logs_helper.rb +3 -0
  73. data/lib/morpheus/cli/mixins/monitoring_helper.rb +1 -1
  74. data/lib/morpheus/cli/mixins/print_helper.rb +29 -4
  75. data/lib/morpheus/cli/mixins/provisioning_helper.rb +38 -9
  76. data/lib/morpheus/cli/mixins/rest_command.rb +106 -8
  77. data/lib/morpheus/cli/mixins/secondary_rest_command.rb +6 -2
  78. data/lib/morpheus/cli/option_types.rb +94 -25
  79. data/lib/morpheus/cli/version.rb +1 -1
  80. data/lib/morpheus/formatters.rb +10 -1
  81. 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
- if args.count != 0
51
- raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args}\n#{optparse}"
52
- return 1
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
- print_red_alert "Invalid source filter #{options[:source]}"
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, options)
140
+ return _get(args[0], options, args.count > 1 ? args[1].to_i : nil)
139
141
  end
140
142
 
141
- def _get(job_id, max_execs = 3, options = {})
142
- begin
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
- params = {'includeExecCount' => max_execs}
146
+ if !(job_id.to_s =~ /\A\d{1,}\Z/)
147
+ job = find_by_name_or_id('job', job_id)
158
148
 
159
- if options[:dry_run]
160
- print_dry_run @jobs_interface.dry.get(job_id, params)
161
- return
149
+ if !job
150
+ print_red_alert "Job #{job_id} not found"
151
+ exit 1
162
152
  end
163
- json_response = @jobs_interface.get(job_id, params)
153
+ job_id = job['id']
154
+ end
164
155
 
165
- render_result = render_with_format(json_response, options, 'job')
166
- return 0 if render_result
156
+ max_execs = 3 if max_execs.nil?
167
157
 
168
- title = "Morpheus Job"
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
- "Job Type" => lambda {|it| it['type']['name']},
198
- "Enabled" => lambda {|it| format_boolean(it['enabled'])},
199
- (job['workflow'] ? 'Workflow' : 'Task') => lambda {|it| it['jobSummary']},
200
- "Schedule" => lambda {|it| schedule_name}
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["Context Type"] = lambda {|it| it['targetType'] == 'appliance' ? 'None' : it['targetType'] }
205
+ description_cols << {"Context Type" => lambda {|it| it['targetType'] == 'appliance' ? 'None' : it['targetType'] } }
205
206
 
206
207
  if job['targetType'] != 'appliance'
207
- description_cols["Context #{job['targetType'].capitalize}#{job['targets'].count > 1 ? 's' : ''}"] = lambda {|it| it['targets'].collect {|it| it['name']}.join(', ')}
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('-t', '--task [TASK]', String, "Task ID or code, assigns task to job. Incompatible with --workflow option.") do |val|
241
- if options[:workflow].nil?
242
- options[:task] = val
243
- else
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 code, assigns workflow to job. Incompatible with --task option.") do |val|
248
- if options[:task].nil?
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
- raise_command_error "Options --task and --workflow are incompatible"
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
- if options[:payload]
286
- payload = parse_payload(options, 'job')
287
- else
288
- apply_options(params, options)
289
-
290
- # name
291
- 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']
292
-
293
- if options[:task].nil? && options[:workflow].nil?
294
- # prompt job type
295
- job_types = @options_interface.options_for_source('jobTypes', {})['data']
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
- job_options = @jobs_interface.options(job_type_id)
300
-
301
- # prompt task / workflow
302
- if ['morpheus.task.jobType', 'morpheus.task'].include?(job_type['code'])
303
- params['task'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'task.id', 'fieldLabel' => 'Task', 'type' => 'select', 'required' => true, 'optionSource' => 'tasks'}], options[:options], @api_client, {})['task']
304
- else
305
- params['workflow'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'workflow.id', 'fieldLabel' => 'Workflow', 'type' => 'select', 'required' => true, 'optionSource' => 'operationTaskSets'}], options[:options], @api_client, {})['workflow']
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
- # task
310
- if !options[:task].nil?
311
- task = find_by_name_or_id('task', options[:task])
360
+ # task
361
+ if !options[:task].nil?
362
+ task = find_by_name_or_id('task', options[:task])
312
363
 
313
- if task.nil?
314
- print_red_alert "Task #{options[:task]} not found"
315
- exit 1
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
- # workflow
322
- task_set = nil
323
- if !options[:workflow].nil?
324
- task_set = find_by_name_or_id('task_set', options[:workflow])
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
- if task_set.nil?
327
- print_red_alert "Workflow #{options[:workflow]} not found"
328
- exit 1
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
- # prompt for custom options for workflow
342
- custom_option_types = task_set ? task_set['optionTypes'] : nil
343
- if custom_option_types && custom_option_types.size() > 0
344
- # they are all returned in a single array right now, so skip prompting for the jobType optionTypes
345
- custom_option_types.reject! { |it| it['code'] && it['code'].include?('job.type') }
346
- custom_option_types = custom_option_types.collect {|it|
347
- it['fieldContext'] = 'customOptions'
348
- it
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
- # load options based upon job type + task / taskset
356
- job_options = @jobs_interface.options(job_type_id, {'taskId' => params['task'] ? params['task']['id'] : nil, 'workflowId' => params['workflow'] ? params['workflow']['id'] : nil})
357
- option_type_id = job_options['optionTypes'][0]['id']
358
-
359
- # context type
360
- if params['targetType'].nil?
361
- 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']
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
- # contexts
365
- if ['instance', 'server'].include?(params['targetType']) && (params['targets'].nil? || params['targets'].empty?)
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
- # schedule
388
- if options[:schedule].nil?
389
- 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']
390
- params['scheduleMode'] = options[:schedule]
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
- if options[:schedule] == 'manual'
394
- # cool
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 schedule.nil?
405
- print_red_alert "Schedule #{options[:schedule]} not found"
406
- exit 1
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
- # custom config
413
- if params['customConfig'].nil? && job_options['allowCustomConfig']
414
- params['customConfig'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'config', 'fieldLabel' => "Custom Config", 'type' => 'text', 'required' => false}], options[:options], @api_client, {})['config']
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
- payload = {'job' => params}
417
- end
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
- @jobs_interface.setopts(options)
420
- if options[:dry_run]
421
- print_dry_run @jobs_interface.dry.create(payload)
422
- return
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
- json_response = @jobs_interface.create(payload)
425
- render_response(json_response, options, 'job') do
426
- print_green_success "Job created"
427
- _get(json_response['id'], 0, options)
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 code, assigns task to job. Incompatible with --workflow option.") do |val|
443
- if options[:workflow].nil?
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 code, assigns workflow to job. Incompatible with --task option.") do |val|
450
- if options[:task].nil?
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
- raise_command_error "Options --task and --workflow are incompatible"
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 "Job updated"
595
- _get(job['id'], nil, options)
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'], nil, options)
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 load_job_type_id_by_code(code)
835
- @options_interface.options_for_source('jobTypes', {})['data'].find {|it| it['code'] == code}['value'] rescue nil
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