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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d91fb9bffefb6a6d9414902cf0b666dcbee5763ce910931461126c81962b9c70
4
- data.tar.gz: d735b4f6382e1f9daf255f3f0033cc8f3e7b00ad720a3c51b1e40b8aea5fb659
3
+ metadata.gz: '088390c13c651a79849a384d7745fad23975bacbedf7913be50d6bed8d66db60'
4
+ data.tar.gz: 6138f9a4c9cdc374e21254c739ec3c45b5dd95ebd87bd04e40dce2b346874380
5
5
  SHA512:
6
- metadata.gz: fa5ae15780a665b6a940ac25c86ba77f52b0ba51cc71ae6ebb4b2c55928876e3e5ecd4b4889ac3e451603939b3daaef14b52b994fdb639b0b24d45987da111fa
7
- data.tar.gz: d8b669044ad9c7b37e95a56ba739ea7f02c23af08f70c89c6cad65ba0f6be2fbda84b61e53ca6e37c61ab10e536074b7417fe61e8181bce6b2dfd98c3ec6e45e
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
- "lodash"
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,
@@ -69,7 +69,7 @@ module Actions
69
69
  end
70
70
 
71
71
  def hosts
72
- job_invocation.targeting.hosts.order(:name, :id)
72
+ job_invocation.targeting.hosts.order("#{TargetingHost.table_name}.id")
73
73
  end
74
74
 
75
75
  def set_up_concurrency_control(invocation)
@@ -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 # FIXME No auto complete because of https://github.com/wvanbergen/scoped_search/issues/138
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
 
@@ -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,5 @@
1
+ class AddRandomizedOrderingToTargeting < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_column :targetings, :randomized_ordering, :bool, :default => false
4
+ end
5
+ end
@@ -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,13 @@
1
+ [WebService]
2
+ LoginTitle = Foreman Cockpit
3
+ UrlRoot = /webcon/
4
+ Origins = http://localhost:3000
5
+
6
+ [Bearer]
7
+ Action = remote-login-ssh
8
+
9
+ [SSH-Login]
10
+ command = /usr/sbin/foreman-cockpit-session
11
+
12
+ [OAuth]
13
+ Url = /cockpit/redirect
@@ -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
@@ -1,3 +1,3 @@
1
1
  module ForemanRemoteExecution
2
- VERSION = '1.8.2'.freeze
2
+ VERSION = '1.8.3'.freeze
3
3
  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/ || exit 0",
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
- "babel-polyfill": "^6.26.0",
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
  }
@@ -28,6 +28,10 @@ FactoryBot.define do
28
28
  search_query { 'name = foo' }
29
29
  targeting_type { 'static_query' }
30
30
  user
31
+
32
+ trait :with_randomized_ordering do
33
+ randomized_ordering { true }
34
+ end
31
35
  end
32
36
 
33
37
  factory :job_invocation do |f|
@@ -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
@@ -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.2
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-07-16 00:00:00.000000000 Z
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.3
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