foreman-tasks 0.2.1 → 0.2.2

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.
@@ -12,7 +12,6 @@ module ForemanTasks
12
12
  after_create :plan_create_action
13
13
  after_update :plan_update_action
14
14
  after_destroy :plan_destroy_action
15
- after_commit :execute_planned_action
16
15
  end
17
16
 
18
17
  module ClassMethods
@@ -97,11 +96,50 @@ module ForemanTasks
97
96
  # we want to be able to rollback the whole db operation when planning fails.
98
97
  def plan_action(action_class, *args)
99
98
  @execution_plan = ::ForemanTasks.dynflow.world.plan(action_class, *args)
100
- planned = @execution_plan.state == :planned
101
- unless planned
102
- errors = @execution_plan.steps.values.map(&:error).compact
103
- # we raise error so that the whole transaction is rollbacked
104
- raise errors.map(&:message).join('; ')
99
+ raise @execution_plan.errors.first if @execution_plan.error?
100
+ end
101
+
102
+ def save(*)
103
+ dynflow_task_wrap(:save) { super }
104
+ end
105
+
106
+ def save!(*)
107
+ dynflow_task_wrap(:save) { super }
108
+ end
109
+
110
+ def destroy
111
+ dynflow_task_wrap(:destroy) { super }
112
+ end
113
+
114
+ # Makes sure the execution plan is executed AFTER the transaction is commited.
115
+ # We can't user after_commit filters because they don't allow to raise
116
+ # exceptions in there, so we would not be able to report that something
117
+ # went wrong when running a sync_task.:
118
+ #
119
+ # http://guides.rubyonrails.org/v3.2.14/active_record_validations_callbacks.html#transaction-callbacks
120
+ #
121
+ # That's why we need to override save and destroy methods instead.
122
+ # Another reason why one should avoid callbacks for orchestration.
123
+ #
124
+ # Also, it makes sure the save is not run inside other transaction because
125
+ # we would start the execution phase inside this transaction which would lead
126
+ # to unexpected results.
127
+ def dynflow_task_wrap(method)
128
+ action = case method
129
+ when :save
130
+ self.new_record? ? create_action : update_action
131
+ when :destroy
132
+ destroy_action
133
+ else
134
+ raise 'unexpected method'
135
+ end
136
+ if action
137
+ if self.class.connection.open_transactions > 0
138
+ raise "Executing dynflow action inside a transaction is not a good idea"
139
+ end
140
+ yield.tap { |result| execute_planned_action if result }
141
+ else
142
+ yield
105
143
  end
106
144
  end
107
145
 
@@ -109,7 +147,13 @@ module ForemanTasks
109
147
  def execute_planned_action
110
148
  if @execution_plan
111
149
  run = ::ForemanTasks.dynflow.world.execute(@execution_plan.id)
112
- run.finished.wait if @dynflow_sync_action
150
+ if @dynflow_sync_action
151
+ run.wait
152
+ if run.value.error?
153
+ task = ForemanTasks::Task::DynflowTask.find_by_external_id!(@execution_plan.id)
154
+ raise ForemanTasks::TaskError.new(task)
155
+ end
156
+ end
113
157
  end
114
158
  return true
115
159
  end
data/lib/foreman_tasks.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'foreman_tasks/version'
2
+ require 'foreman_tasks/task_error'
2
3
  require 'foreman_tasks/engine'
3
4
  require 'foreman_tasks/dynflow'
4
5
  require 'foreman_tasks/triggers'
@@ -34,8 +35,8 @@ module ForemanTasks
34
35
  end
35
36
 
36
37
  def self.sync_task(action, *args, &block)
37
- # TODO raise aggregation error when there are failed run-steps
38
- trigger_task false, action, *args, &block
38
+ trigger_task(false, action, *args, &block).tap do |task|
39
+ raise TaskError.new(task) if task.execution_plan.error?
40
+ end
39
41
  end
40
-
41
42
  end
@@ -0,0 +1,19 @@
1
+ module ForemanTasks
2
+ class TaskError < StandardError
3
+
4
+ attr_reader :task
5
+ attr_reader :errors
6
+
7
+ def initialize(task)
8
+ @task = task
9
+ @errors = task.execution_plan.steps.values.map(&:error).compact
10
+ super(aggregated_message)
11
+ end
12
+
13
+ def aggregated_message
14
+ "Task #{task.id}: " +
15
+ errors.map { |e| "#{e.exception_class}: #{e.message}" }.join('; ')
16
+ end
17
+
18
+ end
19
+ end
@@ -1,3 +1,3 @@
1
1
  module ForemanTasks
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.2"
3
3
  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.2.1
4
+ version: 0.2.2
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: 2014-02-19 00:00:00.000000000 Z
12
+ date: 2014-02-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -156,6 +156,7 @@ files:
156
156
  - lib/foreman_tasks/engine.rb
157
157
  - lib/foreman_tasks/version.rb
158
158
  - lib/foreman_tasks/triggers.rb
159
+ - lib/foreman_tasks/task_error.rb
159
160
  - lib/foreman_tasks/dynflow/configuration.rb
160
161
  - lib/foreman_tasks/dynflow/persistence.rb
161
162
  - lib/foreman_tasks/dynflow/daemon.rb