foreman_remote_execution 0.1.2 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/.rubocop_todo.yml +0 -6
  4. data/app/assets/javascripts/template_input.js +5 -0
  5. data/app/assets/javascripts/template_invocation.js +44 -12
  6. data/app/controllers/api/v2/foreign_input_sets_controller.rb +80 -0
  7. data/app/controllers/api/v2/job_invocations_controller.rb +28 -14
  8. data/app/controllers/api/v2/job_templates_controller.rb +24 -20
  9. data/app/controllers/api/v2/template_inputs_controller.rb +10 -7
  10. data/app/controllers/job_invocations_controller.rb +18 -6
  11. data/app/controllers/job_templates_controller.rb +14 -4
  12. data/app/controllers/template_invocations_controller.rb +5 -3
  13. data/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb +1 -1
  14. data/app/helpers/concerns/foreman_remote_execution/job_templates_extensions.rb +19 -0
  15. data/app/helpers/remote_execution_helper.rb +88 -39
  16. data/app/lib/actions/remote_execution/run_host_job.rb +11 -8
  17. data/app/lib/actions/remote_execution/run_hosts_job.rb +5 -2
  18. data/app/lib/actions/remote_execution/run_proxy_command.rb +9 -4
  19. data/app/models/concerns/foreman_remote_execution/errors_flattener.rb +2 -2
  20. data/app/models/concerns/foreman_remote_execution/host_extensions.rb +2 -2
  21. data/app/models/concerns/foreman_remote_execution/smart_proxy_extensions.rb +1 -1
  22. data/app/models/concerns/foreman_remote_execution/template_extensions.rb +9 -1
  23. data/app/models/foreign_input_set.rb +49 -0
  24. data/app/models/host_status/execution_status.rb +50 -5
  25. data/app/models/input_template_renderer.rb +52 -7
  26. data/app/models/job_invocation.rb +89 -32
  27. data/app/models/job_invocation_composer.rb +71 -55
  28. data/app/models/job_template.rb +43 -7
  29. data/app/models/remote_execution_provider.rb +1 -1
  30. data/app/models/setting/remote_execution.rb +7 -7
  31. data/app/models/ssh_execution_provider.rb +1 -1
  32. data/app/models/targeting.rb +1 -1
  33. data/app/models/template_invocation.rb +9 -4
  34. data/app/views/api/v2/foreign_input_sets/base.json.rabl +3 -0
  35. data/app/views/api/v2/foreign_input_sets/create.json.rabl +3 -0
  36. data/app/views/api/v2/foreign_input_sets/index.json.rabl +3 -0
  37. data/app/views/api/v2/foreign_input_sets/main.json.rabl +5 -0
  38. data/app/views/api/v2/foreign_input_sets/show.json.rabl +3 -0
  39. data/app/views/api/v2/job_invocations/base.json.rabl +10 -1
  40. data/app/views/api/v2/job_invocations/main.json.rabl +14 -1
  41. data/app/views/api/v2/job_templates/base.json.rabl +1 -1
  42. data/app/views/api/v2/job_templates/main.json.rabl +9 -1
  43. data/app/views/api/v2/job_templates/show.json.rabl +0 -10
  44. data/app/views/api/v2/template_inputs/main.json.rabl +2 -1
  45. data/app/views/job_invocation_task_groups/_job_invocation_task_group.html.erb +1 -1
  46. data/app/views/job_invocations/_description_fields.html.erb +4 -0
  47. data/app/views/job_invocations/_form.html.erb +73 -91
  48. data/app/views/job_invocations/_host_actions_td.html.erb +2 -2
  49. data/app/views/job_invocations/_host_name_td.html.erb +7 -0
  50. data/app/views/job_invocations/_tab_hosts.html.erb +6 -6
  51. data/app/views/job_invocations/_tab_overview.html.erb +3 -3
  52. data/app/views/job_invocations/index.html.erb +6 -4
  53. data/app/views/job_invocations/refresh.js.erb +1 -0
  54. data/app/views/job_invocations/show.html.erb +1 -1
  55. data/app/views/job_invocations/show.js.erb +6 -3
  56. data/app/views/job_templates/_custom_tabs.html.erb +24 -14
  57. data/app/views/job_templates/index.html.erb +3 -1
  58. data/app/views/template_inputs/_foreign_input_set_form.html.erb +12 -0
  59. data/app/views/template_inputs/_form.html.erb +11 -12
  60. data/app/views/template_invocations/show.html.erb +2 -2
  61. data/app/views/templates/package_action.erb +2 -2
  62. data/app/views/templates/puppet_run_once.erb +3 -3
  63. data/app/views/templates/run_command.erb +3 -3
  64. data/app/views/templates/service_action.erb +2 -2
  65. data/app/views/unattended/snippets/_remote_execution_ssh_keys.erb +1 -1
  66. data/config/routes.rb +5 -3
  67. data/db/migrate/20150616080015_create_template_input.rb +1 -1
  68. data/db/migrate/20150708133241_add_targeting.rb +7 -7
  69. data/db/migrate/20150708133242_add_invocation.rb +2 -2
  70. data/db/migrate/20150708133305_add_template_invocation.rb +6 -6
  71. data/db/migrate/20151215114631_add_host_id_to_template_invocation.rb +3 -3
  72. data/db/migrate/20151217092555_migrate_to_task_groups.rb +1 -1
  73. data/db/migrate/20160108134600_create_template_input_sets.rb +16 -0
  74. data/db/migrate/20160108141144_make_job_name_default_to_something.rb +9 -0
  75. data/db/migrate/20160111113032_upcase_ssh_feature.rb +19 -0
  76. data/db/migrate/20160113161916_add_run_host_job_task_id_to_template_invocation.rb +6 -0
  77. data/db/migrate/20160113162007_expand_all_template_invocations.rb +45 -0
  78. data/db/migrate/20160114120200_rename_job_categories.rb +20 -0
  79. data/db/migrate/20160114125628_rename_job_name_to_job_category.rb +19 -0
  80. data/db/seeds.d/60-ssh_proxy_feature.rb +1 -1
  81. data/db/seeds.d/80-provision_templates.rb +2 -2
  82. data/db/seeds.d/90-bookmarks.rb +19 -0
  83. data/doc/plugins/graphviz.rb +5 -5
  84. data/doc/plugins/plantuml.rb +6 -6
  85. data/doc/plugins/tags.rb +4 -4
  86. data/foreman_remote_execution.gemspec +3 -4
  87. data/lib/foreman_remote_execution/engine.rb +12 -9
  88. data/lib/foreman_remote_execution/version.rb +1 -1
  89. data/test/factories/foreman_remote_execution_factories.rb +11 -9
  90. data/test/functional/api/v2/foreign_input_sets_controller_test.rb +63 -0
  91. data/test/functional/api/v2/job_invocations_controller_test.rb +45 -13
  92. data/test/functional/api/v2/job_templates_controller_test.rb +6 -6
  93. data/test/functional/api/v2/template_inputs_controller_test.rb +3 -3
  94. data/test/unit/actions/run_hosts_job_test.rb +3 -4
  95. data/test/unit/actions/run_proxy_command_test.rb +7 -7
  96. data/test/unit/concerns/host_extensions_test.rb +1 -1
  97. data/test/unit/execution_task_status_mapper_test.rb +93 -0
  98. data/test/unit/input_template_renderer_test.rb +182 -9
  99. data/test/unit/job_invocation_composer_test.rb +144 -168
  100. data/test/unit/job_invocation_test.rb +67 -15
  101. data/test/unit/job_template_effective_user_test.rb +2 -2
  102. data/test/unit/job_template_test.rb +36 -12
  103. data/test/unit/remote_execution_provider_test.rb +6 -6
  104. data/test/unit/targeting_test.rb +2 -2
  105. metadata +27 -21
  106. data/app/models/concerns/foreman_remote_execution/template_relations.rb +0 -10
  107. data/app/views/job_invocations/_host_provider_td.html.erb +0 -3
  108. data/app/views/job_templates/auto_complete_job_name.json.erb +0 -3
@@ -4,14 +4,24 @@ class JobInvocationsController < ApplicationController
4
4
  before_filter :find_or_create_triggering, :only => [:create, :refresh]
5
5
 
6
6
  def new
7
- @composer = JobInvocationComposer.from_ui_params(
7
+ ui_params = {
8
8
  :host_ids => params[:host_ids],
9
- :job_invocation => {
10
- },
11
9
  :targeting => {
12
10
  :targeting_type => Targeting::STATIC_TYPE,
13
11
  :bookmark_id => params[:bookmark_id]
14
- })
12
+ }
13
+ }
14
+
15
+ if (template = JobTemplate.find_by_id(params[:template_id]))
16
+ ui_params[:job_invocation] = {
17
+ :job_category => template.job_category,
18
+ :providers => {
19
+ template.provider_type => {:job_template_id => template.id}
20
+ }
21
+ }
22
+ end
23
+
24
+ @composer = JobInvocationComposer.from_ui_params(ui_params)
15
25
  end
16
26
 
17
27
  def rerun
@@ -34,23 +44,25 @@ class JobInvocationsController < ApplicationController
34
44
  @composer.triggering.trigger(::Actions::RemoteExecution::RunHostsJob, job_invocation)
35
45
  redirect_to job_invocation_path(job_invocation)
36
46
  else
47
+ @composer.job_invocation.description_format = nil if params[:job_invocation].key?(:description_override)
37
48
  render :action => 'new'
38
49
  end
39
50
  end
40
51
 
41
52
  def show
42
- @job_invocation = resource_base.find(params[:id])
53
+ @job_invocation = resource_base.includes(:template_invocations => :run_host_job_task).find(params[:id])
43
54
  @auto_refresh = @job_invocation.task.try(:pending?)
44
55
  hosts_base = @job_invocation.targeting.hosts.authorized(:view_hosts, Host)
45
56
  @hosts = hosts_base.search_for(params[:search], :order => params[:order] || 'name ASC').paginate(:page => params[:page])
46
57
  end
47
58
 
48
59
  def index
49
- @job_invocations = resource_base.search_for(params[:search]).paginate(:page => params[:page]).with_task.order(params[:order] || 'job_invocations.id DESC')
60
+ @job_invocations = resource_base.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page]).with_task.order('job_invocations.id DESC')
50
61
  end
51
62
 
52
63
  # refreshes the form
53
64
  def refresh
65
+ params[:job_invocation].delete :description_format if params[:job_invocation].key?(:description_override)
54
66
  @composer = JobInvocationComposer.from_ui_params(params)
55
67
  end
56
68
 
@@ -6,11 +6,13 @@ class JobTemplatesController < ::TemplatesController
6
6
  @organizations = @template.organizations
7
7
  end
8
8
 
9
- def auto_complete_job_name
10
- @job_names = resource_base.where(['job_name LIKE ?', "%#{params[:search]}%"]).pluck(:job_name).uniq
9
+ def auto_complete_job_category
10
+ @job_categories = resource_base.where(['job_category LIKE ?', "%#{params[:search]}%"]).pluck(:job_category).uniq
11
+ render :json => @job_categories.map { |name| { 'completed' => '', 'part' => name, 'label' => name, 'category' => '' } }.to_json
11
12
  end
12
13
 
13
14
  def preview
15
+ find_resource unless @template.present?
14
16
  base = Host.authorized(:view_hosts, Host)
15
17
  host = params[:preview_host_id].present? ? base.find(params[:preview_host_id]) : base.first
16
18
  @template.template = params[:template]
@@ -18,15 +20,23 @@ class JobTemplatesController < ::TemplatesController
18
20
  if (output = renderer.preview)
19
21
  render :text => output
20
22
  else
21
- render :status => 406, :text => renderer.error_message
23
+ render :status => 406, :text => renderer.error_message + _(', note that you must save template input changes before you try to preview it.')
22
24
  end
23
25
  end
24
26
 
25
27
  private
26
28
 
29
+ def find_resource
30
+ if params[:id]
31
+ super
32
+ else
33
+ @template = resource_class.new(params[type_name_plural])
34
+ end
35
+ end
36
+
27
37
  def action_permission
28
38
  case params[:action]
29
- when 'auto_complete_job_name'
39
+ when 'auto_complete_job_category'
30
40
  :view_job_templates
31
41
  else
32
42
  super
@@ -6,9 +6,11 @@ class TemplateInvocationsController < ApplicationController
6
6
  end
7
7
 
8
8
  def show
9
- @template_invocation_task = ForemanTasks::Task.find(params[:id])
10
- @template_invocation = @template_invocation_task.locks.where(:resource_type => 'TemplateInvocation').first.try(:resource)
11
- @host = @template_invocation_task.locks.where(:resource_type => 'Host::Managed').first.try(:resource)
9
+ @template_invocation = TemplateInvocation.find(params[:id])
10
+ raise ActiveRecord::RecordNotFound unless User.current.can?(:view_job_invocations, @template_invocation.job_invocation)
11
+
12
+ @template_invocation_task = @template_invocation.run_host_job_task
13
+ @host = @template_invocation.host
12
14
  @auto_refresh = @template_invocation_task.pending?
13
15
  @since = params[:since].to_f if params[:since].present?
14
16
  @line_sets = @template_invocation_task.main_action.live_output
@@ -15,7 +15,7 @@ module ForemanRemoteExecution
15
15
  end
16
16
 
17
17
  def host_title_actions_with_run_button(*args)
18
- title_actions(button_group(link_to(_("Run Job"), new_job_invocation_path(:host_ids => [args.first.id]), :id => :run_button)))
18
+ title_actions(button_group(link_to(_('Run Job'), new_job_invocation_path(:host_ids => [args.first.id]), :id => :run_button)))
19
19
  host_title_actions_without_run_button(*args)
20
20
  end
21
21
 
@@ -0,0 +1,19 @@
1
+ module ForemanRemoteExecution
2
+ module JobTemplatesExtensions
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ alias_method_chain :permitted_actions, :run_button
7
+ end
8
+
9
+ def permitted_actions_with_run_button(template)
10
+ original = permitted_actions_without_run_button(template)
11
+
12
+ if template.is_a?(JobTemplate) && !template.snippet
13
+ original.unshift(display_link_if_authorized(_('Run'), hash_for_new_job_invocation_path(:template_id => template.id)))
14
+ end
15
+
16
+ original
17
+ end
18
+ end
19
+ end
@@ -30,15 +30,17 @@ module RemoteExecutionHelper
30
30
  end
31
31
 
32
32
  def job_invocation_status(invocation)
33
- if invocation.task.blank?
34
- _('Job not started yet 0%')
35
- elsif invocation.task.state == 'scheduled'
36
- _('Job set to execute at %s') % invocation.task.start_at
37
- elsif invocation.task.state == 'stopped' && invocation.task.result == 'error'
38
- invocation.task.execution_plan.errors.map(&:message).join("\n")
39
- else
40
- label = invocation.task.pending ? _('Running') : _('Finished')
41
- label + ' ' + (invocation.task.progress * 100).to_i.to_s + '%'
33
+ case invocation.status
34
+ when HostStatus::ExecutionStatus::QUEUED
35
+ _('queued')
36
+ when HostStatus::ExecutionStatus::RUNNING
37
+ _('running %s%') % invocation.progress
38
+ when HostStatus::ExecutionStatus::OK
39
+ _('succeeded')
40
+ when HostStatus::ExecutionStatus::ERROR
41
+ _('failed')
42
+ else
43
+ _('unknown status')
42
44
  end
43
45
  end
44
46
 
@@ -77,43 +79,48 @@ module RemoteExecutionHelper
77
79
  end
78
80
  end
79
81
 
80
- def template_invocation_actions(task, host)
81
- if task.nil?
82
- []
83
- else
84
- [display_link_if_authorized(_("Details"), hash_for_template_invocation_path(:id => task).merge(:auth_object => host, :permission => :view_hosts))]
85
- end
82
+ def template_invocation_actions(task, host, job_invocation, template_invocation)
83
+ [
84
+ display_link_if_authorized(_('Host detail'), hash_for_host_path(host).merge(:auth_object => host, :permission => :view_hosts)),
85
+ display_link_if_authorized(_('Rerun on %s') % host.name, hash_for_rerun_job_invocation_path(:id => job_invocation, :host_ids => [ host.id ])),
86
+ ]
86
87
  end
87
88
 
88
- def remote_execution_provider_for(task)
89
- template_invocation = task.locks.where(:resource_type => 'TemplateInvocation').first.try(:resource) unless task.nil?
89
+ def remote_execution_provider_for(template_invocation)
90
90
  template_invocation.nil? ? _('N/A') : template_invocation.template.provider.humanized_name
91
91
  end
92
92
 
93
+ def job_invocations_buttons
94
+ [
95
+ documentation_button_rex('3.2ExecutingaJob'),
96
+ display_link_if_authorized(_('Run Job'), hash_for_new_job_invocation_path)
97
+ ]
98
+ end
99
+
93
100
  # rubocop:disable Metrics/AbcSize
94
101
  def job_invocation_task_buttons(task)
95
102
  job_invocation = task.task_groups.find { |group| group.class == JobInvocationTaskGroup }.job_invocation
96
103
  buttons = []
97
104
  buttons << link_to(_('Refresh'), {}, :class => 'btn btn-default', :title => _('Refresh this page'))
98
105
  if authorized_for(hash_for_new_job_invocation_path)
99
- buttons << link_to(_("Rerun"), rerun_job_invocation_path(:id => job_invocation.id),
100
- :class => "btn btn-default",
106
+ buttons << link_to(_('Rerun'), rerun_job_invocation_path(:id => job_invocation.id),
107
+ :class => 'btn btn-default',
101
108
  :title => _('Rerun the job'))
102
109
  end
103
110
  if authorized_for(hash_for_new_job_invocation_path)
104
- buttons << link_to(_("Rerun failed"), rerun_job_invocation_path(:id => job_invocation.id, :failed_only => 1),
105
- :class => "btn btn-default",
111
+ buttons << link_to(_('Rerun failed'), rerun_job_invocation_path(:id => job_invocation.id, :failed_only => 1),
112
+ :class => 'btn btn-default',
106
113
  :disabled => !task.sub_tasks.any? { |sub_task| task_failed?(sub_task) },
107
114
  :title => _('Rerun on failed hosts'))
108
115
  end
109
116
  if authorized_for(:permission => :view_foreman_tasks, :auth_object => task)
110
- buttons << link_to(_("Job Task"), foreman_tasks_task_path(task),
111
- :class => "btn btn-default",
117
+ buttons << link_to(_('Job Task'), foreman_tasks_task_path(task),
118
+ :class => 'btn btn-default',
112
119
  :title => _('See the last task details'))
113
120
  end
114
121
  if authorized_for(:permission => :edit_foreman_tasks, :auth_object => task)
115
- buttons << link_to(_("Cancel Job"), cancel_foreman_tasks_task_path(task),
116
- :class => "btn btn-danger",
122
+ buttons << link_to(_('Cancel Job'), cancel_foreman_tasks_task_path(task),
123
+ :class => 'btn btn-danger',
117
124
  :title => _('Try to cancel the job'),
118
125
  :disabled => !task.cancellable?,
119
126
  :method => :post)
@@ -125,13 +132,13 @@ module RemoteExecutionHelper
125
132
  def template_invocation_task_buttons(task)
126
133
  buttons = []
127
134
  if authorized_for(:permission => :view_foreman_tasks, :auth_object => task)
128
- buttons << link_to(_("Task Details"), foreman_tasks_task_path(task),
129
- :class => "btn btn-default",
135
+ buttons << link_to(_('Task Details'), foreman_tasks_task_path(task),
136
+ :class => 'btn btn-default',
130
137
  :title => _('See the task details'))
131
138
  end
132
139
  if authorized_for(:permission => :edit_foreman_tasks, :auth_object => task)
133
- buttons << link_to(_("Cancel Job"), cancel_foreman_tasks_task_path(task),
134
- :class => "btn btn-danger",
140
+ buttons << link_to(_('Cancel Job'), cancel_foreman_tasks_task_path(task),
141
+ :class => 'btn btn-danger',
135
142
  :title => _('Try to cancel the job on a host'),
136
143
  :disabled => !task.cancellable?,
137
144
  :method => :post)
@@ -140,20 +147,20 @@ module RemoteExecutionHelper
140
147
  end
141
148
 
142
149
  def link_to_invocation_task_if_authorized(invocation)
143
- if invocation.task.present? && invocation.task.state != 'scheduled'
150
+ if invocation.queued?
151
+ job_invocation_status(invocation)
152
+ else
144
153
  link_to_if_authorized job_invocation_status(invocation),
145
154
  hash_for_foreman_tasks_task_path(invocation.task).merge(:auth_object => invocation.task, :permission => :view_foreman_tasks)
146
- else
147
- job_invocation_status(invocation)
148
155
  end
149
156
  end
150
157
 
151
158
  def invocation_count(invocation, options = {})
152
159
  options = { :unknown_string => 'N/A' }.merge(options)
153
- if invocation.task.nil? || invocation.task.state != 'scheduled'
154
- (invocation.task.try(:output) || {}).fetch(options[:output_key], options[:unknown_string])
155
- else
160
+ if invocation.queued?
156
161
  options[:unknown_string]
162
+ else
163
+ (invocation.task.try(:output) || {}).fetch(options[:output_key], options[:unknown_string])
157
164
  end
158
165
  end
159
166
 
@@ -162,7 +169,7 @@ module RemoteExecutionHelper
162
169
  if (preview = renderer.preview)
163
170
  content_tag :pre, preview
164
171
  else
165
- alert :class => "alert-block alert-danger base in fade has-error",
172
+ alert :class => 'alert-block alert-danger base in fade has-error',
166
173
  :text => renderer.error_message.html_safe
167
174
  end
168
175
  end
@@ -178,13 +185,55 @@ module RemoteExecutionHelper
178
185
  end
179
186
  end
180
187
 
181
- def time_ago(time)
188
+ def time_in_words_span(time)
182
189
  if time.nil?
183
- _('-')
190
+ _('N/A')
184
191
  else
185
- content_tag :span, _("%s ago") % time_ago_in_words(time),
192
+ content_tag :span, (time > Time.now ? _('in %s') : _('%s ago')) % time_ago_in_words(time),
186
193
  { :'data-original-title' => time.try(:in_time_zone), :rel => 'twipsy' }
187
194
  end
188
195
  end
189
196
 
197
+ def documentation_button_rex(section = '')
198
+ url = 'http://theforeman.org/plugins/foreman_remote_execution/' +
199
+ "#{ForemanRemoteExecution::VERSION.split('.').take(2).join('.')}/index.html#" +
200
+ section
201
+ link_to(
202
+ icon_text('help', _('Documentation'),
203
+ :class => 'icon-white', :kind => 'pficon'),
204
+ url,
205
+ :rel => 'external', :class => 'btn btn-info', :target => '_blank')
206
+ end
207
+
208
+ def template_input_header(f, template)
209
+ header = _('Template input')
210
+ header += ' ' + remove_child_link('x', f, {:rel => 'twipsy', :'data-title' => _('remove template input'), :'data-placement' => 'left',
211
+ :class => 'fr badge badge-danger'}) unless template.locked?
212
+ header.html_safe
213
+ end
214
+
215
+ def description_checkbox_f(f, job_template, disabled)
216
+ check_box_tag('description_format_override',
217
+ job_template.generate_description_format,
218
+ f.object.description_format.nil?,
219
+ :class => 'description_format_override',
220
+ :name => f.object_name + '[description_override]',
221
+ :disabled => disabled,
222
+ :onchange => 'description_override(this);') + ' ' + _('Use default description template')
223
+ end
224
+
225
+ def description_format_textarea_f(f, job_template, disabled)
226
+ textarea_f f, 'description_format',
227
+ :label => _('Description template'),
228
+ :value => f.object.description_format || job_template.generate_description_format,
229
+ :rows => 2,
230
+ :onchange => 'regenerate_description(this);',
231
+ :class => 'description_format',
232
+ :disabled => disabled,
233
+ :help_inline => popover(_('Explanation'),
234
+ _('This template is used to generate the description.
235
+ Input values can be used using the syntax %{package}.
236
+ You may also include the job category and template
237
+ name using %{job_category} and %{template_name}.'))
238
+ end
190
239
  end
@@ -10,26 +10,29 @@ module Actions
10
10
  end
11
11
 
12
12
  def plan(job_invocation, host, template_invocation, proxy)
13
- action_subject(host, :job_name => job_invocation.job_name, :description => job_invocation.description)
13
+ action_subject(host, :job_category => job_invocation.job_category, :description => job_invocation.description)
14
+
15
+ template_invocation.host_id = host.id
16
+ template_invocation.run_host_job_task_id = task.id
17
+ template_invocation.save!
14
18
 
15
- template_invocation.update_attribute :host_id, host.id
16
19
  link!(job_invocation)
17
20
  link!(template_invocation)
18
21
 
19
22
  verify_permissions(host, template_invocation)
20
23
  hostname = find_ip_or_hostname(host)
21
24
 
22
- raise _("Could not use any template used in the job invocation") if template_invocation.blank?
25
+ raise _('Could not use any template used in the job invocation') if template_invocation.blank?
23
26
 
24
27
  settings = { :global_proxy => 'remote_execution_global_proxy',
25
28
  :fallback_proxy => 'remote_execution_fallback_proxy' }
26
29
 
27
- raise _("Could not use any proxy. Consider configuring %{global_proxy} " +
28
- "or %{fallback_proxy} in settings") % settings if proxy.blank?
30
+ raise _('Could not use any proxy. Consider configuring %{global_proxy} ' +
31
+ 'or %{fallback_proxy} in settings') % settings if proxy.blank?
29
32
 
30
33
  renderer = InputTemplateRenderer.new(template_invocation.template, host, template_invocation)
31
34
  script = renderer.render
32
- raise _("Failed rendering template: %s") % renderer.error_message unless script
35
+ raise _('Failed rendering template: %s') % renderer.error_message unless script
33
36
 
34
37
  provider = template_invocation.template.provider
35
38
  plan_action(RunProxyCommand, proxy, hostname, script, provider.proxy_command_options(template_invocation, host))
@@ -57,9 +60,9 @@ module Actions
57
60
  end
58
61
 
59
62
  def humanized_name
60
- _('%{description} on %{host}') % { :job_name => input[:job_name],
63
+ _('%{description} on %{host}') % { :job_category => input[:job_category],
61
64
  :host => input[:host][:name],
62
- :description => input[:description].try(:capitalize) || input[:job_name] }
65
+ :description => input[:description].try(:capitalize) || input[:job_category] }
63
66
  end
64
67
 
65
68
  def find_ip_or_hostname(host)
@@ -16,7 +16,7 @@ module Actions
16
16
  task.add_missing_task_groups(job_invocation.task_group) if job_invocation.task_group
17
17
  action_subject(job_invocation) unless locked
18
18
  job_invocation.targeting.resolve_hosts! if job_invocation.targeting.dynamic? || !locked
19
- input.update(:job_name => job_invocation.job_name)
19
+ input.update(:job_category => job_invocation.job_category)
20
20
  plan_self(:job_invocation_id => job_invocation.id)
21
21
  end
22
22
 
@@ -24,8 +24,11 @@ module Actions
24
24
  job_invocation = JobInvocation.find(input[:job_invocation_id])
25
25
  load_balancer = ProxyLoadBalancer.new
26
26
 
27
+ # composer creates just "pattern" for template_invocations because target is evaluated
28
+ # during actual run (here) so we build template invocations from these patterns
27
29
  job_invocation.targeting.hosts.map do |host|
28
- template_invocation = job_invocation.template_invocation_for_host(host)
30
+ template_invocation = job_invocation.pattern_template_invocation_for_host(host).deep_clone
31
+ template_invocation.host_id = host.id
29
32
  proxy = determine_proxy(template_invocation, host, load_balancer)
30
33
  trigger(RunHostJob, job_invocation, host, template_invocation, proxy)
31
34
  end
@@ -21,7 +21,7 @@ module Actions
21
21
  .new(data[:metadata][:exception_message]))
22
22
  else
23
23
  super(data)
24
- error! _("Script execution failed") if failed_run?
24
+ error! _('Script execution failed') if failed_run?
25
25
  end
26
26
  end
27
27
 
@@ -63,7 +63,7 @@ module Actions
63
63
  proxy_data.fetch('output', {}).fetch('result', [])
64
64
  rescue => e
65
65
  ::Foreman::Logging.exception("Failed to load data for task #{task.id} from proxy #{ input[:proxy_url] }", e)
66
- [exception_to_output(_("Error loading data from proxy"), e)]
66
+ [exception_to_output(_('Error loading data from proxy'), e)]
67
67
  end
68
68
 
69
69
  def finalized_output
@@ -76,13 +76,18 @@ module Actions
76
76
  end
77
77
 
78
78
  if exit_status
79
- records << format_output(_("Exit status: %s") % exit_status, 'stdout', task.ended_at)
79
+ records << format_output(_('Exit status: %s') % exit_status, 'stdout', final_timestamp(records))
80
80
  elsif run_step && run_step.error
81
- records << format_output(_("Job finished with error") + ": #{run_step.error.exception_class} - #{run_step.error.message}", 'debug', task.ended_at)
81
+ records << format_output(_('Job finished with error') + ": #{run_step.error.exception_class} - #{run_step.error.message}", 'debug', final_timestamp(records))
82
82
  end
83
83
  return records
84
84
  end
85
85
 
86
+ def final_timestamp(records)
87
+ return task.ended_at if records.blank?
88
+ records.last.fetch('timestamp', task.ended_at) + 1
89
+ end
90
+
86
91
  def proxy_result
87
92
  self.output.fetch(:proxy_output, {}).fetch(:result, []) || []
88
93
  end