foreman_remote_execution 1.8.2 → 1.8.3
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/.babelrc +10 -3
- data/.eslintrc +1 -18
- data/app/controllers/api/v2/job_invocations_controller.rb +1 -0
- data/app/lib/actions/remote_execution/run_hosts_job.rb +1 -1
- data/app/models/job_invocation.rb +1 -1
- data/app/models/job_invocation_composer.rb +4 -3
- data/app/models/targeting.rb +7 -2
- data/app/views/api/v2/job_invocations/main.json.rabl +2 -1
- data/app/views/job_invocations/_card_target_hosts.html.erb +4 -0
- data/app/views/job_invocations/_form.html.erb +9 -1
- data/db/migrate/20180913101042_add_randomized_ordering_to_targeting.rb +5 -0
- data/db/seeds.d/100-assign_features_with_templates.rb +21 -0
- data/extra/cockpit/cockpit.conf.example +13 -0
- data/extra/cockpit/foreman-cockpit.service +9 -0
- data/lib/foreman_remote_execution/engine.rb +11 -0
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/package.json +6 -12
- data/test/factories/foreman_remote_execution_factories.rb +4 -0
- data/test/unit/job_invocation_test.rb +6 -1
- data/test/unit/targeting_test.rb +19 -0
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '088390c13c651a79849a384d7745fad23975bacbedf7913be50d6bed8d66db60'
|
4
|
+
data.tar.gz: 6138f9a4c9cdc374e21254c739ec3c45b5dd95ebd87bd04e40dce2b346874380
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c866a3ae708c68f9e2176817d74b27c185558cba82e5d3e0c9777fe6e32a538742fac1ff60c472ed8deda93890db3b81e7b0c38af3102dbe0b56590342559a0c
|
7
|
+
data.tar.gz: c4a1f91be44fb15b78ce1b4ff262b885bb3b325caa7a0ac6078203f3c3011e1ff93e97082869806e14a4428e95b0476cc451d267188bc41ab0b2c4e2eaba1bd3
|
data/.babelrc
CHANGED
@@ -3,7 +3,14 @@
|
|
3
3
|
"plugins": [
|
4
4
|
"transform-class-properties",
|
5
5
|
"transform-object-rest-spread",
|
6
|
-
"transform-object-assign"
|
7
|
-
|
8
|
-
|
6
|
+
"transform-object-assign"
|
7
|
+
],
|
8
|
+
"env": {
|
9
|
+
"test": {
|
10
|
+
"presets": ["@theforeman/vendor-dev/babel.preset.js"]
|
11
|
+
},
|
12
|
+
"storybook": {
|
13
|
+
"presets": ["@theforeman/vendor-dev/babel.preset.js"]
|
14
|
+
}
|
15
|
+
}
|
9
16
|
}
|
data/.eslintrc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"root": true,
|
3
|
-
"extends": "airbnb-base",
|
3
|
+
"extends": ["airbnb-base", "./node_modules/@theforeman/vendor-dev/eslint.extends.js"],
|
4
4
|
"plugins": [
|
5
5
|
"react"
|
6
6
|
],
|
@@ -11,16 +11,6 @@
|
|
11
11
|
"jasmine": true,
|
12
12
|
"jest": true
|
13
13
|
},
|
14
|
-
"globals": {
|
15
|
-
"document": false,
|
16
|
-
"escape": false,
|
17
|
-
"navigator": false,
|
18
|
-
"unescape": false,
|
19
|
-
"window": false,
|
20
|
-
"$": true,
|
21
|
-
"_": true,
|
22
|
-
"__": true
|
23
|
-
},
|
24
14
|
"parser": "babel-eslint",
|
25
15
|
"rules": {
|
26
16
|
"react/jsx-uses-vars": "error",
|
@@ -35,13 +25,6 @@
|
|
35
25
|
"no-underscore-dangle": "off",
|
36
26
|
"no-use-before-define": "off",
|
37
27
|
"import/prefer-default-export": "off",
|
38
|
-
"import/no-extraneous-dependencies": [
|
39
|
-
"error",
|
40
|
-
{
|
41
|
-
// Allow importing devDependencies like @storybook
|
42
|
-
"devDependencies": true
|
43
|
-
}
|
44
|
-
],
|
45
28
|
// Import rules off for now due to HoundCI issue
|
46
29
|
"import/no-unresolved": "off",
|
47
30
|
"import/extensions": "off"
|
@@ -25,6 +25,7 @@ module Api
|
|
25
25
|
param :job_invocation, Hash, :required => true, :action_aware => true do
|
26
26
|
param :job_template_id, String, :required => false, :desc => N_('The job template to use, parameter is required unless feature was specified')
|
27
27
|
param :targeting_type, String, :required => true, :desc => N_('Invocation type, one of %s') % Targeting::TYPES
|
28
|
+
param :randomized_ordering, :bool, :desc => N_('Execute the jobs on hosts in randomized order')
|
28
29
|
param :inputs, Hash, :required => false, :desc => N_('Inputs to use')
|
29
30
|
param :ssh, Hash, :desc => N_('SSH provider specific options') do
|
30
31
|
param :effective_user, String,
|
@@ -23,7 +23,7 @@ class JobInvocation < ApplicationRecord
|
|
23
23
|
validates_associated :targeting, :all_template_invocations
|
24
24
|
|
25
25
|
scoped_search :on => :job_category, :complete_value => true
|
26
|
-
scoped_search :on => :description
|
26
|
+
scoped_search :on => :description, :complete_value => true
|
27
27
|
|
28
28
|
has_many :template_invocations_hosts, :through => :template_invocations, :source => :host
|
29
29
|
scoped_search :relation => :template_invocations_hosts, :on => :name, :rename => 'host', :complete_value => true
|
@@ -108,7 +108,7 @@ class JobInvocationComposer
|
|
108
108
|
|
109
109
|
def targeting_params
|
110
110
|
raise ::Foreman::Exception, _('Cannot specify both bookmark_id and search_query') if api_params[:bookmark_id] && api_params[:search_query]
|
111
|
-
api_params.slice(:targeting_type, :bookmark_id, :search_query).merge(:user_id => User.current.id)
|
111
|
+
api_params.slice(:targeting_type, :bookmark_id, :search_query, :randomized_ordering).merge(:user_id => User.current.id)
|
112
112
|
end
|
113
113
|
|
114
114
|
def triggering_params
|
@@ -205,7 +205,7 @@ class JobInvocationComposer
|
|
205
205
|
search_query = @host_ids.empty? ? 'name ^ ()' : Targeting.build_query_from_hosts(@host_ids)
|
206
206
|
base.merge(:search_query => search_query, :targeting_type => job_invocation.targeting.targeting_type)
|
207
207
|
else
|
208
|
-
base.merge job_invocation.targeting.attributes.slice('search_query', 'bookmark_id', 'targeting_type')
|
208
|
+
base.merge job_invocation.targeting.attributes.slice('search_query', 'bookmark_id', 'targeting_type', 'randomized_ordering')
|
209
209
|
end
|
210
210
|
end
|
211
211
|
|
@@ -512,7 +512,8 @@ class JobInvocationComposer
|
|
512
512
|
Targeting.new(
|
513
513
|
:bookmark_id => bookmark_id,
|
514
514
|
:targeting_type => params[:targeting][:targeting_type],
|
515
|
-
:search_query => query
|
515
|
+
:search_query => query,
|
516
|
+
:randomized_ordering => params[:targeting][:randomized_ordering]
|
516
517
|
) { |t| t.user_id = params[:targeting][:user_id] }
|
517
518
|
end
|
518
519
|
|
data/app/models/targeting.rb
CHANGED
@@ -5,11 +5,15 @@ class Targeting < ApplicationRecord
|
|
5
5
|
TYPES = { STATIC_TYPE => N_('Static Query'), DYNAMIC_TYPE => N_('Dynamic Query') }.freeze
|
6
6
|
RESOLVE_PERMISSION = :view_hosts
|
7
7
|
|
8
|
+
ORDERED = 'ordered_execution'.freeze
|
9
|
+
RANDOMIZED = 'randomized_execution'.freeze
|
10
|
+
ORDERINGS = { ORDERED => N_('Alphabetical'), RANDOMIZED => N_('Randomized') }.freeze
|
11
|
+
|
8
12
|
belongs_to :user
|
9
13
|
belongs_to :bookmark
|
10
14
|
|
11
15
|
has_many :targeting_hosts, :dependent => :destroy
|
12
|
-
has_many :hosts, :through => :targeting_hosts
|
16
|
+
has_many :hosts, -> { order TargetingHost.table_name + '.id' }, :through => :targeting_hosts
|
13
17
|
has_one :job_invocation, :dependent => :delete
|
14
18
|
has_many :template_invocations, :through => :job_invocation
|
15
19
|
|
@@ -40,7 +44,8 @@ class Targeting < ApplicationRecord
|
|
40
44
|
self.validate!
|
41
45
|
# avoid validation of hosts objects - they will be loaded for no reason.
|
42
46
|
# pluck(:id) returns duplicate results for HostCollections
|
43
|
-
host_ids = User.as(user.login) { Host.authorized(RESOLVE_PERMISSION, Host).search_for(search_query).pluck(:id).uniq }
|
47
|
+
host_ids = User.as(user.login) { Host.authorized(RESOLVE_PERMISSION, Host).search_for(search_query).order(:name, :id).pluck(:id).uniq }
|
48
|
+
host_ids.shuffle!(random: Random.new) if randomized_ordering
|
44
49
|
# this can be optimized even more, by introducing bulk insert
|
45
50
|
self.targeting_hosts.build(host_ids.map { |id| { :host_id => id } })
|
46
51
|
self.save(:validate => false)
|
@@ -16,7 +16,8 @@ node do |invocation|
|
|
16
16
|
end
|
17
17
|
|
18
18
|
child :targeting do
|
19
|
-
attributes :bookmark_id, :search_query, :targeting_type, :user_id, :status, :status_label
|
19
|
+
attributes :bookmark_id, :search_query, :targeting_type, :user_id, :status, :status_label,
|
20
|
+
:randomized_ordering
|
20
21
|
|
21
22
|
child :hosts do
|
22
23
|
extends 'api/v2/hosts/base'
|
@@ -16,6 +16,10 @@
|
|
16
16
|
<strong><%= _(Targeting::TYPES[job_invocation.targeting.targeting_type]).downcase %></strong>
|
17
17
|
<pre><%= job_invocation.targeting.search_query %></pre>
|
18
18
|
</p>
|
19
|
+
<p>
|
20
|
+
<% key = job_invocation.targeting.randomized_ordering ? Targeting::RANDOMIZED : Targeting::ORDERED %>
|
21
|
+
<%= _('Execution order') %>: <strong><%= Targeting::ORDERINGS[key].downcase %></strong>
|
22
|
+
</p>
|
19
23
|
</div>
|
20
24
|
<div class='card-pf-footer'>
|
21
25
|
<p>
|
@@ -104,13 +104,21 @@
|
|
104
104
|
</div>
|
105
105
|
|
106
106
|
<div class="form-group advanced hidden">
|
107
|
+
<%= add_label({ :label => _('Execution ordering'), :label_help => _("Execution ordering determines whether the jobs should be executed on hosts in alphabetical order or in randomized order.<br><ul><li><b>Ordered</b> - executes the jobs on hosts in alphabetical order</li><li><b>Randomized</b> - randomizes the order in which jobs are executed on hosts</li></ul>") }, f, :randomized_ordering) %>
|
108
|
+
|
109
|
+
<div class="col-md-4">
|
110
|
+
<%= radio_button_f targeting_fields, :randomized_ordering, :value => false, :text => _(Targeting::ORDERINGS[Targeting::ORDERED]), :checked => !@composer.targeting.randomized_ordering %>
|
111
|
+
<%= radio_button_f targeting_fields, :randomized_ordering, :value => true, :text => _(Targeting::ORDERINGS[Targeting::RANDOMIZED]) %>
|
112
|
+
</div>
|
113
|
+
</div>
|
114
|
+
|
115
|
+
<div class="form-group">
|
107
116
|
<%= add_label({ :label => _('Type of query'), :label_help => _("Type has impact on when is the query evaluated to hosts.<br><ul><li><b>Static</b> - evaluates just after you submit this form</li><li><b>Dynamic</b> - evaluates just before the execution is started, so if it's planed in future, targeted hosts set may change before it</li></ul>") }, f, :targetting_type) %>
|
108
117
|
|
109
118
|
<div class="col-md-4">
|
110
119
|
<%= radio_button_f targeting_fields, :targeting_type, :value => Targeting::STATIC_TYPE, :text => _(Targeting::TYPES[Targeting::STATIC_TYPE]) %>
|
111
120
|
<%= radio_button_f targeting_fields, :targeting_type, :value => Targeting::DYNAMIC_TYPE, :text => _(Targeting::TYPES[Targeting::DYNAMIC_TYPE]) %>
|
112
121
|
</div>
|
113
|
-
|
114
122
|
</div>
|
115
123
|
<% end %>
|
116
124
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
User.as_anonymous_admin do
|
2
|
+
RemoteExecutionFeature.without_auditing do
|
3
|
+
if Rails.env.test? || Foreman.in_rake?
|
4
|
+
# If this file tries to import a template with a REX feature in a SeedsTest,
|
5
|
+
# it will fail - the REX feature isn't registered on SeedsTest because
|
6
|
+
# DatabaseCleaner truncates the db before every test.
|
7
|
+
# During db:seed, we also want to know the feature is registered before
|
8
|
+
# seeding the template
|
9
|
+
# kudos to dLobatog
|
10
|
+
ForemanRemoteExecution.register_rex_feature
|
11
|
+
end
|
12
|
+
JobTemplate.without_auditing do
|
13
|
+
module_template = JobTemplate.find_by(name: 'Puppet Run Once - SSH Default')
|
14
|
+
if module_template && !Rails.env.test? && Setting[:remote_execution_sync_templates]
|
15
|
+
module_template.sync_feature('puppet_run_host')
|
16
|
+
module_template.organizations << Organization.unscoped.all if module_template.organizations.empty?
|
17
|
+
module_template.locations << Location.unscoped.all if module_template.locations.empty?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
[Service]
|
2
|
+
Environment=XDG_CONFIG_DIRS=/etc/foreman/
|
3
|
+
Environment=FOREMAN_COCKPIT_SETTINGS=/etc/foreman/cockpit/foreman-cockpit-session.yml
|
4
|
+
ExecStart=/usr/libexec/cockpit-ws --no-tls --address 127.0.0.1 --port 9999
|
5
|
+
User=foreman
|
6
|
+
Group=foreman
|
7
|
+
|
8
|
+
[Install]
|
9
|
+
WantedBy=multi-user.target
|
@@ -180,6 +180,8 @@ module ForemanRemoteExecution
|
|
180
180
|
ForemanTasks::Task.send(:include, ForemanRemoteExecution::ForemanTasksTaskExtensions)
|
181
181
|
ForemanTasks::Cleaner.send(:include, ForemanRemoteExecution::ForemanTasksCleanerExtensions)
|
182
182
|
RemoteExecutionProvider.register(:SSH, SSHExecutionProvider)
|
183
|
+
|
184
|
+
ForemanRemoteExecution.register_rex_feature
|
183
185
|
end
|
184
186
|
|
185
187
|
initializer 'foreman_remote_execution.register_gettext', after: :load_config_initializers do |_app|
|
@@ -188,4 +190,13 @@ module ForemanRemoteExecution
|
|
188
190
|
Foreman::Gettext::Support.add_text_domain locale_domain, locale_dir
|
189
191
|
end
|
190
192
|
end
|
193
|
+
|
194
|
+
def self.register_rex_feature
|
195
|
+
RemoteExecutionFeature.register(
|
196
|
+
:puppet_run_host,
|
197
|
+
N_('Run Puppet Once'),
|
198
|
+
:description => N_('Perform a single Puppet run'),
|
199
|
+
:host_action_button => true
|
200
|
+
)
|
201
|
+
end
|
191
202
|
end
|
data/package.json
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
"version": "1.0.0",
|
4
4
|
"license": "GPL-3.0",
|
5
5
|
"scripts": {
|
6
|
-
"lint": "./node_modules/.bin/eslint -c .eslintrc webpack/ script/
|
6
|
+
"lint": "./node_modules/.bin/eslint -c .eslintrc webpack/ script/",
|
7
7
|
"test": "node node_modules/.bin/jest webpack",
|
8
8
|
"test:watch": "node node_modules/.bin/jest webpack --watchAll",
|
9
9
|
"test:current": "node node_modules/.bin/jest webpack --watch"
|
@@ -31,13 +31,14 @@
|
|
31
31
|
"url": "http://projects.theforeman.org/projects/foreman_remote_execution/issues"
|
32
32
|
},
|
33
33
|
"devDependencies": {
|
34
|
+
"@theforeman/vendor-dev": "^0.1.1",
|
34
35
|
"babel-eslint": "^8.2.1",
|
35
|
-
"babel-preset-env": "^1.6.0",
|
36
|
-
"babel-preset-react": "^6.24.1",
|
37
36
|
"babel-plugin-lodash": "^3.3.2",
|
38
|
-
"babel-plugin-transform-object-assign": "^6.22.0",
|
39
37
|
"babel-plugin-transform-class-properties": "^6.24.1",
|
38
|
+
"babel-plugin-transform-object-assign": "^6.22.0",
|
40
39
|
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
40
|
+
"babel-preset-env": "^1.6.0",
|
41
|
+
"babel-preset-react": "^6.24.1",
|
41
42
|
"enzyme": "^3.2.0",
|
42
43
|
"enzyme-adapter-react-16": "^1.1.0",
|
43
44
|
"enzyme-to-json": "^3.1.2",
|
@@ -50,13 +51,6 @@
|
|
50
51
|
"jest": "^21.2.1"
|
51
52
|
},
|
52
53
|
"dependencies": {
|
53
|
-
"
|
54
|
-
"prop-types": "^15.6.0",
|
55
|
-
"react": "^16.2.0",
|
56
|
-
"react-dom": "^16.2.0",
|
57
|
-
"react-redux": "^5.0.6",
|
58
|
-
"redux": "^3.7.2",
|
59
|
-
"seamless-immutable": "^7.1.3",
|
60
|
-
"urijs": "^1.19.0"
|
54
|
+
"@theforeman/vendor": "^0.1.1"
|
61
55
|
}
|
62
56
|
}
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'test_plugin_helper'
|
2
2
|
|
3
3
|
class JobInvocationTest < ActiveSupport::TestCase
|
4
|
-
let(:job_invocation) { FactoryBot.build(:job_invocation) }
|
4
|
+
let(:job_invocation) { FactoryBot.build(:job_invocation, :description => 'A text with "quotes"') }
|
5
5
|
let(:template) { FactoryBot.create(:job_template, :with_input) }
|
6
6
|
|
7
7
|
context 'search for job invocations' do
|
@@ -13,6 +13,11 @@ class JobInvocationTest < ActiveSupport::TestCase
|
|
13
13
|
found_jobs = JobInvocation.search_for(%{job_category = "#{job_invocation.job_category}"}).paginate(:page => 1).with_task.order('job_invocations.id DESC')
|
14
14
|
found_jobs.must_equal [job_invocation]
|
15
15
|
end
|
16
|
+
|
17
|
+
it 'is able to auto complete description' do
|
18
|
+
expected = 'description = "A text with \"quotes\""'
|
19
|
+
JobInvocation.complete_for('description = ').must_equal [expected]
|
20
|
+
end
|
16
21
|
end
|
17
22
|
|
18
23
|
context 'able to be created' do
|
data/test/unit/targeting_test.rb
CHANGED
@@ -117,4 +117,23 @@ class TargetingTest < ActiveSupport::TestCase
|
|
117
117
|
end
|
118
118
|
end
|
119
119
|
end
|
120
|
+
|
121
|
+
context 'randomized ordering' do
|
122
|
+
let(:targeting) { FactoryBot.build(:targeting, :with_randomized_ordering) }
|
123
|
+
let(:hosts) { (0..4).map { FactoryBot.create(:host) } }
|
124
|
+
|
125
|
+
it 'loads the hosts in random order' do
|
126
|
+
rng = Random.new(4) # Chosen by a fair dice roll
|
127
|
+
Random.stubs(:new).returns(rng)
|
128
|
+
hosts
|
129
|
+
targeting.search_query = 'name ~ host*'
|
130
|
+
targeting.user = users(:admin)
|
131
|
+
targeting.resolve_hosts!
|
132
|
+
randomized_host_ids = targeting.hosts.map(&:id)
|
133
|
+
host_ids = hosts.map(&:id)
|
134
|
+
|
135
|
+
assert_not_equal host_ids, randomized_host_ids
|
136
|
+
assert_equal host_ids, randomized_host_ids.sort
|
137
|
+
end
|
138
|
+
end
|
120
139
|
end
|
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: 1.8.
|
4
|
+
version: 1.8.3
|
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: 2019-
|
11
|
+
date: 2019-08-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: deface
|
@@ -325,12 +325,16 @@ files:
|
|
325
325
|
- db/migrate/20180202123215_add_feature_id_to_job_invocation.rb
|
326
326
|
- db/migrate/20180226095631_change_task_id_to_uuid.rb
|
327
327
|
- db/migrate/20180411160809_add_sudo_password_to_job_invocation.rb
|
328
|
+
- db/migrate/20180913101042_add_randomized_ordering_to_targeting.rb
|
328
329
|
- db/migrate/20190111153330_remove_remote_execution_without_proxy_setting.rb
|
330
|
+
- db/seeds.d/100-assign_features_with_templates.rb
|
329
331
|
- db/seeds.d/50-notification_blueprints.rb
|
330
332
|
- db/seeds.d/60-ssh_proxy_feature.rb
|
331
333
|
- db/seeds.d/70-job_templates.rb
|
332
334
|
- db/seeds.d/90-bookmarks.rb
|
335
|
+
- extra/cockpit/cockpit.conf.example
|
333
336
|
- extra/cockpit/foreman-cockpit-session
|
337
|
+
- extra/cockpit/foreman-cockpit.service
|
334
338
|
- extra/cockpit/settings.yml.example
|
335
339
|
- foreman_remote_execution.gemspec
|
336
340
|
- lib/foreman_remote_execution.rb
|
@@ -424,7 +428,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
424
428
|
version: '0'
|
425
429
|
requirements: []
|
426
430
|
rubyforge_project:
|
427
|
-
rubygems_version: 2.7.
|
431
|
+
rubygems_version: 2.7.6
|
428
432
|
signing_key:
|
429
433
|
specification_version: 4
|
430
434
|
summary: A plugin bringing remote execution to the Foreman, completing the config
|