foreman-tasks 0.6.12 → 0.6.13

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.
@@ -10,7 +10,7 @@ module ForemanTasks
10
10
  part.to_s
11
11
  end
12
12
  end.join('; ')
13
- h(parts.join(" "))
13
+ parts.join(" ")
14
14
  end
15
15
  end
16
16
  end
@@ -0,0 +1,30 @@
1
+ module Actions
2
+
3
+ class Actions::ActionWithSubPlans < Actions::EntryAction
4
+
5
+ middleware.use Actions::Middleware::KeepCurrentUser
6
+
7
+ include Dynflow::Action::WithSubPlans
8
+
9
+ def plan(*args)
10
+ raise NotImplementedError
11
+ end
12
+
13
+ def humanized_output
14
+ return unless counts_set?
15
+ _("%{total} tasks, %{success} success, %{failed} fail") %
16
+ { total: output[:total_count],
17
+ success: output[:success_count],
18
+ failed: output[:failed_count] }
19
+ end
20
+
21
+ def run_progress
22
+ if counts_set? && output[:total_count] > 0
23
+ (output[:success_count] + output[:failed_count]).to_f / output[:total_count]
24
+ else
25
+ 0.1
26
+ end
27
+ end
28
+
29
+ end
30
+ end
@@ -1,14 +1,6 @@
1
1
  module Actions
2
2
 
3
- class BulkAction < Actions::EntryAction
4
-
5
- middleware.use Actions::Middleware::KeepCurrentUser
6
-
7
- SubPlanFinished = Algebrick.type do
8
- fields! :execution_plan_id => String,
9
- :success => type { variants TrueClass, FalseClass }
10
- end
11
-
3
+ class BulkAction < Actions::ActionWithSubPlans
12
4
  # == Parameters:
13
5
  # actions_class::
14
6
  # Class of action to trigger on targets
@@ -25,7 +17,15 @@ module Actions
25
17
  end
26
18
 
27
19
  def humanized_name
28
- _("Bulk action")
20
+ if task.sub_tasks.first
21
+ task.sub_tasks.first.humanized[:action]
22
+ else
23
+ _("Bulk action")
24
+ end
25
+ end
26
+
27
+ def rescue_strategy
28
+ Dynflow::Action::Rescue::Skip
29
29
  end
30
30
 
31
31
  def humanized_input
@@ -36,32 +36,6 @@ module Actions
36
36
  end
37
37
  end
38
38
 
39
- def humanized_output
40
- return unless counts_set?
41
- _("%{total} tasks, %{success} success, %{failed} fail") %
42
- { total: output[:total_count],
43
- success: output[:success_count],
44
- failed: output[:failed_count] }
45
- end
46
-
47
- def run(event = nil)
48
- case(event)
49
- when nil
50
- if output[:total_count]
51
- resume
52
- else
53
- initiate_sub_plans
54
- end
55
- when SubPlanFinished
56
- mark_as_done(event.execution_plan_id, event.success)
57
- if done?
58
- check_for_errors!
59
- else
60
- suspend
61
- end
62
- end
63
- end
64
-
65
39
  # @api override when the logic for the initiation of the subtasks
66
40
  # is different from the default one
67
41
  def create_sub_plans
@@ -70,85 +44,10 @@ module Actions
70
44
  targets = target_class.where(:id => input[:target_ids])
71
45
 
72
46
  targets.map do |target|
73
- ForemanTasks.trigger(action_class, target, *input[:args])
74
- end
75
- end
76
-
77
- def initiate_sub_plans
78
- output.update(total_count: 0,
79
- failed_count: 0,
80
- success_count: 0)
81
-
82
- planned, failed = create_sub_plans.partition(&:planned?)
83
-
84
- sub_plan_ids = ((planned + failed).map(&:execution_plan_id))
85
- set_parent_task_id(sub_plan_ids)
86
-
87
- output[:total_count] = sub_plan_ids.size
88
- output[:failed_count] = failed.size
89
-
90
- if planned.any?
91
- wait_for_sub_plans(planned)
92
- else
93
- check_for_errors!
94
- end
95
- end
96
-
97
- def resume
98
- if task.sub_tasks.active.any?
99
- fail _("Some sub tasks are still not finished")
47
+ trigger(action_class, target, *input[:args])
100
48
  end
101
49
  end
102
50
 
103
- def rescue_strategy
104
- Dynflow::Action::Rescue::Skip
105
- end
106
-
107
- def wait_for_sub_plans(plans)
108
- suspend do |suspended_action|
109
- plans.each do |plan|
110
- plan.finished.do_then do |value|
111
- suspended_action << SubPlanFinished[plan.execution_plan_id,
112
- value.result == :success]
113
- end
114
- end
115
- end
116
- end
117
-
118
- def mark_as_done(plan_id, success)
119
- if success
120
- output[:success_count] += 1
121
- else
122
- output[:failed_count] += 1
123
- end
124
- end
125
-
126
- def done?
127
- if counts_set?
128
- output[:total_count] - output[:success_count] - output[:failed_count] <= 0
129
- else
130
- false
131
- end
132
- end
133
-
134
- def run_progress
135
- if counts_set?
136
- (output[:success_count] + output[:failed_count]).to_f / output[:total_count]
137
- else
138
- 0.1
139
- end
140
- end
141
-
142
- def counts_set?
143
- output[:total_count] && output[:success_count] && output[:failed_count]
144
- end
145
-
146
- def set_parent_task_id(sub_plan_ids)
147
- ForemanTasks::Task::DynflowTask.
148
- where(external_id: sub_plan_ids).
149
- update_all(parent_task_id: task.id)
150
- end
151
-
152
51
  def check_targets!(targets)
153
52
  if targets.empty?
154
53
  fail _("Empty bulk action")
@@ -158,9 +57,5 @@ module Actions
158
57
  end
159
58
  end
160
59
 
161
- def check_for_errors!
162
- fail _("A sub task failed") if output[:failed_count] > 0
163
- end
164
-
165
60
  end
166
61
  end
@@ -57,7 +57,8 @@ module ForemanTasks
57
57
 
58
58
  # returns a scope of the locks colliding with this one
59
59
  def colliding_locks
60
- colliding_locks_scope = Lock.active.where(Lock.arel_table[:task_id].not_eq(task_id))
60
+ task_ids = task.self_and_parents.map(&:id)
61
+ colliding_locks_scope = Lock.active.where(Lock.arel_table[:task_id].not_in(task_ids))
61
62
  colliding_locks_scope = colliding_locks_scope.where(name: name,
62
63
  resource_id: resource_id,
63
64
  resource_type: resource_type)
@@ -77,6 +77,14 @@ module ForemanTasks
77
77
  self.state == 'paused'
78
78
  end
79
79
 
80
+ def self_and_parents
81
+ [self].tap do |ret|
82
+ if parent_task
83
+ ret.concat(parent_task.self_and_parents)
84
+ end
85
+ end
86
+ end
87
+
80
88
  def self.search_by_generic_resource(key, operator, value)
81
89
  key = "resource_type" if key.blank?
82
90
  key_name = self.connection.quote_column_name(key.sub(/^.*\./,''))
@@ -6,12 +6,17 @@ module ForemanTasks
6
6
  scope :for_action, ->(action_class) { where(label: action_class.name) }
7
7
 
8
8
  def update_from_dynflow(data)
9
- self.external_id = data[:id]
10
- self.started_at = data[:started_at]
11
- self.ended_at = data[:ended_at]
12
- self.state = data[:state].to_s
13
- self.result = data[:result].to_s
14
- self.label ||= main_action.class.name
9
+ self.external_id = data[:id]
10
+ self.started_at = data[:started_at]
11
+ self.ended_at = data[:ended_at]
12
+ self.state = data[:state].to_s
13
+ self.result = data[:result].to_s
14
+ self.parent_task_id ||= begin
15
+ if main_action.caller_execution_plan_id
16
+ DynflowTask.find_by_external_id!(main_action.caller_execution_plan_id).id
17
+ end
18
+ end
19
+ self.label ||= main_action.class.name
15
20
  self.save!
16
21
  end
17
22
 
@@ -124,6 +124,8 @@
124
124
  'progress-bar-success'
125
125
  when 'error'
126
126
  'progress-bar-danger'
127
+ when 'warning'
128
+ 'progress-bar-warning'
127
129
  else
128
130
  nil
129
131
  end
@@ -143,9 +145,21 @@
143
145
  <%= progress.round %>%
144
146
  </div>
145
147
  </div>
148
+
149
+ <% unless @task.humanized[:output].blank? %>
146
150
  <div>
147
151
  <span class="param-name"><%= _("Output") %>:</span>
148
152
  <span class="param-value">
149
153
  <pre><%= @task.humanized[:output] %></pre>
150
154
  </span>
151
155
  </div>
156
+ <% end %>
157
+
158
+ <% unless @task.humanized[:errors].blank? %>
159
+ <div>
160
+ <span class="param-name"><%= _("Errors") %>:</span>
161
+ <span class="param-value">
162
+ <pre><%= @task.humanized[:errors].join("\n") %></pre>
163
+ </span>
164
+ </div>
165
+ <% end %>
@@ -2,9 +2,6 @@
2
2
  <% title_actions help_path%>
3
3
 
4
4
  <style>
5
- .task-id {
6
- white-space: nowrap;
7
- }
8
5
  .param-name {
9
6
  display: inline-block;
10
7
  width: 10em;
@@ -37,7 +34,7 @@ $(document).on('click', ".table-two-pane td.two-pane-link", function(e) {
37
34
  <% for task in @tasks %>
38
35
  <tr>
39
36
  <td class="task-id two-pane-link">
40
- <%= link_to_if_authorized(format_task_input(task, true),
37
+ <%= link_to_if_authorized(trunc_with_tooltip(format_task_input(task, true), 80),
41
38
  hash_for_foreman_tasks_task_path(:id => task)) %>
42
39
  </td>
43
40
  <td><%= task.state %></td>
@@ -87,8 +87,10 @@ module ForemanTasks
87
87
 
88
88
 
89
89
  rake_tasks do
90
- load File.expand_path('../tasks/dynflow.rake', __FILE__)
91
- load File.expand_path('../tasks/test.rake', __FILE__)
90
+ %w[dynflow.rake test.rake].each do |rake_file|
91
+ full_path = File.expand_path("../tasks/#{rake_file}", __FILE__)
92
+ load full_path if File.exists?(full_path)
93
+ end
92
94
  end
93
95
  end
94
96
 
@@ -1,3 +1,3 @@
1
1
  module ForemanTasks
2
- VERSION = "0.6.12"
2
+ VERSION = "0.6.13"
3
3
  end
@@ -12,10 +12,8 @@ module ForemanTasks
12
12
  end
13
13
 
14
14
  it 'formats the task input properly' do
15
- expects(:h).with("user 'Anonymous Admin'")
16
- format_task_input(@task)
17
- expects(:h).with("Create user 'Anonymous Admin'")
18
- format_task_input(@task, true)
15
+ format_task_input(@task).must_equal("user 'Anonymous Admin'")
16
+ format_task_input(@task, true).must_equal("Create user 'Anonymous Admin'")
19
17
  end
20
18
 
21
19
  end
@@ -38,10 +36,8 @@ module ForemanTasks
38
36
 
39
37
  it 'formats the task input properly' do
40
38
  response = "product 'product-2'; organization 'test-0'"
41
- expects(:h).with(response)
42
- format_task_input(@task)
43
- expects(:h).with("Create #{response}")
44
- format_task_input(@task, true)
39
+ format_task_input(@task).must_equal(response)
40
+ format_task_input(@task, true).must_equal("Create #{response}")
45
41
  end
46
42
  end
47
43
  end
@@ -0,0 +1,58 @@
1
+ require "foreman_tasks_test_helper"
2
+
3
+ module ForemanTasks
4
+ class ActionWithSubPlansTest < ActiveSupport::TestCase
5
+ self.use_transactional_fixtures = false
6
+
7
+ before do
8
+ User.current = User.where(:login => 'apiadmin').first
9
+ end
10
+
11
+ # to be able to use the locking
12
+ class ::User < User.parent
13
+ include ForemanTasks::Concerns::ActionSubject
14
+ end
15
+
16
+ class ParentAction < Actions::ActionWithSubPlans
17
+ def plan(user)
18
+ action_subject(user)
19
+ plan_self(user_id: user.id)
20
+ end
21
+
22
+ def create_sub_plans
23
+ user = User.find(input[:user_id])
24
+ trigger(ChildAction, user)
25
+ end
26
+ end
27
+
28
+ class ChildAction < Actions::EntryAction
29
+ def plan(user)
30
+ action_subject(user)
31
+ plan_self(user_id: user.id)
32
+ end
33
+ def run
34
+ end
35
+ end
36
+
37
+ describe Actions::ActionWithSubPlans do
38
+ let(:task) do
39
+ user = FactoryGirl.create(:user)
40
+ triggered = ForemanTasks.trigger(ParentAction, user)
41
+ raise triggered.error if triggered.respond_to?(:error)
42
+ triggered.finished.wait(2)
43
+ ForemanTasks::Task.find_by_external_id(triggered.id)
44
+ end
45
+
46
+ specify "the sub-plan stores the information about its parent" do
47
+ task.sub_tasks.size.must_equal 1
48
+ task.sub_tasks.first.label.must_equal ChildAction.name
49
+ end
50
+
51
+ specify "the locks of the sub-plan don't colide with the locks of its parent" do
52
+ child_task = task.sub_tasks.first
53
+ assert(child_task.locks.any? { |lock| lock.name == 'write' }, "it's locks don't conflict with parent's")
54
+ end
55
+ end
56
+
57
+ end
58
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman-tasks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.12
4
+ version: 0.6.13
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-01-30 00:00:00.000000000 Z
12
+ date: 2015-03-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: dynflow
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - ! '>='
20
20
  - !ruby/object:Gem::Version
21
- version: 0.7.2
21
+ version: 0.7.7
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
- version: 0.7.2
29
+ version: 0.7.7
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: sequel
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -116,6 +116,7 @@ files:
116
116
  - app/lib/actions/foreman/architecture/destroy.rb
117
117
  - app/lib/actions/foreman/host/import_facts.rb
118
118
  - app/lib/actions/entry_action.rb
119
+ - app/lib/actions/action_with_sub_plans.rb
119
120
  - app/lib/actions/base.rb
120
121
  - app/lib/actions/middleware/keep_current_user.rb
121
122
  - app/views/foreman_tasks/tasks/_details.html.erb
@@ -140,7 +141,6 @@ files:
140
141
  - lib/tasks/gettext.rake
141
142
  - lib/foreman-tasks.rb
142
143
  - lib/foreman_tasks/tasks/dynflow.rake
143
- - lib/foreman_tasks/tasks/test.rake
144
144
  - lib/foreman_tasks/version.rb
145
145
  - lib/foreman_tasks/triggers.rb
146
146
  - lib/foreman_tasks/dynflow.rb
@@ -160,6 +160,7 @@ files:
160
160
  - test/controllers/api/tasks_controller_test.rb
161
161
  - test/factories/task_factory.rb
162
162
  - test/unit/dynflow_console_authorizer_test.rb
163
+ - test/unit/actions/action_with_sub_plans_test.rb
163
164
  - test/unit/task_test.rb
164
165
  homepage: https://github.com/theforeman/foreman-tasks
165
166
  licenses: []
@@ -192,5 +193,6 @@ test_files:
192
193
  - test/controllers/api/tasks_controller_test.rb
193
194
  - test/factories/task_factory.rb
194
195
  - test/unit/dynflow_console_authorizer_test.rb
196
+ - test/unit/actions/action_with_sub_plans_test.rb
195
197
  - test/unit/task_test.rb
196
198
  has_rdoc:
@@ -1,22 +0,0 @@
1
- namespace :test do
2
- task :foreman_tasks => 'db:test:prepare' do
3
- test_task = Rake::TestTask.new('foreman_tasks_test_task') do |t|
4
- t.libs << ["test", "#{ForemanTasks::Engine.root}/test"]
5
- t.test_files = ["#{ForemanTasks::Engine.root}/test/**/*_test.rb"]
6
- t.verbose = true
7
- end
8
-
9
- Rake::Task[test_task.name].invoke
10
- end
11
- end
12
-
13
- Rake::Task[:test].enhance do
14
- Rake::Task['test:foreman_tasks'].invoke
15
- end
16
-
17
- load 'tasks/jenkins.rake'
18
- if Rake::Task.task_defined?(:'jenkins:unit')
19
- Rake::Task["jenkins:unit"].enhance do
20
- Rake::Task['test:foreman_tasks'].invoke
21
- end
22
- end