foreman_remote_execution 4.5.6 → 5.0.0

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 (118) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby_ci.yml +7 -0
  3. data/.rubocop_todo.yml +1 -0
  4. data/app/controllers/api/v2/job_invocations_controller.rb +16 -1
  5. data/app/controllers/ui_job_wizard_controller.rb +16 -4
  6. data/app/graphql/mutations/job_invocations/create.rb +43 -0
  7. data/app/graphql/types/job_invocation.rb +16 -0
  8. data/app/graphql/types/job_invocation_input.rb +13 -0
  9. data/app/graphql/types/recurrence_input.rb +8 -0
  10. data/app/graphql/types/scheduling_input.rb +6 -0
  11. data/app/graphql/types/targeting_enum.rb +7 -0
  12. data/app/lib/actions/remote_execution/run_host_job.rb +6 -1
  13. data/app/lib/actions/remote_execution/run_hosts_job.rb +57 -3
  14. data/app/mailers/rex_job_mailer.rb +15 -0
  15. data/app/models/concerns/foreman_remote_execution/host_extensions.rb +8 -0
  16. data/app/models/job_invocation.rb +4 -0
  17. data/app/models/job_invocation_composer.rb +21 -13
  18. data/app/models/job_template.rb +1 -1
  19. data/app/models/remote_execution_provider.rb +17 -2
  20. data/app/models/rex_mail_notification.rb +13 -0
  21. data/app/models/targeting.rb +2 -2
  22. data/app/services/ui_notifications/remote_execution_jobs/base_job_finish.rb +2 -1
  23. data/app/views/dashboard/_latest-jobs.html.erb +21 -0
  24. data/app/views/job_invocations/refresh.js.erb +1 -0
  25. data/app/views/rex_job_mailer/job_finished.html.erb +24 -0
  26. data/app/views/rex_job_mailer/job_finished.text.erb +9 -0
  27. data/app/views/template_invocations/show.html.erb +2 -1
  28. data/config/routes.rb +1 -0
  29. data/db/migrate/20210816100932_rex_setting_category_to_dsl.rb +5 -0
  30. data/db/seeds.d/50-notification_blueprints.rb +14 -0
  31. data/db/seeds.d/95-mail_notifications.rb +24 -0
  32. data/foreman_remote_execution.gemspec +2 -4
  33. data/lib/foreman_remote_execution/engine.rb +114 -6
  34. data/lib/foreman_remote_execution/version.rb +1 -1
  35. data/package.json +6 -6
  36. data/test/functional/api/v2/job_invocations_controller_test.rb +20 -0
  37. data/test/functional/cockpit_controller_test.rb +0 -1
  38. data/test/graphql/mutations/job_invocations/create.rb +58 -0
  39. data/test/graphql/queries/job_invocation_query_test.rb +31 -0
  40. data/test/graphql/queries/job_invocations_query_test.rb +35 -0
  41. data/test/helpers/remote_execution_helper_test.rb +0 -1
  42. data/test/unit/actions/run_host_job_test.rb +21 -0
  43. data/test/unit/actions/run_hosts_job_test.rb +99 -4
  44. data/test/unit/concerns/host_extensions_test.rb +40 -7
  45. data/test/unit/input_template_renderer_test.rb +1 -89
  46. data/test/unit/job_invocation_composer_test.rb +4 -17
  47. data/test/unit/job_invocation_report_template_test.rb +16 -13
  48. data/test/unit/job_template_effective_user_test.rb +0 -4
  49. data/test/unit/remote_execution_provider_test.rb +34 -4
  50. data/test/unit/targeting_test.rb +68 -1
  51. data/webpack/JobWizard/JobWizard.js +106 -15
  52. data/webpack/JobWizard/JobWizard.scss +73 -39
  53. data/webpack/JobWizard/JobWizardConstants.js +36 -0
  54. data/webpack/JobWizard/JobWizardSelectors.js +32 -0
  55. data/webpack/JobWizard/__tests__/fixtures.js +81 -6
  56. data/webpack/JobWizard/__tests__/integration.test.js +26 -15
  57. data/webpack/JobWizard/__tests__/validation.test.js +141 -0
  58. data/webpack/JobWizard/autofill.js +38 -0
  59. data/webpack/JobWizard/index.js +7 -0
  60. data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +7 -4
  61. data/webpack/JobWizard/steps/AdvancedFields/DescriptionField.js +32 -9
  62. data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +216 -12
  63. data/webpack/JobWizard/steps/AdvancedFields/__tests__/__snapshots__/AdvancedFields.test.js.snap +82 -0
  64. data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.js +1 -0
  65. data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +3 -2
  66. data/webpack/JobWizard/steps/HostsAndInputs/HostPreviewModal.js +62 -0
  67. data/webpack/JobWizard/steps/HostsAndInputs/HostSearch.js +54 -0
  68. data/webpack/JobWizard/steps/HostsAndInputs/SelectAPI.js +33 -0
  69. data/webpack/JobWizard/steps/HostsAndInputs/SelectGQL.js +52 -0
  70. data/webpack/JobWizard/steps/HostsAndInputs/SelectedChips.js +82 -7
  71. data/webpack/JobWizard/steps/HostsAndInputs/__tests__/HostsAndInputs.test.js +151 -0
  72. data/webpack/JobWizard/steps/HostsAndInputs/__tests__/TemplateInputs.test.js +7 -4
  73. data/webpack/JobWizard/steps/HostsAndInputs/buildHostQuery.js +18 -0
  74. data/webpack/JobWizard/steps/HostsAndInputs/hostgroups.gql +8 -0
  75. data/webpack/JobWizard/steps/HostsAndInputs/hosts.gql +8 -0
  76. data/webpack/JobWizard/steps/HostsAndInputs/index.js +182 -34
  77. data/webpack/JobWizard/steps/ReviewDetails/index.js +193 -0
  78. data/webpack/JobWizard/steps/Schedule/PurposeField.js +31 -0
  79. data/webpack/JobWizard/steps/Schedule/QueryType.js +46 -43
  80. data/webpack/JobWizard/steps/Schedule/RepeatCron.js +53 -0
  81. data/webpack/JobWizard/steps/Schedule/RepeatDaily.js +37 -0
  82. data/webpack/JobWizard/steps/Schedule/RepeatHour.js +54 -0
  83. data/webpack/JobWizard/steps/Schedule/RepeatMonth.js +46 -0
  84. data/webpack/JobWizard/steps/Schedule/RepeatOn.js +95 -31
  85. data/webpack/JobWizard/steps/Schedule/RepeatWeek.js +70 -0
  86. data/webpack/JobWizard/steps/Schedule/ScheduleType.js +24 -21
  87. data/webpack/JobWizard/steps/Schedule/StartEndDates.js +78 -23
  88. data/webpack/JobWizard/steps/Schedule/__tests__/Schedule.test.js +402 -0
  89. data/webpack/JobWizard/steps/Schedule/__tests__/StartEndDates.test.js +20 -10
  90. data/webpack/JobWizard/steps/Schedule/index.js +153 -19
  91. data/webpack/JobWizard/steps/form/DateTimePicker.js +126 -0
  92. data/webpack/JobWizard/steps/form/FormHelpers.js +4 -0
  93. data/webpack/JobWizard/steps/form/Formatter.js +39 -8
  94. data/webpack/JobWizard/steps/form/NumberInput.js +3 -2
  95. data/webpack/JobWizard/steps/form/ResourceSelect.js +29 -0
  96. data/webpack/JobWizard/steps/form/SearchSelect.js +121 -0
  97. data/webpack/JobWizard/steps/form/SelectField.js +14 -3
  98. data/webpack/JobWizard/steps/form/__tests__/SelectSearch.test.js +33 -0
  99. data/webpack/JobWizard/submit.js +120 -0
  100. data/webpack/JobWizard/validation.js +53 -0
  101. data/webpack/__mocks__/foremanReact/Root/Context/ForemanContext/index.js +2 -0
  102. data/webpack/__mocks__/foremanReact/common/I18n.js +2 -0
  103. data/webpack/__mocks__/foremanReact/components/AutoComplete/AutoCompleteActions.js +1 -0
  104. data/webpack/__mocks__/foremanReact/components/AutoComplete/AutoCompleteConstants.js +1 -0
  105. data/webpack/__mocks__/foremanReact/routes/RouterSelector.js +1 -0
  106. data/webpack/helpers.js +1 -0
  107. data/webpack/react_app/components/RecentJobsCard/JobStatusIcon.js +43 -0
  108. data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +73 -66
  109. data/webpack/react_app/components/RecentJobsCard/RecentJobsTable.js +98 -0
  110. data/webpack/react_app/components/RecentJobsCard/constants.js +11 -0
  111. data/webpack/react_app/components/RecentJobsCard/styles.scss +11 -0
  112. data/webpack/react_app/extend/fillRecentJobsCard.js +1 -1
  113. metadata +56 -23
  114. data/app/models/setting/remote_execution.rb +0 -88
  115. data/webpack/JobWizard/steps/AdvancedFields/__tests__/DescriptionField.test.js +0 -23
  116. data/webpack/JobWizard/steps/HostsAndInputs/__tests__/SelectedChips.test.js +0 -37
  117. data/webpack/JobWizard/steps/form/__tests__/Formatter.test.js.example +0 -76
  118. data/webpack/react_app/components/RecentJobsCard/styles.css +0 -15
@@ -0,0 +1,5 @@
1
+ class RexSettingCategoryToDsl < ActiveRecord::Migration[6.0]
2
+ def up
3
+ Setting.where(category: 'Setting::RemoteExecution').update_all(category: 'Setting')
4
+ end
5
+ end
@@ -13,6 +13,20 @@ blueprints = [
13
13
  ],
14
14
  },
15
15
  },
16
+ {
17
+ group: N_('Jobs'),
18
+ name: 'rex_job_failed',
19
+ message: N_("A job '%{subject}' has failed"),
20
+ level: 'error',
21
+ actions:
22
+ {
23
+ links:
24
+ [
25
+ path_method: :job_invocation_path,
26
+ title: N_('Job Details'),
27
+ ],
28
+ },
29
+ },
16
30
  ]
17
31
 
18
32
  blueprints.each { |blueprint| UINotifications::Seed.new(blueprint).configure }
@@ -0,0 +1,24 @@
1
+ N_('Remote execution job')
2
+
3
+ notifications = [
4
+ {
5
+ :name => 'remote_execution_job',
6
+ :description => N_('A notification when a job finishes'),
7
+ :mailer => 'RexJobMailer',
8
+ :method => 'job_finished',
9
+ :subscription_type => 'alert',
10
+ },
11
+ ]
12
+
13
+ notifications.each do |notification|
14
+ if (mail = RexMailNotification.find_by(name: notification[:name]))
15
+ mail.attributes = notification
16
+ mail.save! if mail.changed?
17
+ else
18
+ created_notification = RexMailNotification.create(notification)
19
+ if created_notification.nil? || created_notification.errors.any?
20
+ raise ::Foreman::Exception.new(N_("Unable to create mail notification: %s"),
21
+ format_errors(created_notification))
22
+ end
23
+ end
24
+ end
@@ -16,8 +16,7 @@ Gem::Specification.new do |s|
16
16
  'management functionality with remote management functionality.'
17
17
 
18
18
  s.files = `git ls-files`.split("\n").reject do |file|
19
- file.start_with?('scripts', 'lib/foreman_remote_execution_core') ||
20
- file == 'foreman_remote_execution_core.gemspec'
19
+ file.start_with?('scripts')
21
20
  end
22
21
 
23
22
  s.test_files = `git ls-files test`.split("\n")
@@ -25,8 +24,7 @@ Gem::Specification.new do |s|
25
24
 
26
25
  s.add_dependency 'deface'
27
26
  s.add_dependency 'dynflow', '>= 1.0.2', '< 2.0.0'
28
- s.add_dependency 'foreman_remote_execution_core'
29
- s.add_dependency 'foreman-tasks', '>= 4.1.0'
27
+ s.add_dependency 'foreman-tasks', '>= 5.1.0'
30
28
 
31
29
  s.add_development_dependency 'factory_bot_rails', '~> 4.8.0'
32
30
  s.add_development_dependency 'rdoc'
@@ -19,10 +19,6 @@ module ForemanRemoteExecution
19
19
  end
20
20
  assets_to_precompile += %w(foreman_remote_execution/foreman_remote_execution.css)
21
21
 
22
- initializer 'foreman_remote_execution.load_default_settings', :before => :load_config_initializers do
23
- require_dependency File.expand_path('../../../app/models/setting/remote_execution.rb', __FILE__) if (Setting.table_exists? rescue(false))
24
- end
25
-
26
22
  # Add any db migrations
27
23
  initializer 'foreman_remote_execution.load_app_instance_data' do |app|
28
24
  ForemanRemoteExecution::Engine.paths['db/migrate'].existent.each do |path|
@@ -51,7 +47,7 @@ module ForemanRemoteExecution
51
47
 
52
48
  initializer 'foreman_remote_execution.register_plugin', before: :finisher_hook do |_app|
53
49
  Foreman::Plugin.register :foreman_remote_execution do
54
- requires_foreman '>= 2.2'
50
+ requires_foreman '>= 3.1'
55
51
  register_global_js_file 'global'
56
52
 
57
53
  apipie_documented_controllers ["#{ForemanRemoteExecution::Engine.root}/app/controllers/api/v2/*.rb"]
@@ -61,13 +57,114 @@ module ForemanRemoteExecution
61
57
  automatic_assets(false)
62
58
  precompile_assets(*assets_to_precompile)
63
59
 
60
+ # Add settings to a Remote Execution category
61
+ settings do
62
+ category :remote_execution, N_('Remote Execution') do
63
+ setting 'remote_execution_fallback_proxy',
64
+ type: :boolean,
65
+ description: N_('Search the host for any proxy with Remote Execution, useful when the host has no subnet or the subnet does not have an execution proxy'),
66
+ default: false,
67
+ full_name: N_('Fallback to Any Proxy')
68
+ setting 'remote_execution_global_proxy',
69
+ type: :boolean,
70
+ description: N_('Search for remote execution proxy outside of the proxies assigned to the host. The search will be limited to the host\'s organization and location.'),
71
+ default: true,
72
+ full_name: N_('Enable Global Proxy')
73
+ setting 'remote_execution_ssh_user',
74
+ type: :string,
75
+ description: N_('Default user to use for SSH. You may override per host by setting a parameter called remote_execution_ssh_user.'),
76
+ default: 'root',
77
+ full_name: N_('SSH User')
78
+ setting 'remote_execution_effective_user',
79
+ type: :string,
80
+ description: N_('Default user to use for executing the script. If the user differs from the SSH user, su or sudo is used to switch the user.'),
81
+ default: 'root',
82
+ full_name: N_('Efffective User')
83
+ setting 'remote_execution_effective_user_method',
84
+ type: :string,
85
+ description: N_('What command should be used to switch to the effective user. One of %s') % SSHExecutionProvider::EFFECTIVE_USER_METHODS.inspect,
86
+ default: 'sudo',
87
+ full_name: N_('Effective User Method'),
88
+ collection: proc { Hash[SSHExecutionProvider::EFFECTIVE_USER_METHODS.map { |method| [method, method] }] }
89
+ setting 'remote_execution_effective_user_password',
90
+ type: :string,
91
+ description: N_('Effective user password'),
92
+ default: '',
93
+ full_name: N_('Effective user password'),
94
+ encrypted: true
95
+ setting 'remote_execution_sync_templates',
96
+ type: :boolean,
97
+ description: N_('Whether we should sync templates from disk when running db:seed.'),
98
+ default: true,
99
+ full_name: N_('Sync Job Templates')
100
+ setting 'remote_execution_ssh_port',
101
+ type: :integer,
102
+ description: N_('Port to use for SSH communication. Default port 22. You may override per host by setting a parameter called remote_execution_ssh_port.'),
103
+ default: 22,
104
+ full_name: N_('SSH Port')
105
+ setting 'remote_execution_connect_by_ip',
106
+ type: :boolean,
107
+ description: N_('Should the ip addresses on host interfaces be preferred over the fqdn? '\
108
+ 'It is useful when DNS not resolving the fqdns properly. You may override this per host by setting a parameter called remote_execution_connect_by_ip. '\
109
+ 'For dual-stacked hosts you should consider the remote_execution_connect_by_ip_prefer_ipv6 setting'),
110
+ default: false,
111
+ full_name: N_('Connect by IP')
112
+ setting 'remote_execution_connect_by_ip_prefer_ipv6',
113
+ type: :boolean,
114
+ description: N_('When connecting using ip address, should the IPv6 addresses be preferred? '\
115
+ 'If no IPv6 address is set, it falls back to IPv4 automatically. You may override this per host by setting a parameter called remote_execution_connect_by_ip_prefer_ipv6. '\
116
+ 'By default and for compatibility, IPv4 will be preferred over IPv6 by default'),
117
+ default: false,
118
+ full_name: N_('Prefer IPv6 over IPv4')
119
+ setting 'remote_execution_ssh_password',
120
+ type: :string,
121
+ description: N_('Default password to use for SSH. You may override per host by setting a parameter called remote_execution_ssh_password'),
122
+ default: nil,
123
+ full_name: N_('Default SSH password'),
124
+ encrypted: true
125
+ setting 'remote_execution_ssh_key_passphrase',
126
+ type: :string,
127
+ description: N_('Default key passphrase to use for SSH. You may override per host by setting a parameter called remote_execution_ssh_key_passphrase'),
128
+ default: nil,
129
+ full_name: N_('Default SSH key passphrase'),
130
+ encrypted: true
131
+ setting 'remote_execution_workers_pool_size',
132
+ type: :integer,
133
+ description: N_('Amount of workers in the pool to handle the execution of the remote execution jobs. Restart of the dynflowd/foreman-tasks service is required.'),
134
+ default: 5,
135
+ full_name: N_('Workers pool size')
136
+ setting 'remote_execution_cleanup_working_dirs',
137
+ type: :boolean,
138
+ description: N_('When enabled, working directories will be removed after task completion. You may override this per host by setting a parameter called remote_execution_cleanup_working_dirs.'),
139
+ default: true,
140
+ full_name: N_('Cleanup working directories')
141
+ setting 'remote_execution_cockpit_url',
142
+ type: :string,
143
+ description: N_('Where to find the Cockpit instance for the Web Console button. By default, no button is shown.'),
144
+ default: nil,
145
+ full_name: N_('Cockpit URL')
146
+ setting 'remote_execution_form_job_template',
147
+ type: :string,
148
+ description: N_('Choose a job template that is pre-selected in job invocation form'),
149
+ default: 'Run Command - SSH Default',
150
+ full_name: N_('Form Job Template'),
151
+ collection: proc { Hash[JobTemplate.unscoped.map { |template| [template.name, template.name] }] }
152
+ setting 'remote_execution_job_invocation_report_template',
153
+ type: :string,
154
+ description: N_('Select a report template used for generating a report for a particular remote execution job'),
155
+ default: 'Jobs - Invocation report template',
156
+ full_name: N_('Job Invocation Report Template'),
157
+ collection: proc { ForemanRemoteExecution.job_invocation_report_templates_select }
158
+ end
159
+ end
160
+
64
161
  # Add permissions
65
162
  security_block :foreman_remote_execution do
66
163
  permission :view_job_templates, { :job_templates => [:index, :show, :revision, :auto_complete_search, :auto_complete_job_category, :preview, :export],
67
164
  :'api/v2/job_templates' => [:index, :show, :revision, :export],
68
165
  :'api/v2/template_inputs' => [:index, :show],
69
166
  :'api/v2/foreign_input_sets' => [:index, :show],
70
- :ui_job_wizard => [:categories, :template]}, :resource_type => 'JobTemplate'
167
+ :ui_job_wizard => [:categories, :template, :resources]}, :resource_type => 'JobTemplate'
71
168
  permission :create_job_templates, { :job_templates => [:new, :create, :clone_template, :import],
72
169
  :'api/v2/job_templates' => [:create, :clone, :import] }, :resource_type => 'JobTemplate'
73
170
  permission :edit_job_templates, { :job_templates => [:edit, :update],
@@ -86,6 +183,7 @@ module ForemanRemoteExecution
86
183
  permission :view_template_invocations, { :template_invocations => [:show],
87
184
  'api/v2/template_invocations' => [:template_invocations] }, :resource_type => 'TemplateInvocation'
88
185
  permission :create_template_invocations, {}, :resource_type => 'TemplateInvocation'
186
+ permission :execute_jobs_on_infrastructure_hosts, {}, :resource_type => 'JobInvocation'
89
187
  permission :cancel_job_invocations, { :job_invocations => [:cancel], 'api/v2/job_invocations' => [:cancel] }, :resource_type => 'JobInvocation'
90
188
  # this permissions grants user to get auto completion hints when setting up filters
91
189
  permission :filter_autocompletion_for_template_invocation, { :template_invocations => [ :auto_complete_search, :index ] },
@@ -146,12 +244,18 @@ module ForemanRemoteExecution
146
244
  register_custom_status HostStatus::ExecutionStatus
147
245
  # add dashboard widget
148
246
  # widget 'foreman_remote_execution_widget', name: N_('Foreman plugin template widget'), sizex: 4, sizey: 1
247
+ widget 'dashboard/latest-jobs', :name => N_('Latest Jobs'), :sizex => 6, :sizey => 1
149
248
 
150
249
  parameter_filter Subnet, :remote_execution_proxies, :remote_execution_proxy_ids => []
151
250
  parameter_filter Nic::Interface do |ctx|
152
251
  ctx.permit :execution
153
252
  end
154
253
 
254
+ register_graphql_query_field :job_invocations, '::Types::JobInvocation', :collection_field
255
+ register_graphql_query_field :job_invocation, '::Types::JobInvocation', :record_field
256
+
257
+ register_graphql_mutation_field :create_job_invocation, ::Mutations::JobInvocations::Create
258
+
155
259
  extend_template_helpers ForemanRemoteExecution::RendererMethods
156
260
 
157
261
  extend_rabl_template 'api/v2/smart_proxies/main', 'api/v2/smart_proxies/pubkey'
@@ -237,6 +341,10 @@ module ForemanRemoteExecution
237
341
  end
238
342
  end
239
343
 
344
+ def self.job_invocation_report_templates_select
345
+ Hash[ReportTemplate.unscoped.joins(:template_inputs).where(template_inputs: TemplateInput.where(name: 'job_id')).map { |template| [template.name, template.name] }]
346
+ end
347
+
240
348
  def self.register_rex_feature
241
349
  RemoteExecutionFeature.register(
242
350
  :puppet_run_host,
@@ -1,3 +1,3 @@
1
1
  module ForemanRemoteExecution
2
- VERSION = '4.5.6'.freeze
2
+ VERSION = '5.0.0'.freeze
3
3
  end
data/package.json CHANGED
@@ -21,11 +21,11 @@
21
21
  },
22
22
  "devDependencies": {
23
23
  "@babel/core": "^7.7.0",
24
- "@theforeman/builder": "^4.14.0",
25
- "@theforeman/eslint-plugin-foreman": "^4.14.0",
26
- "@theforeman/stories": "^4.14.0",
27
- "@theforeman/test": "^4.14.0",
28
- "@theforeman/vendor-dev": "^4.14.0",
24
+ "@theforeman/builder": "^8.16.0",
25
+ "@theforeman/eslint-plugin-foreman": "^8.16.0",
26
+ "@theforeman/stories": "^8.16.0",
27
+ "@theforeman/test": "^8.16.0",
28
+ "@theforeman/vendor-dev": "^8.16.0",
29
29
  "babel-eslint": "^10.0.0",
30
30
  "eslint": "^6.8.0",
31
31
  "prettier": "^1.19.1",
@@ -33,6 +33,6 @@
33
33
  "redux-mock-store": "^1.2.2"
34
34
  },
35
35
  "peerDependencies": {
36
- "@theforeman/vendor": "^8.3.0"
36
+ "@theforeman/vendor": "^8.16.0"
37
37
  }
38
38
  }
@@ -90,6 +90,16 @@ module Api
90
90
  assert_response :success
91
91
  end
92
92
 
93
+ test 'should create with a scheduled recurrence' do
94
+ @attrs[:scheduling] = { start_at: (Time.now + 1.hour) }
95
+ @attrs[:recurrence] = { cron_line: '5 * * * *' }
96
+ post :create, params: { job_invocation: @attrs }
97
+ invocation = ActiveSupport::JSON.decode(@response.body)
98
+ assert_equal 'recurring', invocation['mode']
99
+ assert invocation['start_at']
100
+ assert_response :success
101
+ end
102
+
93
103
  context 'with_feature' do
94
104
  setup do
95
105
  @feature = FactoryBot.create(:remote_execution_feature,
@@ -181,6 +191,11 @@ module Api
181
191
  host_id: FactoryBot.create(:host).id }
182
192
  assert_response :missing
183
193
  end
194
+
195
+ test 'should not break when taxonomy parameters are provided' do
196
+ get :output, params: { :job_invocation_id => @invocation.id, :host_id => host.id, :organization_id => host.organization_id, :location_id => host.location_id }
197
+ assert_response :success
198
+ end
184
199
  end
185
200
 
186
201
  describe '#outputs' do
@@ -233,6 +248,11 @@ module Api
233
248
  assert_response :success
234
249
  end
235
250
 
251
+ test 'should not break when taxonomy parameters are provided' do
252
+ get :raw_output, params: { :job_invocation_id => @invocation.id, :host_id => host.id, :organization_id => host.organization_id, :location_id => host.location_id }
253
+ assert_response :success
254
+ end
255
+
236
256
  test 'should provide raw output for delayed task' do
237
257
  start_time = Time.now
238
258
  JobInvocation.any_instance
@@ -2,7 +2,6 @@ require 'test_plugin_helper'
2
2
 
3
3
  class CockpitControllerTest < ActionController::TestCase
4
4
  def setup
5
- Setting::RemoteExecution.load_defaults
6
5
  as_admin do
7
6
  @host = FactoryBot.create(:host)
8
7
  end
@@ -0,0 +1,58 @@
1
+ require 'test_plugin_helper'
2
+
3
+ module Mutations
4
+ module JobInvocations
5
+ class CreateMutationTest < ActiveSupport::TestCase
6
+ let(:host) { FactoryBot.create(:host) }
7
+ let(:job_template) { FactoryBot.create(:job_template, :with_input) }
8
+ let(:cron_line) { '5 * * * *' }
9
+ let(:purpose) { 'test' }
10
+ let(:variables) do
11
+ {
12
+ jobInvocation: {
13
+ hostIds: [host.id],
14
+ jobTemplateId: job_template.id,
15
+ targetingType: 'static_query',
16
+ inputs: { job_template.template_inputs.first.name => "bar" },
17
+ recurrence: {
18
+ cronLine: cron_line,
19
+ purpose: purpose,
20
+ },
21
+ },
22
+ }
23
+ end
24
+
25
+ let(:query) do
26
+ <<-GRAPHQL
27
+ mutation CreateJobInvocation($jobInvocation: JobInvocationInput!) {
28
+ createJobInvocation(input: { jobInvocation: $jobInvocation }) {
29
+ jobInvocation {
30
+ id
31
+ description
32
+ recurringLogic {
33
+ cronLine
34
+ purpose
35
+ }
36
+ }
37
+ }
38
+ }
39
+ GRAPHQL
40
+ end
41
+
42
+ context 'with admin user' do
43
+ let(:user) { FactoryBot.create(:user, :admin) }
44
+ let(:context) { { current_user: user } }
45
+
46
+ test 'create a job invocation' do
47
+ assert_difference('JobInvocation.count', +1) do
48
+ result = ForemanGraphqlSchema.execute(query, variables: variables, context: context)
49
+ assert_empty result['errors']
50
+ assert_empty result['data']['createJobInvocation']['jobInvocation']['errors']
51
+ assert_equal cron_line, result['data']['createJobInvocation']['jobInvocation']['recurringLogic']['cronLine']
52
+ assert_equal purpose, result['data']['createJobInvocation']['jobInvocation']['recurringLogic']['purpose']
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,31 @@
1
+ require 'test_helper'
2
+
3
+ module Queries
4
+ class JobInvocationQueryTest < GraphQLQueryTestCase
5
+ let(:query) do
6
+ <<-GRAPHQL
7
+ query (
8
+ $id: String!
9
+ ) {
10
+ jobInvocation(id: $id) {
11
+ id
12
+ jobCategory
13
+ }
14
+ }
15
+ GRAPHQL
16
+ end
17
+
18
+ let(:job_invocation) { FactoryBot.create(:job_invocation) }
19
+
20
+ let(:global_id) { Foreman::GlobalId.for(job_invocation) }
21
+ let(:variables) { { id: global_id } }
22
+ let(:data) { result['data']['jobInvocation'] }
23
+
24
+ test 'fetching job invocation attributes' do
25
+ assert_empty result['errors']
26
+
27
+ assert_equal global_id, data['id']
28
+ assert_equal job_invocation.job_category, data['jobCategory']
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,35 @@
1
+ require 'test_plugin_helper'
2
+
3
+ module Queries
4
+ class JobInvocationsQueryTest < GraphQLQueryTestCase
5
+ let(:query) do
6
+ <<-GRAPHQL
7
+ query {
8
+ jobInvocations {
9
+ totalCount
10
+ nodes {
11
+ id
12
+ jobCategory
13
+ }
14
+ }
15
+ }
16
+ GRAPHQL
17
+ end
18
+
19
+ let(:data) { result['data']['jobInvocations'] }
20
+
21
+ setup do
22
+ FactoryBot.create_list(:job_invocation, 2)
23
+ end
24
+
25
+ test 'should fetch job invocations' do
26
+ assert_empty result['errors']
27
+
28
+ expected_count = JobInvocation.count
29
+
30
+ assert_not_equal 0, expected_count
31
+ assert_equal expected_count, data['totalCount']
32
+ assert_equal expected_count, data['nodes'].count
33
+ end
34
+ end
35
+ end
@@ -26,7 +26,6 @@ class RemoteExecutionHelperTest < ActionView::TestCase
26
26
 
27
27
  describe 'test correct setting' do
28
28
  it 'should found correct template from setting' do
29
- Setting::RemoteExecution.load_defaults
30
29
  template_name = 'Job Invocation Report Template'
31
30
  setting_key = 'remote_execution_job_invocation_report_template'
32
31
  template = FactoryBot.create(:report_template, name: template_name)
@@ -36,6 +36,27 @@ module ForemanRemoteExecution
36
36
  end
37
37
  end
38
38
 
39
+ describe '#verify_permission' do
40
+ let(:job_invocation) { FactoryBot.create(:job_invocation, :with_task) }
41
+ let(:template_invocation) { job_invocation.template_invocations.first }
42
+
43
+ before { job_invocation }
44
+
45
+ it 'raises an exception when run against an infrastructure host' do
46
+ template_invocation.host = FactoryBot.create(:host, :with_infrastructure_facet)
47
+
48
+ setup_user('view', 'hosts')
49
+ setup_user('view', 'job_templates')
50
+ setup_user('create', 'template_invocations')
51
+
52
+ action = Actions::RemoteExecution::RunHostJob.allocate
53
+ exception = assert_raises do
54
+ action.send(:verify_permissions, template_invocation.host, template_invocation)
55
+ end
56
+ _(exception.message).must_include "infrastructure host #{template_invocation.host.name}"
57
+ end
58
+ end
59
+
39
60
  describe '#finalize' do
40
61
  let(:host) { FactoryBot.create(:host, :with_execution) }
41
62
 
@@ -5,6 +5,16 @@ module ForemanRemoteExecution
5
5
  class RunHostsJobTest < ActiveSupport::TestCase
6
6
  include Dynflow::Testing
7
7
 
8
+ # Adding run_step_id wich is needed in RunHostsJob as a quick fix
9
+ # it will be added to dynflow in the future see https://github.com/Dynflow/dynflow/pull/391
10
+ # rubocop:disable Style/ClassAndModuleChildren
11
+ class Dynflow::Testing::DummyPlannedAction
12
+ def run_step_id
13
+ Dynflow::Testing.get_id
14
+ end
15
+ end
16
+ # rubocop:enable Style/ClassAndModuleChildren
17
+
8
18
  let(:host) { FactoryBot.create(:host, :with_execution) }
9
19
  let(:proxy) { host.remote_execution_proxies('SSH')[:subnet].first }
10
20
  let(:targeting) { FactoryBot.create(:targeting, :search_query => "name = #{host.name}", :user => User.current) }
@@ -94,6 +104,22 @@ module ForemanRemoteExecution
94
104
  planned # To make the expectations happy
95
105
  end
96
106
 
107
+ describe '#proxy_batch_size' do
108
+ it 'defaults to Setting[foreman_tasks_proxy_batch_size]' do
109
+ Setting.expects(:[]).with('foreman_tasks_proxy_batch_size').returns(14)
110
+ planned
111
+ _(planned.proxy_batch_size).must_equal 14
112
+ end
113
+
114
+ it 'gets the provider value' do
115
+ provider = mock('provider')
116
+ provider.expects(:proxy_batch_size).returns(15)
117
+ JobTemplate.any_instance.expects(:provider).returns(provider)
118
+
119
+ _(planned.proxy_batch_size).must_equal 15
120
+ end
121
+ end
122
+
97
123
  describe 'concurrency control' do
98
124
  let(:level) { 5 }
99
125
  let(:span) { 60 }
@@ -127,10 +153,79 @@ module ForemanRemoteExecution
127
153
  end
128
154
 
129
155
  describe 'notifications' do
130
- it 'creates notification on sucess run' do
131
- FactoryBot.create(:notification_blueprint, :name => 'rex_job_succeeded')
132
- assert_difference 'NotificationRecipient.where(:user_id => targeting.user.id).count' do
133
- finalize_action planned
156
+ it 'creates drawer notification on succeess' do
157
+ blueprint = planned.job_invocation.build_notification
158
+ blueprint.expects(:deliver!)
159
+ planned.job_invocation.expects(:build_notification).returns(blueprint)
160
+ planned.notify_on_success(nil)
161
+ end
162
+
163
+ it 'creates drawer notification on failure' do
164
+ blueprint = planned.job_invocation.build_notification
165
+ blueprint.expects(:deliver!)
166
+ planned.job_invocation.expects(:build_notification).returns(blueprint)
167
+ planned.notify_on_failure(nil)
168
+ end
169
+
170
+ describe 'ignoring drawer notification' do
171
+ before do
172
+ blueprint = planned.job_invocation.build_notification
173
+ blueprint.expects(:deliver!)
174
+ planned.job_invocation.expects(:build_notification).returns(blueprint)
175
+ end
176
+
177
+ let(:mail) do
178
+ object = mock
179
+ object.stubs(:deliver_now)
180
+ object
181
+ end
182
+
183
+ describe 'for user subscribed to all' do
184
+ before do
185
+ planned.expects(:mail_notification_preference).returns(UserMailNotification.new(:interval => RexMailNotification::ALL_JOBS))
186
+ end
187
+
188
+ it 'sends the mail notification on success' do
189
+ RexJobMailer.expects(:job_finished).returns(mail)
190
+ planned.notify_on_success(nil)
191
+ end
192
+
193
+ it 'sends the mail notification on failure' do
194
+ RexJobMailer.expects(:job_finished).returns(mail)
195
+ planned.notify_on_failure(nil)
196
+ end
197
+ end
198
+
199
+ describe 'for user subscribed to failures' do
200
+ before do
201
+ planned.expects(:mail_notification_preference).returns(UserMailNotification.new(:interval => RexMailNotification::FAILED_JOBS))
202
+ end
203
+
204
+ it 'it does not send the mail notification on success' do
205
+ RexJobMailer.expects(:job_finished).never
206
+ planned.notify_on_success(nil)
207
+ end
208
+
209
+ it 'sends the mail notification on failure' do
210
+ RexJobMailer.expects(:job_finished).returns(mail)
211
+ planned.notify_on_failure(nil)
212
+ end
213
+ end
214
+
215
+ describe 'for user subscribed to successful jobs' do
216
+ before do
217
+ planned.expects(:mail_notification_preference).returns(UserMailNotification.new(:interval => RexMailNotification::SUCCEEDED_JOBS))
218
+ end
219
+
220
+ it 'sends the mail notification on success' do
221
+ RexJobMailer.expects(:job_finished).returns(mail)
222
+ planned.notify_on_success(nil)
223
+ end
224
+
225
+ it 'does not send the mail notification on failure' do
226
+ RexJobMailer.expects(:job_finished).never
227
+ planned.notify_on_failure(nil)
228
+ end
134
229
  end
135
230
  end
136
231
  end