foreman-tasks 0.14.0 → 0.14.1

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
- SHA1:
3
- metadata.gz: 99bc7ed098375451dddebc63a0d2c5af37ec3374
4
- data.tar.gz: 573580a8808e4c68f84a82db9a05919b960aa771
2
+ SHA256:
3
+ metadata.gz: fddcfe8ab9f1550261511074f70d3391836126c7fe54839857f0c10c31d2d456
4
+ data.tar.gz: ac4bf4b19d05f6d6af13440655d76fce521ec76a7bdae32207a1ed883cfaa569
5
5
  SHA512:
6
- metadata.gz: 9f37b871477d68c476aef947749d516337e3ae8fe5d71bc7f49f82bc5f0467fa265ac324661cb3ad9a2e7368159e92e017e101cd940422b2f1eaea24e4960948
7
- data.tar.gz: a11510b41ff6b00d8d76acaa01a3dede47792c732711ee916e86979e2f56cd02ca861a8d648673ac34e9d8d0bf73452677029969557311446c90b391ecebcb24
6
+ metadata.gz: 0d6e2483dfdf63d5b32980889edd41af2d496ba5fce667593887f678781587282c17fb9abf3e77e22750d4c965c7930e45608c9a4ec7fa475d6c8f0b8e3a69e6
7
+ data.tar.gz: 5345f2fdce7081763d79468b548add5eea06093ec43581efbc3a84614571477d7446faaf4d10bda41b5c3007f0c0e3a207ab9d0d403ece7872aa2ee7ca2825a3
data/README.md CHANGED
@@ -21,6 +21,7 @@ happening/happened in your Foreman instance. A framework for asynchronous tasks
21
21
  | >= 1.16 | ~> 0.10.0 |
22
22
  | >= 1.17 | ~> 0.11.0 |
23
23
  | >= 1.18 | ~> 0.13.0 |
24
+ | >= 1.20 | ~> 0.14.0 |
24
25
 
25
26
  Installation
26
27
  ------------
@@ -11,13 +11,14 @@ module ForemanTasks
11
11
 
12
12
  def index
13
13
  params[:order] ||= 'started_at DESC'
14
- @tasks = filter(resource_base)
15
14
  respond_to do |format|
16
15
  format.html do
16
+ @tasks = filter(resource_base)
17
17
  render :index
18
18
  end
19
19
  format.csv do
20
- csv_response(@tasks, [:action_label, :state, :result, 'started_at.in_time_zone', 'ended_at.in_time_zone', :username], ['Action', 'State', 'Result', 'Started At', 'Ended At', 'User'])
20
+ @tasks = filter(resource_base, paginate: false)
21
+ csv_response(@tasks, [:action, :state, :result, 'started_at.in_time_zone', 'ended_at.in_time_zone', :username], ['Action', 'State', 'Result', 'Started At', 'Ended At', 'User'])
21
22
  end
22
23
  end
23
24
  end
@@ -124,11 +125,12 @@ module ForemanTasks
124
125
  resource_scope.where(:type => 'ForemanTasks::Task::DynflowTask').find(params[:id])
125
126
  end
126
127
 
127
- def filter(scope)
128
+ def filter(scope, paginate: true)
128
129
  search = current_taxonomy_search
129
130
  search = [search, params[:search]].select(&:present?).join(' AND ')
130
- scope.search_for(search, :order => params[:order])
131
- .paginate(:page => params[:page], :per_page => params[:per_page]).distinct
131
+ scope = scope.search_for(search, :order => params[:order])
132
+ scope = scope.paginate(:page => params[:page], :per_page => params[:per_page]) if paginate
133
+ scope.distinct
132
134
  end
133
135
 
134
136
  def current_taxonomy_search
@@ -1,7 +1,5 @@
1
1
  module Actions
2
2
  class Actions::ActionWithSubPlans < Actions::EntryAction
3
- middleware.use Actions::Middleware::KeepCurrentUser
4
-
5
3
  include Dynflow::Action::WithSubPlans
6
4
 
7
5
  def plan(*_args)
@@ -9,13 +9,13 @@ module Actions
9
9
  def plan(_host_type, host_name, facts, certname, proxy_id)
10
10
  facts['domain'].try(:downcase!)
11
11
  host = if SETTINGS[:version].short > '1.16'
12
- ::Host::Base.import_host(host_name, certname, proxy_id)
12
+ ::Host::Managed.import_host(host_name, certname)
13
13
  else
14
14
  # backwards compatibility
15
15
  ::Host::Managed.import_host(host_name, facts['_type'], certname, proxy_id)
16
16
  end
17
17
  host.save(:validate => false) if host.new_record?
18
- action_subject(host, :facts => facts)
18
+ action_subject(host, :facts => facts.to_unsafe_h, :proxy_id => proxy_id)
19
19
  if host.build?
20
20
  ::Foreman::Logging.logger('foreman-tasks').info "Skipping importing of facts for #{host.name} because it's in build mode"
21
21
  else
@@ -26,7 +26,7 @@ module Actions
26
26
  def run
27
27
  ::User.as :admin do
28
28
  host = ::Host.find(input[:host][:id])
29
- state = host.import_facts(input[:facts])
29
+ state = host.import_facts(input[:facts], proxy)
30
30
  output[:state] = state
31
31
  end
32
32
  rescue ::Foreman::Exception => e
@@ -36,6 +36,10 @@ module Actions
36
36
  raise e unless e.code == 'ERF51-9911'
37
37
  end
38
38
 
39
+ def proxy
40
+ SmartProxy.find_by(id: input[:proxy_id]) if input[:proxy_id].present?
41
+ end
42
+
39
43
  def rescue_strategy
40
44
  ::Dynflow::Action::Rescue::Skip
41
45
  end
@@ -64,9 +64,9 @@ module Actions
64
64
  task
65
65
  end
66
66
  rescue => e
67
- # We could not reach the remote task, we assume it's gone
67
+ # We could not reach the remote task, we'll try again next time
68
68
  action.action_logger.warn(_('Failed to check on tasks on proxy at %{url}: %{exception}') % { :url => url, :exception => e.message })
69
- tasks
69
+ []
70
70
  end
71
71
  end
72
72
  end
@@ -51,7 +51,8 @@ module ForemanTasks
51
51
  :complete_value => true,
52
52
  :rename => 'owner.id',
53
53
  :ext_method => :search_by_owner,
54
- :validator => ->(value) { ScopedSearch::Validators::INTEGER.call(value) || value == 'current_user' }
54
+ :validator => ->(value) { ScopedSearch::Validators::INTEGER.call(value) || value == 'current_user' },
55
+ :aliases => ['user.id']
55
56
  scoped_search :relation => :owners, :on => :login, :complete_value => true, :rename => 'owner.login', :ext_method => :search_by_owner, :aliases => [:user]
56
57
  scoped_search :relation => :owners, :on => :firstname, :complete_value => true, :rename => 'owner.firstname', :ext_method => :search_by_owner
57
58
  scoped_search :relation => :task_groups, :on => :id, :complete_value => true, :rename => 'task_group.id', :validator => ScopedSearch::Validators::INTEGER
@@ -85,8 +86,24 @@ module ForemanTasks
85
86
  delayed? ? N_('Delayed') : N_('Immediate')
86
87
  end
87
88
 
89
+ def get_humanized(method)
90
+ attr = case method
91
+ when :humanized_name
92
+ :action
93
+ when :humanized_input
94
+ :input
95
+ when :humanized_output
96
+ :output
97
+ end
98
+ if attr
99
+ humanized[attr]
100
+ else
101
+ _('N/A')
102
+ end
103
+ end
104
+
88
105
  def humanized
89
- { action: label,
106
+ { action: action,
90
107
  input: '',
91
108
  output: '' }
92
109
  end
@@ -158,6 +175,20 @@ module ForemanTasks
158
175
  # using uniq suffix to avoid colisions when searching by two different owners via ScopedSearch
159
176
  uniq_suffix = SecureRandom.hex(3)
160
177
  key_name = connection.quote_column_name(key.sub(/^.*\./, ''))
178
+ value.sub!('*', '%%')
179
+ condition = if key.blank?
180
+ sanitize_sql_for_conditions(["users#{uniq_suffix}.login #{operator} ? or users#{uniq_suffix}.firstname #{operator} ? ", value, value])
181
+ elsif key =~ /\.id\Z/
182
+ value = User.current.id if value == 'current_user'
183
+ sanitize_sql_for_conditions(["foreman_tasks_locks_owner#{uniq_suffix}.resource_id #{operator} ?", value])
184
+ else
185
+ placeholder, value = operator == 'IN' ? ['(?)', value.split(',').map(&:strip)] : ['?', value]
186
+ sanitize_sql_for_conditions(["users#{uniq_suffix}.#{key_name} #{operator} #{placeholder}", value])
187
+ end
188
+ { :conditions => condition, :joins => joins_for_user_search(key, uniq_suffix) }
189
+ end
190
+
191
+ def self.joins_for_user_search(key, uniq_suffix)
161
192
  joins = <<-SQL
162
193
  INNER JOIN foreman_tasks_locks AS foreman_tasks_locks_owner#{uniq_suffix}
163
194
  ON (foreman_tasks_locks_owner#{uniq_suffix}.task_id = foreman_tasks_tasks.id AND
@@ -170,15 +201,7 @@ module ForemanTasks
170
201
  ON (users#{uniq_suffix}.id = foreman_tasks_locks_owner#{uniq_suffix}.resource_id)
171
202
  SQL
172
203
  end
173
- condition = if key.blank?
174
- sanitize_sql_for_conditions(["users#{uniq_suffix}.login #{operator} ? or users#{uniq_suffix}.firstname #{operator} ? ", value, value])
175
- elsif key =~ /\.id\Z/
176
- value = User.current.id if value == 'current_user'
177
- sanitize_sql_for_conditions(["foreman_tasks_locks_owner#{uniq_suffix}.resource_id #{operator} ?", value])
178
- else
179
- sanitize_sql_for_conditions(["users#{uniq_suffix}.#{key_name} #{operator} ?", value])
180
- end
181
- { :conditions => condition, :joins => joins }
204
+ joins
182
205
  end
183
206
 
184
207
  def progress
@@ -5,6 +5,7 @@ class Setting::ForemanTasks < Setting
5
5
 
6
6
  transaction do
7
7
  [
8
+ set('foreman_tasks_sync_task_timeout', N_('Number of seconds to wait for synchronous task to finish.'), 120),
8
9
  set('dynflow_allow_dangerous_actions', N_('Allow unlocking actions which can have dangerous consequences.'), false),
9
10
  set('dynflow_enable_console', N_('Enable the dynflow console (/foreman_tasks/dynflow) for debugging'), true),
10
11
  set('dynflow_console_require_auth', N_('Require user to be authenticated as user with admin rights when accessing dynflow console'), true),
@@ -29,7 +29,7 @@ same resource. It also optionally provides Dynflow infrastructure for using it f
29
29
  s.extra_rdoc_files = Dir['README*', 'LICENSE']
30
30
 
31
31
  s.add_dependency "foreman-tasks-core"
32
- s.add_dependency "dynflow", '~> 1.0', '>= 1.1.0'
32
+ s.add_dependency "dynflow", '~> 1.0', '>= 1.1.1'
33
33
  s.add_dependency "sinatra" # for Dynflow web console
34
34
  s.add_dependency "parse-cron", '~> 0.1.4'
35
35
  s.add_dependency "get_process_mem" # for memory polling
data/lib/foreman_tasks.rb CHANGED
@@ -27,8 +27,15 @@ module ForemanTasks
27
27
  raise error
28
28
  end),
29
29
  (on ::Dynflow::World::Triggered.call(execution_plan_id: ~any, future: ~any) do |id, finished|
30
- finished.wait if async == false
31
- ForemanTasks::Task::DynflowTask.where(:external_id => id).first!
30
+ unless async
31
+ timeout = Setting['foreman_tasks_sync_task_timeout']
32
+ finished.wait(timeout)
33
+ task = ForemanTasks::Task::DynflowTask.where(:external_id => id).first
34
+ if task.nil? || task.pending?
35
+ raise TimeoutError, "The time waiting for task #{task.try(:id)} to finish exceeded the 'foreman_tasks_sync_task_timeout' (#{timeout}s)"
36
+ end
37
+ end
38
+ task || ForemanTasks::Task::DynflowTask.where(:external_id => id).first!
32
39
  end)
33
40
  end
34
41
  end
@@ -115,8 +115,12 @@ module ForemanTasks
115
115
 
116
116
  initializer 'foreman_tasks.require_dynflow', :before => 'foreman_tasks.initialize_dynflow' do |_app|
117
117
  ForemanTasks.dynflow.require!
118
- ::ForemanTasks.dynflow.config.on_init do |world|
118
+ ::ForemanTasks.dynflow.config.on_init(false) do |world|
119
+ world.middleware.use Actions::Middleware::KeepCurrentTaxonomies
119
120
  world.middleware.use Actions::Middleware::KeepCurrentUser
121
+ end
122
+
123
+ ::ForemanTasks.dynflow.config.on_init do |world|
120
124
  ForemanTasksCore.dynflow_setup(world)
121
125
  end
122
126
  end
@@ -1,3 +1,3 @@
1
1
  module ForemanTasks
2
- VERSION = '0.14.0'.freeze
2
+ VERSION = '0.14.1'.freeze
3
3
  end
@@ -24,6 +24,14 @@ module ForemanTasks
24
24
  Organization.current = Location.current = nil
25
25
  end
26
26
 
27
+ it 'supports csv export' do
28
+ FactoryBot.create(:some_task, :action => 'Some action')
29
+ get(:index, params: { format: :csv }, session: set_session_user)
30
+ assert_response :success
31
+ assert_equal 2, response.body.lines.size
32
+ assert_include response.body.lines[1], 'Some action'
33
+ end
34
+
27
35
  describe 'taxonomy scoping' do
28
36
  let(:organizations) { (0..1).map { FactoryBot.create(:organization) } }
29
37
  let(:tasks) { organizations.map { |o| linked_task(o) } + [FactoryBot.create(:some_task)] }
@@ -21,6 +21,24 @@ class TasksTest < ActiveSupport::TestCase
21
21
  test 'can search the tasks by current_user in combination with implicit search' do
22
22
  assert_equal [@task_one], ForemanTasks::Task.search_for("owner.id = current_user AND #{@task_one.label}")
23
23
  end
24
+
25
+ test 'can search the tasks by user' do
26
+ assert_equal [@task_one], ForemanTasks::Task.search_for("user = #{@user_one.login}")
27
+ end
28
+
29
+ test 'can search the tasks by user\'s id' do
30
+ assert_equal [@task_one], ForemanTasks::Task.search_for("user.id = #{@user_one.id}")
31
+ assert_equal [@task_one], ForemanTasks::Task.search_for("owner.id = #{@user_one.id}")
32
+ end
33
+
34
+ test 'can search the tasks by user with wildcards' do
35
+ glob = '*' + @user_one.login[1..-1] # search for '*ser1' if login is 'user1'
36
+ assert_equal [@task_one], ForemanTasks::Task.search_for("user ~ #{glob}")
37
+ end
38
+
39
+ test 'can search the tasks by array' do
40
+ assert_equal [@task_one], ForemanTasks::Task.search_for("user ^ (this_user, #{@user_one.login}, that_user)")
41
+ end
24
42
  end
25
43
 
26
44
  describe 'authorization filtering' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman-tasks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.0
4
+ version: 0.14.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Nečas
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-31 00:00:00.000000000 Z
11
+ date: 2018-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: foreman-tasks-core
@@ -33,7 +33,7 @@ dependencies:
33
33
  version: '1.0'
34
34
  - - ">="
35
35
  - !ruby/object:Gem::Version
36
- version: 1.1.0
36
+ version: 1.1.1
37
37
  type: :runtime
38
38
  prerelease: false
39
39
  version_requirements: !ruby/object:Gem::Requirement
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: '1.0'
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: 1.1.0
46
+ version: 1.1.1
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: sinatra
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -297,7 +297,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
297
297
  version: '0'
298
298
  requirements: []
299
299
  rubyforge_project:
300
- rubygems_version: 2.6.12
300
+ rubygems_version: 2.7.3
301
301
  signing_key:
302
302
  specification_version: 4
303
303
  summary: Foreman plugin for showing tasks information for resoruces and users