foreman_remote_execution 0.1.2 → 0.2.1

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.
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