foreman_remote_execution 6.0.0 → 6.1.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.
- checksums.yaml +4 -4
- data/app/controllers/foreman_remote_execution/concerns/api/v2/registration_controller_extensions.rb +5 -0
- data/app/controllers/ui_job_wizard_controller.rb +1 -1
- data/app/helpers/remote_execution_helper.rb +4 -3
- data/app/lib/actions/remote_execution/run_host_job.rb +1 -1
- data/app/models/concerns/foreman_remote_execution/host_extensions.rb +4 -0
- data/app/models/concerns/foreman_remote_execution/nic_extensions.rb +6 -4
- data/app/models/remote_execution_provider.rb +10 -1
- data/app/models/ssh_execution_provider.rb +8 -2
- data/app/views/api/v2/host/main.rabl +1 -0
- data/app/views/dashboard/_latest-jobs.html.erb +1 -1
- data/app/views/templates/{ssh → script}/check_update.erb +2 -2
- data/app/views/templates/{ssh → script}/module_action.erb +2 -2
- data/app/views/templates/{ssh → script}/package_action.erb +5 -2
- data/app/views/templates/{ssh → script}/power_action.erb +2 -2
- data/app/views/templates/{ssh → script}/puppet_agent_disable.erb +2 -2
- data/app/views/templates/{ssh → script}/puppet_agent_enable.erb +2 -2
- data/app/views/templates/{ssh → script}/puppet_install_modules_from_forge.erb +2 -2
- data/app/views/templates/{ssh → script}/puppet_install_modules_from_git.erb +2 -2
- data/app/views/templates/{ssh → script}/puppet_run_once.erb +2 -2
- data/app/views/templates/{ssh → script}/run_command.erb +2 -2
- data/app/views/templates/{ssh → script}/service_action.erb +2 -2
- data/db/migrate/20220321101835_rename_ssh_provider_to_script.rb +29 -0
- data/jsconfig.json +8 -0
- data/lib/foreman_remote_execution/engine.rb +4 -2
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/locale/action_names.rb +1 -1
- data/package.json +6 -7
- data/test/unit/concerns/host_extensions_test.rb +2 -1
- data/test/unit/remote_execution_provider_test.rb +2 -0
- data/webpack/JobWizard/JobWizard.js +8 -2
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.js +24 -15
- data/webpack/JobWizard/steps/form/GroupedSelectField.js +7 -1
- data/webpack/global_index.js +4 -0
- data/webpack/react_app/components/FeaturesDropdown/actions.js +13 -0
- data/webpack/react_app/components/FeaturesDropdown/constant.js +2 -0
- data/webpack/react_app/components/FeaturesDropdown/index.js +74 -0
- data/webpack/react_app/components/HostKebab/KebabItems.js +22 -0
- data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +6 -1
- data/webpack/react_app/components/RecentJobsCard/RecentJobsTable.js +2 -2
- data/webpack/react_app/components/RecentJobsCard/constants.js +1 -0
- data/webpack/react_app/extend/fillKebabItems.js +11 -0
- data/webpack/react_app/extend/fillRexFeaturesDropdown.js +11 -0
- metadata +23 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 910d4fd508188efbad4cfb121085171d077d65881e7ef8b5c9f1dea4092aa182
|
4
|
+
data.tar.gz: af7a5ca195d2b0d6ba269962bb3d726cd19dd6cc23358e6e6dc21cc77b36b3b2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 030ed44dd17a6da7b334f3c3621d34cec9d4afc6323f2cbacd1054716520dc4f955f5da4d0aa5bfb93b0f83699c6dd15a3d75a9ff770fa1b9c5d6df2afbba440
|
7
|
+
data.tar.gz: 80d41c53f5e1620919069e08a97b23d22d884406275ad426b49ee08c97dc92c958762fe79ff5bb703b63859f1551fb3cfad838f1b63e75cbc143983dc864e7fd
|
data/app/controllers/foreman_remote_execution/concerns/api/v2/registration_controller_extensions.rb
CHANGED
@@ -13,6 +13,7 @@ module ForemanRemoteExecution
|
|
13
13
|
|
14
14
|
def host_setup_extension
|
15
15
|
remote_execution_interface
|
16
|
+
reset_host_known_keys! unless @host.new_record?
|
16
17
|
super
|
17
18
|
end
|
18
19
|
|
@@ -21,6 +22,10 @@ module ForemanRemoteExecution
|
|
21
22
|
|
22
23
|
@host.set_execution_interface(params['remote_execution_interface'])
|
23
24
|
end
|
25
|
+
|
26
|
+
def reset_host_known_keys!
|
27
|
+
@host.host_proxy_invocations.destroy_all
|
28
|
+
end
|
24
29
|
end
|
25
30
|
end
|
26
31
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module RemoteExecutionHelper
|
2
2
|
def providers_options
|
3
|
-
RemoteExecutionProvider.providers.map { |key, provider| [ key, _(provider.humanized_name) ] }
|
3
|
+
RemoteExecutionProvider.providers.reject { |key, _provider| key == 'SSH' }.map { |key, provider| [ key, _(provider.humanized_name) ] }
|
4
4
|
end
|
5
5
|
|
6
6
|
def job_hosts_authorizer
|
@@ -147,9 +147,10 @@ module RemoteExecutionHelper
|
|
147
147
|
end
|
148
148
|
end
|
149
149
|
|
150
|
-
def invocation_description(invocation)
|
150
|
+
def invocation_description(invocation, keep_tooltip: true)
|
151
151
|
description = invocation.description.try(:capitalize) || invocation.job_category
|
152
|
-
trunc_with_tooltip(description, 80)
|
152
|
+
description = trunc_with_tooltip(description, 80) if keep_tooltip
|
153
|
+
description
|
153
154
|
end
|
154
155
|
|
155
156
|
def invocation_result(invocation, key)
|
@@ -40,7 +40,7 @@ module Actions
|
|
40
40
|
provider = template_invocation.template.provider
|
41
41
|
proxy_selector = provider.required_proxy_selector_for(template_invocation.template) || proxy_selector
|
42
42
|
|
43
|
-
provider_type =
|
43
|
+
provider_type = provider.proxy_feature
|
44
44
|
proxy = determine_proxy!(proxy_selector, provider_type, host)
|
45
45
|
link!(proxy)
|
46
46
|
input[:proxy_id] = proxy.id
|
@@ -46,6 +46,10 @@ module ForemanRemoteExecution
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
+
def cockpit_url
|
50
|
+
SSHExecutionProvider.cockpit_url_for_host(self.name)
|
51
|
+
end
|
52
|
+
|
49
53
|
def execution_status(options = {})
|
50
54
|
@execution_status ||= get_status(HostStatus::ExecutionStatus).to_status(options)
|
51
55
|
end
|
@@ -3,16 +3,18 @@ module ForemanRemoteExecution
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
included do
|
6
|
-
before_validation :set_execution_flag
|
7
6
|
validate :exclusive_execution_interface
|
7
|
+
before_validation :move_execution_flag
|
8
8
|
end
|
9
9
|
|
10
10
|
private
|
11
11
|
|
12
|
-
def
|
13
|
-
return unless
|
12
|
+
def move_execution_flag
|
13
|
+
return unless host && self.execution?
|
14
14
|
|
15
|
-
|
15
|
+
host.interfaces
|
16
|
+
.select { |i| i.execution? && i != self }
|
17
|
+
.each { |i| i.execution = false }
|
16
18
|
end
|
17
19
|
|
18
20
|
def exclusive_execution_interface
|
@@ -4,7 +4,16 @@ class RemoteExecutionProvider
|
|
4
4
|
|
5
5
|
class << self
|
6
6
|
def provider_for(type)
|
7
|
-
providers[type.to_s] || providers[:
|
7
|
+
providers[type.to_s] || providers[:script]
|
8
|
+
end
|
9
|
+
|
10
|
+
def registered_name
|
11
|
+
klass = self
|
12
|
+
providers.key(klass)
|
13
|
+
end
|
14
|
+
|
15
|
+
def proxy_feature
|
16
|
+
registered_name
|
8
17
|
end
|
9
18
|
|
10
19
|
def providers
|
@@ -1,4 +1,4 @@
|
|
1
|
-
class
|
1
|
+
class ScriptExecutionProvider < RemoteExecutionProvider
|
2
2
|
class << self
|
3
3
|
def proxy_command_options(template_invocation, host)
|
4
4
|
super.merge(:ssh_user => ssh_user(host),
|
@@ -9,7 +9,7 @@ class SSHExecutionProvider < RemoteExecutionProvider
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def humanized_name
|
12
|
-
_('
|
12
|
+
_('Script')
|
13
13
|
end
|
14
14
|
|
15
15
|
def supports_effective_user?
|
@@ -53,6 +53,10 @@ class SSHExecutionProvider < RemoteExecutionProvider
|
|
53
53
|
Setting[:remote_execution_cockpit_url] % { :host => host } if Setting[:remote_execution_cockpit_url].present?
|
54
54
|
end
|
55
55
|
|
56
|
+
def proxy_feature
|
57
|
+
'SSH'
|
58
|
+
end
|
59
|
+
|
56
60
|
private
|
57
61
|
|
58
62
|
def ssh_user(host)
|
@@ -64,3 +68,5 @@ class SSHExecutionProvider < RemoteExecutionProvider
|
|
64
68
|
end
|
65
69
|
end
|
66
70
|
end
|
71
|
+
|
72
|
+
SSHExecutionProvider = ScriptExecutionProvider
|
@@ -0,0 +1 @@
|
|
1
|
+
attributes :cockpit_url
|
@@ -10,7 +10,7 @@
|
|
10
10
|
</tr>
|
11
11
|
<% JobInvocation.latest_jobs.each do |invocation| %>
|
12
12
|
<tr>
|
13
|
-
<td class="ellipsis"><%= link_to_if_authorized invocation_description(invocation), hash_for_job_invocation_path(invocation).merge(:auth_object => invocation, :permission => :view_job_invocations, :authorizer => authorizer) %></td>
|
13
|
+
<td class="ellipsis"><%= link_to_if_authorized invocation_description(invocation, keep_tooltip: false), hash_for_job_invocation_path(invocation).merge(:auth_object => invocation, :permission => :view_job_invocations, :authorizer => authorizer) %></td>
|
14
14
|
<td><%= link_to_invocation_task_if_authorized(invocation) %></td>
|
15
15
|
<td><%= time_in_words_span(invocation.start_at) %></td>
|
16
16
|
</tr>
|
@@ -1,10 +1,10 @@
|
|
1
1
|
<%#
|
2
2
|
kind: job_template
|
3
|
-
name: Module Action -
|
3
|
+
name: Module Action - Script Default
|
4
4
|
model: JobTemplate
|
5
5
|
job_category: Modules
|
6
6
|
description_format: "Module %{action} %{module_spec}"
|
7
|
-
provider_type:
|
7
|
+
provider_type: script
|
8
8
|
template_inputs:
|
9
9
|
- name: pre_script
|
10
10
|
description: A script to run prior to the module action
|
@@ -1,10 +1,10 @@
|
|
1
1
|
<%#
|
2
2
|
kind: job_template
|
3
|
-
name: Package Action -
|
3
|
+
name: Package Action - Script Default
|
4
4
|
model: JobTemplate
|
5
5
|
job_category: Packages
|
6
6
|
description_format: "%{action} package(s) %{package}"
|
7
|
-
provider_type:
|
7
|
+
provider_type: script
|
8
8
|
template_inputs:
|
9
9
|
- name: pre_script
|
10
10
|
description: A script to run prior to the package action
|
@@ -69,8 +69,11 @@ handle_zypp_res_codes () {
|
|
69
69
|
|
70
70
|
if [ "${ZYPP_RES_CODES[$RETVAL]}" != "" ]; then
|
71
71
|
echo ${ZYPP_RES_CODES[$RETVAL]}
|
72
|
+
fi
|
73
|
+
if [[ $RETVAL -ge 100 && $RETVAL -le 103 ]]; then
|
72
74
|
RETVAL=0
|
73
75
|
fi
|
76
|
+
|
74
77
|
return $RETVAL
|
75
78
|
}
|
76
79
|
<% end -%>
|
@@ -1,10 +1,10 @@
|
|
1
1
|
<%#
|
2
2
|
kind: job_template
|
3
|
-
name: Power Action -
|
3
|
+
name: Power Action - Script Default
|
4
4
|
model: JobTemplate
|
5
5
|
job_category: Power
|
6
6
|
description_format: '%{action} host'
|
7
|
-
provider_type:
|
7
|
+
provider_type: script
|
8
8
|
template_inputs:
|
9
9
|
- name: action
|
10
10
|
description: Action to perform on the service
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<%#
|
2
|
-
name: Puppet Agent Disable -
|
2
|
+
name: Puppet Agent Disable - Script Default
|
3
3
|
model: JobTemplate
|
4
4
|
job_category: Puppet
|
5
5
|
description_format: Disable Puppet agent
|
@@ -10,7 +10,7 @@ template_inputs:
|
|
10
10
|
input_type: user
|
11
11
|
description: Reason for disabling the Puppet agent
|
12
12
|
advanced: false
|
13
|
-
provider_type:
|
13
|
+
provider_type: script
|
14
14
|
kind: job_template
|
15
15
|
-%>
|
16
16
|
<% if @host.operatingsystem.family == 'Debian' -%>
|
@@ -1,10 +1,10 @@
|
|
1
1
|
<%#
|
2
|
-
name: Puppet Agent Enable -
|
2
|
+
name: Puppet Agent Enable - Script Default
|
3
3
|
model: JobTemplate
|
4
4
|
job_category: Puppet
|
5
5
|
description_format: Enable Puppet agent
|
6
6
|
snippet: false
|
7
|
-
provider_type:
|
7
|
+
provider_type: script
|
8
8
|
kind: job_template
|
9
9
|
-%>
|
10
10
|
<% if @host.operatingsystem.family == 'Debian' -%>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<%#
|
2
|
-
name: Puppet Module - Install from forge -
|
2
|
+
name: Puppet Module - Install from forge - Script Default
|
3
3
|
model: JobTemplate
|
4
4
|
job_category: Puppet
|
5
5
|
description_format: Install Puppet Module "%{puppet_module}" from forge
|
@@ -30,7 +30,7 @@ template_inputs:
|
|
30
30
|
input_type: user
|
31
31
|
description: Do not attempt to install dependencies. Type "true" to ignore dependencies.
|
32
32
|
advanced: true
|
33
|
-
provider_type:
|
33
|
+
provider_type: script
|
34
34
|
kind: job_template
|
35
35
|
-%>
|
36
36
|
<% if @host.operatingsystem.family == 'Debian' -%>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<%#
|
2
|
-
name: Puppet Module - Install from git -
|
2
|
+
name: Puppet Module - Install from git - Script Default
|
3
3
|
model: JobTemplate
|
4
4
|
job_category: Puppet
|
5
5
|
description_format: Install Puppet Module "%{puppet_module}" from git
|
@@ -15,7 +15,7 @@ template_inputs:
|
|
15
15
|
input_type: user
|
16
16
|
description: For example, '/etc/puppetlabs/code/environments/production/modules/puppet'.
|
17
17
|
advanced: false
|
18
|
-
provider_type:
|
18
|
+
provider_type: script
|
19
19
|
kind: job_template
|
20
20
|
-%>
|
21
21
|
git clone <%= input('git_repository') %> <%= input('target_dir') %>
|
@@ -1,10 +1,10 @@
|
|
1
1
|
<%#
|
2
2
|
kind: job_template
|
3
|
-
name: Puppet Run Once -
|
3
|
+
name: Puppet Run Once - Script Default
|
4
4
|
model: JobTemplate
|
5
5
|
job_category: Puppet
|
6
6
|
description_format: 'Run Puppet once with "%{puppet_options}"'
|
7
|
-
provider_type:
|
7
|
+
provider_type: script
|
8
8
|
template_inputs:
|
9
9
|
- name: puppet_options
|
10
10
|
description: Additional options to pass to puppet
|
@@ -1,10 +1,10 @@
|
|
1
1
|
<%#
|
2
2
|
kind: job_template
|
3
|
-
name: Run Command -
|
3
|
+
name: Run Command - Script Default
|
4
4
|
model: JobTemplate
|
5
5
|
job_category: Commands
|
6
6
|
description_format: "Run %{command}"
|
7
|
-
provider_type:
|
7
|
+
provider_type: script
|
8
8
|
template_inputs:
|
9
9
|
- name: command
|
10
10
|
description: Command to run on the host
|
@@ -1,10 +1,10 @@
|
|
1
1
|
<%#
|
2
2
|
kind: job_template
|
3
|
-
name: Service Action -
|
3
|
+
name: Service Action - Script Default
|
4
4
|
model: JobTemplate
|
5
5
|
job_category: Services
|
6
6
|
description_format: '%{action} service %{service}'
|
7
|
-
provider_type:
|
7
|
+
provider_type: script
|
8
8
|
template_inputs:
|
9
9
|
- name: action
|
10
10
|
description: Action to perform on the service
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class RenameSshProviderToScript < ActiveRecord::Migration[6.0]
|
4
|
+
|
5
|
+
def do_change(from, to, from_re, new_label)
|
6
|
+
setting = Setting.find_by(:name => 'remote_execution_form_job_template')
|
7
|
+
default_template = nil
|
8
|
+
|
9
|
+
Template.where(:provider_type => from).each do |t|
|
10
|
+
default_template = t if t.name == setting&.value
|
11
|
+
t.provider_type = to
|
12
|
+
t.name = t.name.gsub(from_re, new_label)
|
13
|
+
t.save!
|
14
|
+
end
|
15
|
+
|
16
|
+
if default_template
|
17
|
+
setting.value = default_template.name
|
18
|
+
setting.save!
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def up
|
23
|
+
do_change 'SSH', 'script', /SSH Default$/, 'Script Default'
|
24
|
+
end
|
25
|
+
|
26
|
+
def down
|
27
|
+
do_change 'script', 'SSH', /Script Default$/, 'SSH Default'
|
28
|
+
end
|
29
|
+
end
|
data/jsconfig.json
ADDED
@@ -146,7 +146,7 @@ module ForemanRemoteExecution
|
|
146
146
|
setting 'remote_execution_form_job_template',
|
147
147
|
type: :string,
|
148
148
|
description: N_('Choose a job template that is pre-selected in job invocation form'),
|
149
|
-
default: 'Run Command -
|
149
|
+
default: 'Run Command - Script Default',
|
150
150
|
full_name: N_('Form Job Template'),
|
151
151
|
collection: proc { Hash[JobTemplate.unscoped.map { |template| [template.name, template.name] }] }
|
152
152
|
setting 'remote_execution_job_invocation_report_template',
|
@@ -265,6 +265,7 @@ module ForemanRemoteExecution
|
|
265
265
|
extend_rabl_template 'api/v2/smart_proxies/main', 'api/v2/smart_proxies/pubkey'
|
266
266
|
extend_rabl_template 'api/v2/interfaces/main', 'api/v2/interfaces/execution_flag'
|
267
267
|
extend_rabl_template 'api/v2/subnets/show', 'api/v2/subnets/remote_execution_proxies'
|
268
|
+
extend_rabl_template 'api/v2/hosts/main', 'api/v2/host/main'
|
268
269
|
parameter_filter ::Subnet, :remote_execution_proxy_ids
|
269
270
|
describe_host { overview_buttons_provider :host_overview_buttons }
|
270
271
|
|
@@ -328,7 +329,8 @@ module ForemanRemoteExecution
|
|
328
329
|
require_dependency 'foreman_tasks/task'
|
329
330
|
ForemanTasks::Task.include ForemanRemoteExecution::ForemanTasksTaskExtensions
|
330
331
|
ForemanTasks::Cleaner.include ForemanRemoteExecution::ForemanTasksCleanerExtensions
|
331
|
-
RemoteExecutionProvider.register(:SSH, SSHExecutionProvider)
|
332
|
+
RemoteExecutionProvider.register(:SSH, ::SSHExecutionProvider)
|
333
|
+
RemoteExecutionProvider.register(:script, ::ScriptExecutionProvider)
|
332
334
|
|
333
335
|
ForemanRemoteExecution.register_rex_feature
|
334
336
|
|
data/locale/action_names.rb
CHANGED
data/package.json
CHANGED
@@ -21,20 +21,19 @@
|
|
21
21
|
},
|
22
22
|
"devDependencies": {
|
23
23
|
"@babel/core": "^7.7.0",
|
24
|
-
"@theforeman/builder": "^
|
25
|
-
"@theforeman/eslint-plugin-foreman": "^
|
26
|
-
"@theforeman/stories": "^
|
27
|
-
"@theforeman/test": "^
|
28
|
-
"@theforeman/vendor-dev": "^
|
24
|
+
"@theforeman/builder": "^10.1.0",
|
25
|
+
"@theforeman/eslint-plugin-foreman": "^10.1.0",
|
26
|
+
"@theforeman/stories": "^10.1.0",
|
27
|
+
"@theforeman/test": "^10.1.0",
|
28
|
+
"@theforeman/vendor-dev": "^10.1.0",
|
29
29
|
"babel-eslint": "^10.0.0",
|
30
30
|
"eslint": "^6.8.0",
|
31
31
|
"prettier": "^1.19.1",
|
32
|
-
"@patternfly/react-catalog-view-extension": "^4.8.126",
|
33
32
|
"redux-mock-store": "^1.2.2",
|
34
33
|
"graphql-tag": "^2.11.0",
|
35
34
|
"graphql": "^15.5.0"
|
36
35
|
},
|
37
36
|
"peerDependencies": {
|
38
|
-
"@theforeman/vendor": "^
|
37
|
+
"@theforeman/vendor": "^10.1.0"
|
39
38
|
}
|
40
39
|
}
|
@@ -67,7 +67,8 @@ class ForemanRemoteExecutionHostExtensionsTest < ActiveSupport::TestCase
|
|
67
67
|
it 'should only have one execution interface' do
|
68
68
|
host.interfaces << FactoryBot.build(:nic_managed)
|
69
69
|
host.interfaces.each { |interface| interface.execution = true }
|
70
|
-
_(host).
|
70
|
+
_(host).must_be :valid?
|
71
|
+
_(host.interfaces.count(&:execution?)).must_equal 1
|
71
72
|
end
|
72
73
|
|
73
74
|
it 'returns the execution interface' do
|
@@ -6,9 +6,11 @@ class RemoteExecutionProviderTest < ActiveSupport::TestCase
|
|
6
6
|
it { _(providers).must_be_kind_of HashWithIndifferentAccess }
|
7
7
|
it 'makes providers accessible using symbol' do
|
8
8
|
_(providers[:SSH]).must_equal SSHExecutionProvider
|
9
|
+
_(providers[:script]).must_equal ScriptExecutionProvider
|
9
10
|
end
|
10
11
|
it 'makes providers accessible using string' do
|
11
12
|
_(providers['SSH']).must_equal SSHExecutionProvider
|
13
|
+
_(providers['script']).must_equal ScriptExecutionProvider
|
12
14
|
end
|
13
15
|
end
|
14
16
|
|
@@ -21,6 +21,7 @@ import {
|
|
21
21
|
selectJobTemplate,
|
22
22
|
selectIsSubmitting,
|
23
23
|
selectRouterSearch,
|
24
|
+
selectIsLoading,
|
24
25
|
} from './JobWizardSelectors';
|
25
26
|
import Schedule from './steps/Schedule/';
|
26
27
|
import HostsAndInputs from './steps/HostsAndInputs/';
|
@@ -130,9 +131,14 @@ export const JobWizard = () => {
|
|
130
131
|
const templateError = !!useSelector(selectTemplateError);
|
131
132
|
const templateResponse = useSelector(selectJobTemplate);
|
132
133
|
const isSubmitting = useSelector(selectIsSubmitting);
|
134
|
+
const isTemplatesLoading = useSelector(state =>
|
135
|
+
selectIsLoading(state, JOB_TEMPLATE)
|
136
|
+
);
|
133
137
|
const isTemplate =
|
134
|
-
!
|
135
|
-
|
138
|
+
!isTemplatesLoading &&
|
139
|
+
!templateError &&
|
140
|
+
!!jobTemplateID &&
|
141
|
+
templateResponse.job_template;
|
136
142
|
const steps = [
|
137
143
|
{
|
138
144
|
name: WIZARD_TITLES.categoryAndTemplate,
|
@@ -1,11 +1,13 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import PropTypes from 'prop-types';
|
3
|
+
import { useSelector } from 'react-redux';
|
3
4
|
import { Text, TextVariants, Form, Alert } from '@patternfly/react-core';
|
4
5
|
import { translate as __ } from 'foremanReact/common/I18n';
|
5
6
|
import { SelectField } from '../form/SelectField';
|
6
7
|
import { GroupedSelectField } from '../form/GroupedSelectField';
|
7
8
|
import { WizardTitle } from '../form/WizardTitle';
|
8
|
-
import { WIZARD_TITLES } from '../../JobWizardConstants';
|
9
|
+
import { WIZARD_TITLES, JOB_TEMPLATES } from '../../JobWizardConstants';
|
10
|
+
import { selectIsLoading } from '../../JobWizardSelectors';
|
9
11
|
|
10
12
|
export const CategoryAndTemplate = ({
|
11
13
|
jobCategories,
|
@@ -17,18 +19,23 @@ export const CategoryAndTemplate = ({
|
|
17
19
|
errors,
|
18
20
|
}) => {
|
19
21
|
const templatesGroups = {};
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
22
|
+
const isTemplatesLoading = useSelector(state =>
|
23
|
+
selectIsLoading(state, JOB_TEMPLATES)
|
24
|
+
);
|
25
|
+
if (!isTemplatesLoading) {
|
26
|
+
jobTemplates.forEach(template => {
|
27
|
+
if (templatesGroups[template.provider_type]?.options)
|
28
|
+
templatesGroups[template.provider_type].options.push({
|
29
|
+
label: template.name,
|
30
|
+
value: template.id,
|
31
|
+
});
|
32
|
+
else
|
33
|
+
templatesGroups[template.provider_type] = {
|
34
|
+
options: [{ label: template.name, value: template.id }],
|
35
|
+
groupLabel: template.provider_type,
|
36
|
+
};
|
37
|
+
});
|
38
|
+
}
|
32
39
|
|
33
40
|
const selectedTemplate = jobTemplates.find(
|
34
41
|
template => template.id === selectedTemplateID
|
@@ -60,8 +67,10 @@ export const CategoryAndTemplate = ({
|
|
60
67
|
fieldId="job_template"
|
61
68
|
groups={Object.values(templatesGroups)}
|
62
69
|
setSelected={setJobTemplate}
|
63
|
-
selected={selectedTemplate}
|
64
|
-
isDisabled={
|
70
|
+
selected={isTemplatesLoading ? [] : selectedTemplate}
|
71
|
+
isDisabled={
|
72
|
+
!!(categoryError || allTemplatesError || isTemplatesLoading)
|
73
|
+
}
|
65
74
|
placeholderText={allTemplatesError ? __('Error') : ''}
|
66
75
|
/>
|
67
76
|
{isError && (
|
@@ -69,6 +69,8 @@ export const GroupedSelectField = ({
|
|
69
69
|
className="without_select2"
|
70
70
|
onClear={onClear}
|
71
71
|
menuAppendTo={() => document.body}
|
72
|
+
aria-labelledby={fieldId}
|
73
|
+
toggleAriaLabel={`${label} toggle`}
|
72
74
|
{...props}
|
73
75
|
>
|
74
76
|
{options}
|
@@ -81,7 +83,11 @@ GroupedSelectField.propTypes = {
|
|
81
83
|
label: PropTypes.string.isRequired,
|
82
84
|
fieldId: PropTypes.string.isRequired,
|
83
85
|
groups: PropTypes.array,
|
84
|
-
selected: PropTypes.oneOfType([
|
86
|
+
selected: PropTypes.oneOfType([
|
87
|
+
PropTypes.string,
|
88
|
+
PropTypes.number,
|
89
|
+
PropTypes.array,
|
90
|
+
]),
|
85
91
|
setSelected: PropTypes.func.isRequired,
|
86
92
|
};
|
87
93
|
|
data/webpack/global_index.js
CHANGED
@@ -2,9 +2,13 @@ import { registerRoutes } from 'foremanReact/routes/RoutingService';
|
|
2
2
|
import routes from './Routes/routes';
|
3
3
|
import fillregistrationAdvanced from './react_app/extend/fillregistrationAdvanced';
|
4
4
|
import fillRecentJobsCard from './react_app/extend/fillRecentJobsCard';
|
5
|
+
import fillFeaturesDropdown from './react_app/extend/fillRexFeaturesDropdown';
|
6
|
+
import fillKebabItems from './react_app/extend/fillKebabItems';
|
5
7
|
import registerReducers from './react_app/extend/reducers';
|
6
8
|
|
7
9
|
registerReducers();
|
8
10
|
registerRoutes('foreman_remote_execution', routes);
|
11
|
+
fillFeaturesDropdown();
|
9
12
|
fillRecentJobsCard();
|
10
13
|
fillregistrationAdvanced();
|
14
|
+
fillKebabItems();
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import { foremanUrl } from 'foremanReact/common/helpers';
|
2
|
+
import { sprintf, translate as __ } from 'foremanReact/common/I18n';
|
3
|
+
import { post } from 'foremanReact/redux/API';
|
4
|
+
|
5
|
+
export const runFeature = (hostId, feature, label) => dispatch => {
|
6
|
+
const url = foremanUrl(
|
7
|
+
`/job_invocations?feature=${feature}&host_ids%5B%5D=${hostId}`
|
8
|
+
);
|
9
|
+
|
10
|
+
const successToast = () => sprintf(__('%s job has been invoked'), label);
|
11
|
+
const errorToast = ({ message }) => message;
|
12
|
+
dispatch(post({ key: feature.toUpperCase(), url, successToast, errorToast }));
|
13
|
+
};
|
@@ -0,0 +1,74 @@
|
|
1
|
+
import PropTypes from 'prop-types';
|
2
|
+
import React, { useState } from 'react';
|
3
|
+
import { useDispatch } from 'react-redux';
|
4
|
+
import {
|
5
|
+
DropdownItem,
|
6
|
+
Dropdown,
|
7
|
+
DropdownToggle,
|
8
|
+
DropdownToggleAction,
|
9
|
+
} from '@patternfly/react-core';
|
10
|
+
import { push } from 'connected-react-router';
|
11
|
+
|
12
|
+
import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
|
13
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
14
|
+
import { foremanUrl } from 'foremanReact/common/helpers';
|
15
|
+
import { STATUS } from 'foremanReact/constants';
|
16
|
+
|
17
|
+
import { REX_FEATURES_API, NEW_JOB_PAGE } from './constant';
|
18
|
+
import { runFeature } from './actions';
|
19
|
+
|
20
|
+
const FeaturesDropdown = ({ hostId }) => {
|
21
|
+
const [isOpen, setIsOpen] = useState(false);
|
22
|
+
const {
|
23
|
+
response: { results: features },
|
24
|
+
status,
|
25
|
+
} = useAPI('get', foremanUrl(REX_FEATURES_API));
|
26
|
+
|
27
|
+
const dispatch = useDispatch();
|
28
|
+
const dropdownItems = features
|
29
|
+
?.filter(feature => feature.host_action_button)
|
30
|
+
?.map(({ name, label, id, description }) => (
|
31
|
+
<DropdownItem
|
32
|
+
onClick={() => dispatch(runFeature(hostId, label, name))}
|
33
|
+
key={id}
|
34
|
+
description={description}
|
35
|
+
>
|
36
|
+
{name}
|
37
|
+
</DropdownItem>
|
38
|
+
));
|
39
|
+
const scheduleJob = [
|
40
|
+
<DropdownToggleAction
|
41
|
+
onClick={() => dispatch(push(`${NEW_JOB_PAGE}=${hostId}`))}
|
42
|
+
key="schedule-job-action"
|
43
|
+
>
|
44
|
+
{__('Schedule a job')}
|
45
|
+
</DropdownToggleAction>,
|
46
|
+
];
|
47
|
+
|
48
|
+
return (
|
49
|
+
<Dropdown
|
50
|
+
alignments={{ default: 'right' }}
|
51
|
+
onSelect={() => setIsOpen(false)}
|
52
|
+
toggle={
|
53
|
+
<DropdownToggle
|
54
|
+
splitButtonItems={scheduleJob}
|
55
|
+
toggleVariant="primary"
|
56
|
+
onToggle={() => setIsOpen(prev => !prev)}
|
57
|
+
isDisabled={status === STATUS.PENDING}
|
58
|
+
splitButtonVariant="action"
|
59
|
+
/>
|
60
|
+
}
|
61
|
+
isOpen={isOpen}
|
62
|
+
dropdownItems={dropdownItems}
|
63
|
+
/>
|
64
|
+
);
|
65
|
+
};
|
66
|
+
|
67
|
+
FeaturesDropdown.propTypes = {
|
68
|
+
hostId: PropTypes.number,
|
69
|
+
};
|
70
|
+
FeaturesDropdown.defaultProps = {
|
71
|
+
hostId: undefined,
|
72
|
+
};
|
73
|
+
|
74
|
+
export default FeaturesDropdown;
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { useSelector } from 'react-redux';
|
3
|
+
import { DropdownItem } from '@patternfly/react-core';
|
4
|
+
import { CodeIcon } from '@patternfly/react-icons';
|
5
|
+
import { selectAPIResponse } from 'foremanReact/redux/API/APISelectors';
|
6
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
7
|
+
import { HOST_DETAILS_KEY } from 'foremanReact/components/HostDetails/consts';
|
8
|
+
|
9
|
+
const HostKebabItems = () => {
|
10
|
+
const { cockpit_url: consoleUrl } = useSelector(state =>
|
11
|
+
selectAPIResponse(state, HOST_DETAILS_KEY)
|
12
|
+
);
|
13
|
+
|
14
|
+
if (!consoleUrl) return null;
|
15
|
+
return (
|
16
|
+
<DropdownItem icon={<CodeIcon />} href={consoleUrl}>
|
17
|
+
{__('Web Console')}
|
18
|
+
</DropdownItem>
|
19
|
+
);
|
20
|
+
};
|
21
|
+
|
22
|
+
export default HostKebabItems;
|
@@ -52,7 +52,12 @@ const RecentJobsCard = ({ hostDetails: { name, id } }) => {
|
|
52
52
|
</DropdownItem>,
|
53
53
|
]}
|
54
54
|
>
|
55
|
-
<Tabs
|
55
|
+
<Tabs
|
56
|
+
mountOnEnter
|
57
|
+
unmountOnExit
|
58
|
+
activeKey={activeTab}
|
59
|
+
onSelect={handleTabClick}
|
60
|
+
>
|
56
61
|
<Tab
|
57
62
|
eventKey={FINISHED_TAB}
|
58
63
|
title={<TabTitleText>{__('Finished')}</TabTitleText>}
|
@@ -19,7 +19,7 @@ import { translate as __ } from 'foremanReact/common/I18n';
|
|
19
19
|
import { foremanUrl } from 'foremanReact/common/helpers';
|
20
20
|
|
21
21
|
import JobStatusIcon from './JobStatusIcon';
|
22
|
-
import { JOB_API_URL, JOBS_IN_CARD } from './constants';
|
22
|
+
import { JOB_API_URL, JOBS_IN_CARD, RECENT_JOBS_KEY } from './constants';
|
23
23
|
|
24
24
|
const RecentJobsTable = ({ status, hostId }) => {
|
25
25
|
const jobsUrl =
|
@@ -30,7 +30,7 @@ const RecentJobsTable = ({ status, hostId }) => {
|
|
30
30
|
const {
|
31
31
|
response: { results: jobs },
|
32
32
|
status: responseStatus,
|
33
|
-
} = useAPI('get', jobsUrl);
|
33
|
+
} = useAPI('get', jobsUrl, RECENT_JOBS_KEY);
|
34
34
|
|
35
35
|
return (
|
36
36
|
<DataList aria-label="recent-jobs-table" isCompact>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { addGlobalFill } from 'foremanReact/components/common/Fill/GlobalFill';
|
3
|
+
import KebabItems from '../components/HostKebab/KebabItems';
|
4
|
+
|
5
|
+
export default () =>
|
6
|
+
addGlobalFill(
|
7
|
+
'host-details-kebab',
|
8
|
+
'rex-host-details-kebab-job',
|
9
|
+
<KebabItems key="rex-host-details-kebab-job" />,
|
10
|
+
100
|
11
|
+
);
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { addGlobalFill } from 'foremanReact/components/common/Fill/GlobalFill';
|
3
|
+
import FeaturesDropdown from '../components/FeaturesDropdown';
|
4
|
+
|
5
|
+
export default () =>
|
6
|
+
addGlobalFill(
|
7
|
+
'_rex-host-features',
|
8
|
+
'_rex-host-features',
|
9
|
+
<FeaturesDropdown key="_rex-host-features" />,
|
10
|
+
1000
|
11
|
+
);
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreman_remote_execution
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.
|
4
|
+
version: 6.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Foreman Remote Execution team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-03-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: deface
|
@@ -201,6 +201,7 @@ files:
|
|
201
201
|
- app/views/api/v2/foreign_input_sets/index.json.rabl
|
202
202
|
- app/views/api/v2/foreign_input_sets/main.json.rabl
|
203
203
|
- app/views/api/v2/foreign_input_sets/show.json.rabl
|
204
|
+
- app/views/api/v2/host/main.rabl
|
204
205
|
- app/views/api/v2/interfaces/execution_flag.json.rabl
|
205
206
|
- app/views/api/v2/job_invocations/base.json.rabl
|
206
207
|
- app/views/api/v2/job_invocations/create.json.rabl
|
@@ -263,17 +264,17 @@ files:
|
|
263
264
|
- app/views/template_invocations/_refresh.js.erb
|
264
265
|
- app/views/template_invocations/show.html.erb
|
265
266
|
- app/views/template_invocations/show.js.erb
|
266
|
-
- app/views/templates/
|
267
|
-
- app/views/templates/
|
268
|
-
- app/views/templates/
|
269
|
-
- app/views/templates/
|
270
|
-
- app/views/templates/
|
271
|
-
- app/views/templates/
|
272
|
-
- app/views/templates/
|
273
|
-
- app/views/templates/
|
274
|
-
- app/views/templates/
|
275
|
-
- app/views/templates/
|
276
|
-
- app/views/templates/
|
267
|
+
- app/views/templates/script/check_update.erb
|
268
|
+
- app/views/templates/script/module_action.erb
|
269
|
+
- app/views/templates/script/package_action.erb
|
270
|
+
- app/views/templates/script/power_action.erb
|
271
|
+
- app/views/templates/script/puppet_agent_disable.erb
|
272
|
+
- app/views/templates/script/puppet_agent_enable.erb
|
273
|
+
- app/views/templates/script/puppet_install_modules_from_forge.erb
|
274
|
+
- app/views/templates/script/puppet_install_modules_from_git.erb
|
275
|
+
- app/views/templates/script/puppet_run_once.erb
|
276
|
+
- app/views/templates/script/run_command.erb
|
277
|
+
- app/views/templates/script/service_action.erb
|
277
278
|
- config/routes.rb
|
278
279
|
- db/migrate/20150612121541_add_job_template_to_template.rb
|
279
280
|
- db/migrate/20150708133241_add_targeting.rb
|
@@ -321,6 +322,7 @@ files:
|
|
321
322
|
- db/migrate/20210312074713_add_provider_inputs.rb
|
322
323
|
- db/migrate/2021051713291621250977_add_host_proxy_invocations.rb
|
323
324
|
- db/migrate/20210816100932_rex_setting_category_to_dsl.rb
|
325
|
+
- db/migrate/20220321101835_rename_ssh_provider_to_script.rb
|
324
326
|
- db/seeds.d/100-assign_features_with_templates.rb
|
325
327
|
- db/seeds.d/20-permissions.rb
|
326
328
|
- db/seeds.d/50-notification_blueprints.rb
|
@@ -333,6 +335,7 @@ files:
|
|
333
335
|
- extra/cockpit/foreman-cockpit.service
|
334
336
|
- extra/cockpit/settings.yml.example
|
335
337
|
- foreman_remote_execution.gemspec
|
338
|
+
- jsconfig.json
|
336
339
|
- lib/foreman_remote_execution.rb
|
337
340
|
- lib/foreman_remote_execution/engine.rb
|
338
341
|
- lib/foreman_remote_execution/version.rb
|
@@ -475,6 +478,10 @@ files:
|
|
475
478
|
- webpack/global_index.js
|
476
479
|
- webpack/helpers.js
|
477
480
|
- webpack/index.js
|
481
|
+
- webpack/react_app/components/FeaturesDropdown/actions.js
|
482
|
+
- webpack/react_app/components/FeaturesDropdown/constant.js
|
483
|
+
- webpack/react_app/components/FeaturesDropdown/index.js
|
484
|
+
- webpack/react_app/components/HostKebab/KebabItems.js
|
478
485
|
- webpack/react_app/components/RecentJobsCard/JobStatusIcon.js
|
479
486
|
- webpack/react_app/components/RecentJobsCard/RecentJobsCard.js
|
480
487
|
- webpack/react_app/components/RecentJobsCard/RecentJobsTable.js
|
@@ -507,7 +514,9 @@ files:
|
|
507
514
|
- webpack/react_app/components/jobInvocations/AggregateStatus/index.js
|
508
515
|
- webpack/react_app/components/jobInvocations/AggregateStatus/index.test.js
|
509
516
|
- webpack/react_app/components/jobInvocations/index.js
|
517
|
+
- webpack/react_app/extend/fillKebabItems.js
|
510
518
|
- webpack/react_app/extend/fillRecentJobsCard.js
|
519
|
+
- webpack/react_app/extend/fillRexFeaturesDropdown.js
|
511
520
|
- webpack/react_app/extend/fillregistrationAdvanced.js
|
512
521
|
- webpack/react_app/extend/reducers.js
|
513
522
|
- webpack/react_app/redux/actions/jobInvocations/index.js
|
@@ -536,7 +545,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
536
545
|
- !ruby/object:Gem::Version
|
537
546
|
version: '0'
|
538
547
|
requirements: []
|
539
|
-
rubygems_version: 3.
|
548
|
+
rubygems_version: 3.2.26
|
540
549
|
signing_key:
|
541
550
|
specification_version: 4
|
542
551
|
summary: A plugin bringing remote execution to the Foreman, completing the config
|