foreman_remote_execution 1.8.2 → 1.8.3

Sign up to get free protection for your applications and to get access to all the features.
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