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.
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 +40 -10
  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