foreman-tasks 0.2.1 → 0.2.2

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