foreman-tasks 0.3.6 → 0.4.0

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.
@@ -1,15 +1,9 @@
1
1
  module ForemanTasks
2
2
  module Concerns
3
- module ActionSubject # TODO slit to two modules: auto planning, acting as action subject
3
+ module ActionSubject
4
4
 
5
5
  extend ActiveSupport::Concern
6
6
 
7
- included do
8
- after_create :plan_create_action
9
- after_update :plan_update_action
10
- after_destroy :plan_destroy_action
11
- end
12
-
13
7
  module ClassMethods
14
8
  def available_locks
15
9
  [:read, :write]
@@ -49,115 +43,6 @@ module ForemanTasks
49
43
  mine + mine.reduce(Set.new) { |s, resource| s + get_all_related_resources.(resource) }
50
44
  end
51
45
 
52
- # to make the triggered action synchronous
53
- def sync_action!
54
- @dynflow_sync_action = true
55
- end
56
-
57
- def sync_action_flag_reset!
58
- @dynflow_sync_action = false
59
- end
60
-
61
- def create_action
62
- end
63
-
64
- def update_action
65
- end
66
-
67
- def destroy_action
68
- end
69
-
70
- def plan_create_action
71
- plan_action(create_action, self) if create_action
72
- return true
73
- end
74
-
75
- def plan_update_action
76
- plan_action(update_action, self) if update_action
77
- return true
78
- end
79
-
80
- def plan_destroy_action
81
- plan_action(destroy_action, self) if destroy_action
82
- return true
83
- end
84
-
85
- # Perform planning phase of the action tied with the model event.
86
- # We do it separately from the execution phase, because the transaction
87
- # of planning phase is expected to be commited when execution occurs. Also
88
- # we want to be able to rollback the whole db operation when planning fails.
89
- def plan_action(action_class, *args)
90
- @execution_plan = ::ForemanTasks.dynflow.world.plan(action_class, *args)
91
- raise @execution_plan.errors.first if @execution_plan.error?
92
- end
93
-
94
- def save(*)
95
- dynflow_task_wrap(:save) { super }
96
- end
97
-
98
- def save!(*)
99
- dynflow_task_wrap(:save) { super }
100
- end
101
-
102
- def destroy
103
- dynflow_task_wrap(:destroy) { super }
104
- end
105
-
106
- # Makes sure the execution plan is executed AFTER the transaction is commited.
107
- # We can't user after_commit filters because they don't allow to raise
108
- # exceptions in there, so we would not be able to report that something
109
- # went wrong when running a sync_task.:
110
- #
111
- # http://guides.rubyonrails.org/v3.2.14/active_record_validations_callbacks.html#transaction-callbacks
112
- #
113
- # That's why we need to override save and destroy methods instead.
114
- # Another reason why one should avoid callbacks for orchestration.
115
- #
116
- # Also, it makes sure the save is not run inside other transaction because
117
- # we would start the execution phase inside this transaction which would lead
118
- # to unexpected results.
119
- def dynflow_task_wrap(method)
120
- action = case method
121
- when :save
122
- self.new_record? ? create_action : update_action
123
- when :destroy
124
- destroy_action
125
- else
126
- raise 'unexpected method'
127
- end
128
- if action
129
- ensure_not_in_transaction!
130
- yield.tap do |result|
131
- execute_planned_action if result
132
- sync_action_flag_reset!
133
- end
134
- else
135
- yield
136
- end
137
- end
138
-
139
- # we don't want to start executing the task calling to external services
140
- # when inside some other transaction. Might lead to unexpected results
141
- def ensure_not_in_transaction!
142
- if self.class.connection.open_transactions > 0
143
- raise "Executing dynflow action inside a transaction is not a good idea"
144
- end
145
- end
146
-
147
- # Execute the prepared execution plan after the db transaction was commited
148
- def execute_planned_action
149
- if @execution_plan
150
- run = ::ForemanTasks.dynflow.world.execute(@execution_plan.id)
151
- if @dynflow_sync_action
152
- run.wait
153
- if run.value.error?
154
- task = ForemanTasks::Task::DynflowTask.find_by_external_id!(@execution_plan.id)
155
- raise ForemanTasks::TaskError.new(task)
156
- end
157
- end
158
- end
159
- return true
160
- end
161
46
  end
162
47
  end
163
48
  end
@@ -0,0 +1,129 @@
1
+ module ForemanTasks
2
+ module Concerns
3
+ module ActionTriggering
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ after_create :plan_create_action
9
+ after_update :plan_update_action
10
+ after_destroy :plan_destroy_action
11
+ end
12
+
13
+ # @override
14
+ def create_action
15
+ end
16
+
17
+ # @override
18
+ def update_action
19
+ end
20
+
21
+ # @override
22
+ def destroy_action
23
+ end
24
+
25
+ def save(*)
26
+ dynflow_task_wrap(:save) { super }
27
+ end
28
+
29
+ def save!(*)
30
+ dynflow_task_wrap(:save) { super }
31
+ end
32
+
33
+ def destroy
34
+ dynflow_task_wrap(:destroy) { super }
35
+ end
36
+
37
+ protected
38
+
39
+ def sync_action_flag_reset!
40
+ @dynflow_sync_action = false
41
+ end
42
+
43
+ # to make the triggered action synchronous
44
+ def sync_action!
45
+ @dynflow_sync_action = true
46
+ end
47
+
48
+ def plan_create_action
49
+ plan_action(create_action, self) if create_action
50
+ return true
51
+ end
52
+
53
+ def plan_update_action
54
+ plan_action(update_action, self) if update_action
55
+ return true
56
+ end
57
+
58
+ def plan_destroy_action
59
+ plan_action(destroy_action, self) if destroy_action
60
+ return true
61
+ end
62
+
63
+ # Perform planning phase of the action tied with the model event.
64
+ # We do it separately from the execution phase, because the transaction
65
+ # of planning phase is expected to be commited when execution occurs. Also
66
+ # we want to be able to rollback the whole db operation when planning fails.
67
+ def plan_action(action_class, *args)
68
+ @execution_plan = ::ForemanTasks.dynflow.world.plan(action_class, *args)
69
+ raise @execution_plan.errors.first if @execution_plan.error?
70
+ end
71
+
72
+ # Makes sure the execution plan is executed AFTER the transaction is commited.
73
+ # We can't user after_commit filters because they don't allow to raise
74
+ # exceptions in there, so we would not be able to report that something
75
+ # went wrong when running a sync_task.:
76
+ #
77
+ # http://guides.rubyonrails.org/v3.2.14/active_record_validations_callbacks.html#transaction-callbacks
78
+ #
79
+ # That's why we need to override save and destroy methods instead.
80
+ # Another reason why one should avoid callbacks for orchestration.
81
+ #
82
+ # Also, it makes sure the save is not run inside other transaction because
83
+ # we would start the execution phase inside this transaction which would lead
84
+ # to unexpected results.
85
+ def dynflow_task_wrap(method)
86
+ action = case method
87
+ when :save
88
+ self.new_record? ? create_action : update_action
89
+ when :destroy
90
+ destroy_action
91
+ else
92
+ raise 'unexpected method'
93
+ end
94
+ if action
95
+ ensure_not_in_transaction!
96
+ yield.tap do |result|
97
+ execute_planned_action if result
98
+ sync_action_flag_reset!
99
+ end
100
+ else
101
+ yield
102
+ end
103
+ end
104
+
105
+ # we don't want to start executing the task calling to external services
106
+ # when inside some other transaction. Might lead to unexpected results
107
+ def ensure_not_in_transaction!
108
+ if self.class.connection.open_transactions > 0
109
+ raise "Executing dynflow action inside a transaction is not a good idea"
110
+ end
111
+ end
112
+
113
+ # Execute the prepared execution plan after the db transaction was commited
114
+ def execute_planned_action
115
+ if @execution_plan
116
+ run = ::ForemanTasks.dynflow.world.execute(@execution_plan.id)
117
+ if @dynflow_sync_action
118
+ run.wait
119
+ if run.value.error?
120
+ task = ForemanTasks::Task::DynflowTask.find_by_external_id!(@execution_plan.id)
121
+ raise ForemanTasks::TaskError.new(task)
122
+ end
123
+ end
124
+ end
125
+ return true
126
+ end
127
+ end
128
+ end
129
+ end
@@ -3,6 +3,7 @@ module ForemanTasks
3
3
  module ArchitectureActionSubject
4
4
  extend ActiveSupport::Concern
5
5
  include ForemanTasks::Concerns::ActionSubject
6
+ include ForemanTasks::Concerns::ActionTriggering
6
7
 
7
8
  def create_action
8
9
  ::Actions::Foreman::Architecture::Create
@@ -40,8 +40,13 @@
40
40
  <span class="param-name"><%= _("Progress") %>:</span>
41
41
  <span class="param-value">
42
42
  <div class="progress progress-striped">
43
- <div class="bar" style="width: <%= 100 * @task.progress %>%;"></div>
43
+ <% progress = 100 * @task.progress %>
44
+ <div class="progress-bar" role="progressbar" aria-valuenow="<%= progress %>"
45
+ aria-valuemin="0" aria-valuemax="100" style="width: <%= progress %>%">
46
+ <span class="sr-only"><%= progress %>% Complete</span>
47
+ </div>
44
48
  </div>
49
+ </span>
45
50
  </div>
46
51
  <% if @task.cli_example %>
47
52
  <div>
@@ -1,3 +1,3 @@
1
1
  module ForemanTasks
2
- VERSION = "0.3.6"
2
+ VERSION = "0.4.0"
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.3.6
4
+ version: 0.4.0
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-03-10 00:00:00.000000000 Z
12
+ date: 2014-03-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -143,6 +143,7 @@ files:
143
143
  - app/controllers/foreman_tasks/tasks_controller.rb
144
144
  - app/controllers/foreman_tasks/concerns/hosts_controller_extension.rb
145
145
  - app/models/foreman_tasks/concerns/architecture_action_subject.rb
146
+ - app/models/foreman_tasks/concerns/action_triggering.rb
146
147
  - app/models/foreman_tasks/concerns/host_action_subject.rb
147
148
  - app/models/foreman_tasks/concerns/action_subject.rb
148
149
  - app/models/foreman_tasks/task/dynflow_task.rb