foreman_remote_execution 0.0.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 (83) hide show
  1. checksums.yaml +15 -0
  2. data/LICENSE +619 -0
  3. data/README.md +54 -0
  4. data/Rakefile +47 -0
  5. data/app/assets/javascripts/template_input.js +9 -0
  6. data/app/assets/javascripts/template_invocation.js +32 -0
  7. data/app/assets/stylesheets/job_invocations.css.scss +35 -0
  8. data/app/assets/stylesheets/template_invocation.css.scss +16 -0
  9. data/app/controllers/api/v2/job_templates_controller.rb +108 -0
  10. data/app/controllers/job_invocations_controller.rb +35 -0
  11. data/app/controllers/job_templates_controller.rb +35 -0
  12. data/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb +40 -0
  13. data/app/helpers/remote_execution_helper.rb +88 -0
  14. data/app/lib/actions/remote_execution/run_host_job.rb +93 -0
  15. data/app/lib/actions/remote_execution/run_hosts_job.rb +35 -0
  16. data/app/lib/actions/remote_execution/run_proxy_command.rb +34 -0
  17. data/app/models/concerns/foreman_remote_execution/bookmark_extensions.rb +9 -0
  18. data/app/models/concerns/foreman_remote_execution/foreman_tasks_task_extensions.rb +9 -0
  19. data/app/models/concerns/foreman_remote_execution/host_extensions.rb +19 -0
  20. data/app/models/concerns/foreman_remote_execution/template_extensions.rb +20 -0
  21. data/app/models/concerns/foreman_remote_execution/template_relations.rb +10 -0
  22. data/app/models/concerns/foreman_remote_execution/user_extensions.rb +9 -0
  23. data/app/models/input_template_renderer.rb +42 -0
  24. data/app/models/job_invocation.rb +21 -0
  25. data/app/models/job_invocation_composer.rb +210 -0
  26. data/app/models/job_template.rb +52 -0
  27. data/app/models/remote_execution_provider.rb +17 -0
  28. data/app/models/setting/remote_execution.rb +19 -0
  29. data/app/models/ssh_execution_provider.rb +2 -0
  30. data/app/models/targeting.rb +56 -0
  31. data/app/models/targeting_host.rb +9 -0
  32. data/app/models/template_input.rb +154 -0
  33. data/app/models/template_invocation.rb +13 -0
  34. data/app/models/template_invocation_input_value.rb +8 -0
  35. data/app/views/api/v2/job_templates/base.json.rabl +3 -0
  36. data/app/views/api/v2/job_templates/create.json.rabl +3 -0
  37. data/app/views/api/v2/job_templates/index.json.rabl +3 -0
  38. data/app/views/api/v2/job_templates/main.json.rabl +5 -0
  39. data/app/views/api/v2/job_templates/show.json.rabl +9 -0
  40. data/app/views/job_invocations/_form.html.erb +67 -0
  41. data/app/views/job_invocations/_tab_hosts.html.erb +33 -0
  42. data/app/views/job_invocations/_tab_overview.html.erb +41 -0
  43. data/app/views/job_invocations/index.html.erb +30 -0
  44. data/app/views/job_invocations/new.html.erb +8 -0
  45. data/app/views/job_invocations/refresh.js.erb +1 -0
  46. data/app/views/job_invocations/show.html.erb +21 -0
  47. data/app/views/job_templates/_custom_tab_headers.html.erb +2 -0
  48. data/app/views/job_templates/_custom_tabs.html.erb +28 -0
  49. data/app/views/job_templates/auto_complete_job_name.json.erb +3 -0
  50. data/app/views/job_templates/edit.html.erb +6 -0
  51. data/app/views/job_templates/index.html.erb +33 -0
  52. data/app/views/job_templates/new.html.erb +6 -0
  53. data/app/views/template_inputs/_form.html.erb +22 -0
  54. data/config/routes.rb +35 -0
  55. data/db/migrate/20150612121541_add_job_template_to_template.rb +6 -0
  56. data/db/migrate/20150616080015_create_template_input.rb +19 -0
  57. data/db/migrate/20150708133241_add_targeting.rb +25 -0
  58. data/db/migrate/20150708133242_add_invocation.rb +11 -0
  59. data/db/migrate/20150708133305_add_template_invocation.rb +22 -0
  60. data/db/migrate/20150812110800_add_resolved_at_to_targeting.rb +5 -0
  61. data/db/migrate/20150812145900_add_last_task_id_to_job_invocation.rb +6 -0
  62. data/db/seeds.d/60-ssh_proxy_feature.rb +2 -0
  63. data/lib/foreman_remote_execution/engine.rb +119 -0
  64. data/lib/foreman_remote_execution/version.rb +3 -0
  65. data/lib/foreman_remote_execution.rb +6 -0
  66. data/lib/tasks/foreman_remote_execution_tasks.rake +49 -0
  67. data/locale/Makefile +62 -0
  68. data/locale/en/foreman_remote_execution.po +19 -0
  69. data/locale/foreman_remote_execution.pot +19 -0
  70. data/locale/gemspec.rb +2 -0
  71. data/test/factories/foreman_remote_execution_factories.rb +48 -0
  72. data/test/functional/api/v2/job_templates_controller_test.rb +74 -0
  73. data/test/test_plugin_helper.rb +8 -0
  74. data/test/unit/actions/run_hosts_job_test.rb +40 -0
  75. data/test/unit/actions/run_proxy_command_test.rb +30 -0
  76. data/test/unit/input_template_renderer_test.rb +366 -0
  77. data/test/unit/job_invocation_composer_test.rb +415 -0
  78. data/test/unit/job_invocation_test.rb +31 -0
  79. data/test/unit/job_template_test.rb +5 -0
  80. data/test/unit/remote_execution_provider_test.rb +51 -0
  81. data/test/unit/targeting_test.rb +107 -0
  82. data/test/unit/template_input_test.rb +25 -0
  83. metadata +195 -0
@@ -0,0 +1,3 @@
1
+ object @job_template
2
+
3
+ extends "api/v2/job_templates/show"
@@ -0,0 +1,3 @@
1
+ collection @job_templates
2
+
3
+ extends "api/v2/job_templates/main"
@@ -0,0 +1,5 @@
1
+ object @job_template
2
+
3
+ extends "api/v2/job_templates/base"
4
+
5
+ attributes :snippet, :audit_comment, :created_at, :updated_at
@@ -0,0 +1,9 @@
1
+ object @job_template
2
+
3
+ extends "api/v2/job_templates/main"
4
+
5
+ attributes :template, :locked
6
+
7
+ node do |job_template|
8
+ partial("api/v2/taxonomies/children_nodes", :object => job_template)
9
+ end
@@ -0,0 +1,67 @@
1
+ <%= form_for @composer.job_invocation, :html => { 'data-refresh-url' => refresh_job_invocations_path, :id => 'job_invocation_form' } do |f| %>
2
+ <%= selectable_f f, :job_name, @composer.available_job_names %>
3
+
4
+ <%= fields_for @composer.targeting do |targeting_fields| %>
5
+ <%= targeting_fields.hidden_field :bookmark_id, :value => params[:bookmark_id] %>
6
+ <%= textarea_f targeting_fields, :search_query, :value => @composer.displayed_search_query, :rows => 5 %>
7
+
8
+ <div class="form-group ">
9
+ <label class="col-md-2 control-label"><%= _('Resolves to') %></label>
10
+ <div class="col-md-4">
11
+ <%= @composer.targeted_hosts_count %> <%= _('hosts') %>
12
+ <%= button_tag(:type => 'button', :class => 'btn btn-default btn-sm', :title => _("Refresh"), :id => 'refresh_execution_form') do %>
13
+ <%= icon_text('refresh') %>
14
+ <% end %>
15
+ </div>
16
+ </div>
17
+
18
+ <div class="form-group ">
19
+ <label class="col-md-2 control-label"><%= _('Type of query') %></label>
20
+ <div class="col-md-4">
21
+ <%= radio_button_f targeting_fields, :targeting_type, :value => Targeting::STATIC_TYPE, :text => _(Targeting::TYPES[Targeting::STATIC_TYPE]) %>
22
+ <%= radio_button_f targeting_fields, :targeting_type, :value => Targeting::DYNAMIC_TYPE, :text => _(Targeting::TYPES[Targeting::DYNAMIC_TYPE]) %>
23
+ </div>
24
+ <span class="help-inline"><%= popover(_('Explanation'),
25
+ _("Type has impact on when is the query evaulated to hosts.<br><ul><li><b>Static</b> - evaluates just after you submit this form</li><li><b>Dynamic</b> - evaluates just before the execution is started, so if it's planed in future, targeted hosts set may change before it</li></ul>")) %></span>
26
+ </div>
27
+ <% end %>
28
+
29
+ <% @composer.displayed_provider_types.each do |provider_type| %>
30
+ <fieldset id="provider_<%= provider_type %>" class="provider_form">
31
+ <legend><%= _('Provider') + ': ' + provider_type %></legend>
32
+ <%= f.fields_for 'providers' do |providers_fields| %>
33
+ <%= providers_fields.fields_for provider_type do |provider_type_fields| %>
34
+ <%= provider_type_fields.fields_for :job_templates do |job_templates_fields| %>
35
+ <% if @composer.needs_provider_type_selection? %>
36
+ <%= radio_button_f provider_type_fields, 'job_template_id', :value => '', :text => _('Disable this provider'),
37
+ :class => 'job_template_selector', :checked => @composer.preselect_disabled_for_provider(provider_type) %>
38
+ <span />
39
+ <% end %>
40
+ <% @composer.templates_for_provider(provider_type).each do |job_template| %>
41
+ <%= radio_button_f provider_type_fields, 'job_template_id',
42
+ :value => job_template.id,
43
+ :text => job_template.name,
44
+ :class => 'job_template_selector',
45
+ :checked => @composer.job_template_ids.include?(job_template.id) || @composer.only_one_template_available? %>
46
+
47
+ <fieldset id="job_template_<%= job_template.id %>" class="job_template_form <%= 'hidden' if !@composer.job_template_ids.include?(job_template.id) && !@composer.only_one_template_available? %>">
48
+ <%= job_templates_fields.fields_for job_template.id.to_s do |job_template_fields| %>
49
+ <%= job_template_fields.fields_for :input_values do |input_values_fields| %>
50
+ <% job_template.template_inputs.where(:input_type => 'user').each do |input| %>
51
+ <%= input_values_fields.fields_for input.id.to_s, @composer.template_invocation_input_value_for(input) do |input_fields| %>
52
+ <%= text_f input_fields, :value, :label => input.name, :help_inline => input.description, :required => input.required %>
53
+ <% end %>
54
+ <% end %>
55
+ <% end %>
56
+ <% end %>
57
+ </fieldset>
58
+ <% end %>
59
+ <% end %>
60
+ <% end %>
61
+ <% end %>
62
+ </fieldset>
63
+ <% end %>
64
+
65
+
66
+ <%= submit_or_cancel f %>
67
+ <% end %>
@@ -0,0 +1,33 @@
1
+ <% if job_invocation.last_task.present? %>
2
+ <table class="table table-bordered table-striped table-condensed">
3
+ <thead>
4
+ <tr>
5
+ <th><%= _('Host') %></th>
6
+ <th><%= _('Provider') %></th>
7
+ <th><%= _('Status') %></th>
8
+ <th><%= _('Actions') %></th>
9
+ </tr>
10
+ </thead>
11
+
12
+ <tbody>
13
+ <% job_invocation.last_task.sub_tasks.for_action_types('Actions::RemoteExecution::RunHostJob').each do |task| %>
14
+ <tr>
15
+ <% template_invocation = task.locks.where(:resource_type => 'TemplateInvocation').first.try(:resource) %>
16
+ <% if (host = task.locks.where(:resource_type => 'Host::Managed').first.try(:resource)) %>
17
+ <td><%= link_to_if_authorized host.name, hash_for_host_path(host).merge(:auth_object => host, :permission => :view_hosts) %></td>
18
+ <% else %>
19
+ <td><%= _('host not linked') %></td>
20
+ <% end %>
21
+
22
+ <td><%= template_invocation.nil? ? _('N/A') : _(RemoteExecutionProvider.provider_for(template_invocation.template.provider_type)) %></td>
23
+ <td><%= template_invocation_status(task) %></td>
24
+ <td><%= action_buttons(
25
+ display_link_if_authorized(_("Details"), hash_for_foreman_tasks_task_path(:id => task).merge(:auth_object => host, :permission => :view_foreman_tasks))
26
+ ) %></td>
27
+ </tr>
28
+ <% end %>
29
+ </tbody>
30
+ </table>
31
+ <% else %>
32
+ <%= _('No host could be found, this can happen if the action was not executed yet. Try visiting this page later.') %>
33
+ <% end %>
@@ -0,0 +1,41 @@
1
+ <div id="status_chart">
2
+ <%= job_invocation_chart(job_invocation) %>
3
+ </div>
4
+
5
+ <div class="col-md-6 infoblock">
6
+ <h4><%= _('Target hosts') %></h4>
7
+ <% if job_invocation.bookmark.present? %>
8
+ <%= _('Bookmark') %> <%= job_invocation.bookmark.name %><br>
9
+ <% else %>
10
+ <%= _('Manual selection') %>
11
+ <% end %>
12
+ <%= 'using ' + _(Targeting::TYPES[job_invocation.targeting.targeting_type]) %>
13
+ <pre><%= job_invocation.targeting.search_query %></pre>
14
+
15
+ <%= _('Evaluated at:') %> <%= job_invocation.targeting.resolved_at %><br>
16
+ <% if job_invocation.template_invocations.size > 1 %>
17
+ <% job_invocation.template_invocations.each do |template_invocation| %>
18
+ <%= host_counter _(RemoteExecutionProvider.provider_for(template_invocation.template.provider_type)), ForemanTasks::Task::DynflowTask.for_action(Actions::RemoteExecution::RunHostJob).for_resource(template_invocation).uniq.size %>
19
+ <% end %>
20
+ <% end %>
21
+ <%= host_counter(_('Total hosts'), job_invocation.last_task.output['total_count']) if job_invocation.last_task %>
22
+ </div>
23
+
24
+ <div class="col-md-6 infoblock">
25
+ <h4><%= _('Providers and templates') %></h4>
26
+ <% job_invocation.template_invocations.each do |template_invocation| %>
27
+ <h5>
28
+ <b><%= template_invocation.template.name %></b> <%= 'through' %> <%= _(RemoteExecutionProvider.provider_for(template_invocation.template.provider_type)) %>
29
+ </h5>
30
+ <pre><%= InputTemplateRenderer.new(template_invocation.template, nil, template_invocation).preview %></pre>
31
+
32
+ <% if template_invocation.input_values.present? %>
33
+ <%= _('following user inputs were provided') %>
34
+ <ul>
35
+ <% template_invocation.input_values.joins(:template_input).each do |input_value| %>
36
+ <li><b><%= input_value.template_input.name %></b>: <%= input_value.value %></li>
37
+ <% end %>
38
+ </ul>
39
+ <% end %>
40
+ <% end %>
41
+ </div>
@@ -0,0 +1,30 @@
1
+ <% title _('Job invocations') %>
2
+
3
+ <table class="table table-bordered table-striped table-condensed">
4
+ <thead>
5
+ <tr>
6
+ <th><%= _('Job name') %></th>
7
+ <th><%= _('Status') %></th>
8
+ <th><%= _('Succeeded') %></th>
9
+ <th><%= _('Failed') %></th>
10
+ <th><%= _('Pending') %></th>
11
+ <th><%= _('Total hosts') %></th>
12
+ </tr>
13
+ </thead>
14
+
15
+ <tbody>
16
+ <% @job_invocations.each do |invocation| %>
17
+ <tr>
18
+ <td><%= link_to_if_authorized invocation.job_name, hash_for_job_invocation_path(invocation).merge(:auth_object => invocation, :permission => :view_job_invocation) %></td>
19
+ <td><%= link_to_invocation_task_if_authorized(invocation) %></td>
20
+ <td><%= invocation_count(invocation, :output_key => :success_count) %></td>
21
+ <td><%= invocation_count(invocation, :output_key => :failed_count) %></td>
22
+ <td><%= invocation_count(invocation, :output_key => :pending_count) %></td>
23
+ <td><%= invocation_count(invocation, :output_key => :total_count) %></td>
24
+ </tr>
25
+ <% end %>
26
+ </tbody>
27
+
28
+ </table>
29
+
30
+ <%= will_paginate_with_info @job_invocations %>
@@ -0,0 +1,8 @@
1
+ <% javascript 'template_invocation' %>
2
+ <% stylesheet 'template_invocation' %>
3
+
4
+ <div class="row form-group">
5
+ <h1 class="col-md-8"><%= _('New Job Invocation') %></h1>
6
+ </div>
7
+
8
+ <%= render :partial => 'form' %>
@@ -0,0 +1 @@
1
+ $('form#job_invocation_form').html("<%=j render :partial => 'form' %>");
@@ -0,0 +1,21 @@
1
+ <% title @job_invocation.job_name %>
2
+ <% stylesheet 'job_invocations' %>
3
+
4
+ <% if @job_invocation.last_task %>
5
+ <% title_actions(job_invocation_task_buttons(@job_invocation.last_task)) %>
6
+ <% end %>
7
+
8
+ <ul class="nav nav-tabs" data-tabs="tabs">
9
+ <li class="active"><a href="#primary" data-toggle="tab"><%= _('Overview') %></a></li>
10
+ <li><a href="#hosts" data-toggle="tab"><%= _('Hosts') %></a></li>
11
+ </ul>
12
+
13
+ <div class="tab-content">
14
+ <div class="tab-pane active" id="primary">
15
+ <%= render 'tab_overview', :job_invocation => @job_invocation %>
16
+ </div>
17
+
18
+ <div class="tab-pane" id="hosts">
19
+ <%= render 'tab_hosts', :job_invocation => @job_invocation %>
20
+ </div>
21
+ </div>
@@ -0,0 +1,2 @@
1
+ <li><a href="#template_job" data-toggle="tab"><%= _("Job") %></a></li>
2
+ <li><a href="#template_type" data-toggle="tab"><%= _("Type") %></a></li>
@@ -0,0 +1,28 @@
1
+ <div class="tab-pane" id="template_job">
2
+
3
+ <%= field(f, :job_name) do %>
4
+ <%= auto_complete_search(:job_name,
5
+ f.object.job_name,
6
+ :placeholder => _("Job name") + ' ...',
7
+ :name => 'job_template[job_name]',
8
+ :id => 'search') %>
9
+ <% end %>
10
+
11
+ <%= select_f f, :provider_type, providers_options, :first, :last %>
12
+
13
+ <div class="children_fields">
14
+ <%= new_child_fields_template(f, :template_inputs, { :partial => "template_inputs/form" }) %>
15
+ <%= f.fields_for :template_inputs do |ff| %>
16
+ <div class="template_input_form fields">
17
+ <%= render 'template_inputs/form', :f => ff %>
18
+ </div>
19
+ <% end %>
20
+ <%= add_child_link '+ ' + _("Add Input"), :template_inputs, { :title => _('add a input for this template')} %>
21
+ </div>
22
+
23
+ </div>
24
+
25
+
26
+ <div class="tab-pane" id="template_type">
27
+ <%= checkbox_f f, :snippet, :onchange => "snippet_changed(this)", :label=>_('Snippet'), :disabled => @template.locked? %>
28
+ </div>
@@ -0,0 +1,3 @@
1
+ [
2
+ <%= @job_names.map { |name| '{"completed": "","part": "' + name + '","label": "' + name + '","category": ""}' }.join(',').html_safe %>
3
+ ]
@@ -0,0 +1,6 @@
1
+ <%= javascript 'lookup_keys' %>
2
+ <%= javascript 'template_input' %>
3
+
4
+ <% title _("Edit Job Template") %>
5
+
6
+ <%= render :partial => 'form' %>
@@ -0,0 +1,33 @@
1
+ <%= include_javascript %>
2
+ <%= javascript 'lookup_keys' %>
3
+ <%= javascript 'template_input' %>
4
+
5
+ <% title _("Job Templates") %>
6
+ <% title_actions display_link_if_authorized(_("New Job Template"), hash_for_new_job_template_path) %>
7
+
8
+ <table class="table table-bordered table-striped table-two-pane">
9
+ <thead>
10
+ <tr>
11
+ <th><%= sort :name, :as => s_("JobTemplate|Name") %></th>
12
+ <th><%= sort :snippet, :as => s_("JobTemplate|Snippet") %></th>
13
+ <th><%= sort :locked, :as => s_("JobTemplate|Locked"), :default => "DESC" %></th>
14
+ <th></th>
15
+ </tr>
16
+ </thead>
17
+ <tbody>
18
+ <% for job_template in @templates %>
19
+ <tr>
20
+ <td class="display-two-pane"><%= link_to_if_authorized trunc_with_tooltip(job_template),
21
+ hash_for_edit_job_template_path(:id => job_template).merge(:auth_object => job_template, :authorizer => authorizer) %></td>
22
+ <td align='center'><%= checked_icon job_template.snippet %></td>
23
+ <td align='center'>
24
+ <%= locked_icon job_template.locked?, _("This template is locked for editing.") %>
25
+ </td>
26
+ <td>
27
+ <%= action_buttons(*permitted_actions(job_template)) %>
28
+ </td>
29
+ </tr>
30
+ <% end %>
31
+ </tbody>
32
+ </table>
33
+ <%= will_paginate_with_info @templates %>
@@ -0,0 +1,6 @@
1
+ <%= javascript 'lookup_keys' %>
2
+ <%= javascript 'template_input' %>
3
+
4
+ <% title _("New Job Template") %>
5
+
6
+ <%= render :partial => 'form' %>
@@ -0,0 +1,22 @@
1
+ <div class="fields">
2
+ <div class="form-group">
3
+ <%= field_set_tag _("Template input").html_safe +
4
+ ' ' +
5
+ remove_child_link('x', f, {:rel => 'twipsy', "data-title" => _('remove template input'), :'data-placement' => 'left', :class => 'fr badge badge-danger'}).html_safe do %>
6
+ <%= text_f f, :name %>
7
+ <%= checkbox_f f, :required %>
8
+ <%= selectable_f f, :input_type, template_input_types_options, {}, :class => 'input_type_selector' %>
9
+ <div class="fact_input_type custom_input_type_fields" style="<%= f.object.fact_template_input? ? '' : 'display:none' %>">
10
+ <%= text_f f, :fact_name, :class => 'fact_input_type', :required => true %>
11
+ </div>
12
+ <div class="variable_input_type custom_input_type_fields" style="<%= f.object.variable_template_input? ? '' : 'display:none' %>">
13
+ <%= text_f f, :variable_name, :class => 'variable_input_type', :required => true %>
14
+ </div>
15
+ <div class="puppet_parameter_input_type custom_input_type_fields" style="<%= f.object.puppet_parameter_template_input? ? '' : 'display:none' %>">
16
+ <%= text_f f, :puppet_class_name, :class => 'puppet_parameter_input_type', :required => true %>
17
+ <%= text_f f, :puppet_parameter_name, :class => 'puppet_parameter_input_type', :required => true %>
18
+ </div>
19
+ <%= textarea_f f, :description, :rows => 3 %>
20
+ <% end %>
21
+ </div>
22
+ </div>
data/config/routes.rb ADDED
@@ -0,0 +1,35 @@
1
+ Rails.application.routes.draw do
2
+ resources :job_templates, :except => [:show] do
3
+ member do
4
+ get 'clone_template'
5
+ get 'lock'
6
+ get 'unlock'
7
+ post 'preview'
8
+ end
9
+ collection do
10
+ get 'revision'
11
+ get 'auto_complete_search'
12
+ get 'auto_complete_job_name'
13
+ end
14
+ end
15
+
16
+ resources :job_invocations, :only => [:new, :create, :show, :index] do
17
+ collection do
18
+ post 'refresh'
19
+ get 'auto_complete_search'
20
+ end
21
+ end
22
+
23
+ namespace :api, :defaults => {:format => 'json'} do
24
+ scope "(:apiv)", :module => :v2, :defaults => {:apiv => 'v2'}, :apiv => /v1|v2/, :constraints => ApiConstraints.new(:version => 2, :default => true) do
25
+ resources :job_templates, :except => [:new, :edit] do
26
+ (resources :locations, :only => [:index, :show]) if SETTINGS[:locations_enabled]
27
+ (resources :organizations, :only => [:index, :show]) if SETTINGS[:organizations_enabled]
28
+ post :clone, :on => :member
29
+ collection do
30
+ get 'revision'
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,6 @@
1
+ class AddJobTemplateToTemplate < ActiveRecord::Migration
2
+ def change
3
+ add_column :templates, :job_name, :string
4
+ add_column :templates, :provider_type, :string
5
+ end
6
+ end
@@ -0,0 +1,19 @@
1
+ class CreateTemplateInput < ActiveRecord::Migration
2
+ def change
3
+ create_table :template_inputs do |t|
4
+ t.string :name, :null => false
5
+ t.boolean :required, :null => false, :default => false
6
+ t.string :input_type, :null => false
7
+ t.string :fact_name
8
+ t.string :variable_name
9
+ t.string :puppet_class_name
10
+ t.string :puppet_parameter_name
11
+ t.text :description
12
+ t.integer :template_id
13
+
14
+ t.timestamps
15
+ end
16
+
17
+ add_foreign_key :template_inputs, :templates, :name => "templates_template_id_fk", :column => 'template_id'
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ class AddTargeting < ActiveRecord::Migration
2
+ def change
3
+ create_table :targetings do |t|
4
+ t.string :search_query
5
+ t.references :bookmark
6
+ t.references :user
7
+ t.string :targeting_type, :null => false
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :targetings, [:bookmark_id], :name => "targetings_bookmark_id"
12
+ add_index :targetings, [:user_id], :name => "targetings_user_id"
13
+ add_foreign_key :targetings, :bookmarks, :name => "targetings_bookmark_id", :column => 'bookmark_id'
14
+ add_foreign_key :targetings, :users, :name => "targetings_user_id", :column => 'user_id'
15
+
16
+ create_table :targeting_hosts do |t|
17
+ t.references :host, :null => false
18
+ t.references :targeting, :null => false
19
+ end
20
+
21
+ add_index :targeting_hosts, [:host_id, :targeting_id], :name => "targeting_hosts_host_targeting_ids"
22
+ add_foreign_key :targeting_hosts, :hosts, :name => "targeting_hosts_host_id", :column => 'host_id'
23
+ add_foreign_key :targeting_hosts, :targetings, :name => "targeting_hosts_targeting_id", :column => 'targeting_id'
24
+ end
25
+ end
@@ -0,0 +1,11 @@
1
+ class AddInvocation< ActiveRecord::Migration
2
+ def change
3
+ create_table :job_invocations do |t|
4
+ t.references :targeting, :null => false
5
+ t.string :job_name, :null => false
6
+ end
7
+
8
+ add_index :job_invocations, [:targeting_id], :name => "job_invocations_targeting_id"
9
+ add_foreign_key :job_invocations, :targetings, :name => "job_invocation_targeting_id", :column => 'targeting_id'
10
+ end
11
+ end
@@ -0,0 +1,22 @@
1
+ class AddTemplateInvocation < ActiveRecord::Migration
2
+ def change
3
+ create_table :template_invocations do |t|
4
+ t.references :template, :null => false
5
+ t.references :job_invocation, :null => false
6
+ end
7
+
8
+ add_index :template_invocations, [:template_id, :job_invocation_id], :name => "targeting_invocation_template_ji_ids"
9
+ add_foreign_key :template_invocations, :templates, :name => "template_invoc_template_id", :column => 'template_id'
10
+ add_foreign_key :template_invocations, :job_invocations, :name => "template_invoc_job_invocation_id", :column => 'job_invocation_id'
11
+
12
+ create_table :template_invocation_input_values do |t|
13
+ t.references :template_invocation, :null => false
14
+ t.references :template_input, :null => false
15
+ t.string :value, :null => false
16
+ end
17
+
18
+ add_index :template_invocation_input_values, [:template_invocation_id, :template_input_id], :name => "template_invocation_input_values_ti_ti_ids"
19
+ add_foreign_key :template_invocation_input_values, :template_invocations, :name => "template_invoc_input_values_template_invoc_id", :column => 'template_invocation_id'
20
+ add_foreign_key :template_invocation_input_values, :template_inputs, :name => "template_invoc_input_values_template_input_id", :column => 'template_input_id'
21
+ end
22
+ end
@@ -0,0 +1,5 @@
1
+ class AddResolvedAtToTargeting < ActiveRecord::Migration
2
+ def change
3
+ add_column :targetings, :resolved_at, :datetime
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ class AddLastTaskIdToJobInvocation < ActiveRecord::Migration
2
+ def change
3
+ add_column :job_invocations, :last_task_id, :string
4
+ add_index :job_invocations, [:last_task_id], :name => 'job_invocations_last_task_id'
5
+ end
6
+ end
@@ -0,0 +1,2 @@
1
+ f = Feature.find_or_create_by_name('Ssh')
2
+ raise "Unable to create proxy feature: #{format_errors f}" if f.nil? || f.errors.any?
@@ -0,0 +1,119 @@
1
+ module ForemanRemoteExecution
2
+ class Engine < ::Rails::Engine
3
+ engine_name 'foreman_remote_execution'
4
+
5
+ config.autoload_paths += Dir["#{config.root}/app/controllers/concerns"]
6
+ config.autoload_paths += Dir["#{config.root}/app/helpers/concerns"]
7
+ config.autoload_paths += Dir["#{config.root}/app/models/concerns"]
8
+
9
+
10
+ initializer 'foreman_remote_execution.load_default_settings', :before => :load_config_initializers do
11
+ require_dependency File.expand_path('../../../app/models/setting/remote_execution.rb', __FILE__) if (Setting.table_exists? rescue(false))
12
+ end
13
+
14
+ # Add any db migrations
15
+ initializer 'foreman_remote_execution.load_app_instance_data' do |app|
16
+ app.config.paths['db/migrate'] += ForemanRemoteExecution::Engine.paths['db/migrate'].existent
17
+ end
18
+
19
+ initializer "foreman_remote_execution.apipie" do
20
+ Apipie.configuration.checksum_path += ['/api/']
21
+ end
22
+
23
+ initializer "foreman_remote_execution.require_dynflow", :before => "foreman_tasks.initialize_dynflow" do |app|
24
+ ForemanTasks.dynflow.require!
25
+ ForemanTasks.dynflow.config.eager_load_paths << File.join(ForemanRemoteExecution::Engine.root, 'app/lib/actions')
26
+ end
27
+
28
+ initializer 'foreman_remote_execution.register_plugin', after: :finisher_hook do |_app|
29
+ Foreman::Plugin.register :foreman_remote_execution do
30
+ requires_foreman '>= 1.9'
31
+
32
+ apipie_documented_controllers ["#{ForemanRemoteExecution::Engine.root}/app/controllers/api/v2/*.rb"]
33
+
34
+ # Add permissions
35
+ security_block :foreman_remote_execution do
36
+ permission :view_job_templates, { :job_templates => [:index, :show, :revision, :auto_complete_search, :auto_complete_job_name, :preview],
37
+ :'api/v2/job_templates' => [:index, :show, :revision] }, :resource_type => 'JobTemplate'
38
+ permission :create_job_templates, { :job_templates => [:new, :create, :clone_template],
39
+ :'api/v2/job_templates' => [:create, :clone] }, :resource_type => 'JobTemplate'
40
+ permission :edit_job_templates, { :job_templates => [:edit, :update],
41
+ :'api/v2/job_templates' => [:update] }, :resource_type => 'JobTemplate'
42
+ permission :destroy_job_templates, { :job_templates => [:destroy],
43
+ :'api/v2/job_templates' => [:destroy] }, :resource_type => 'JobTemplate'
44
+ permission :lock_job_templates, { :job_templates => [:lock, :unlock] }, :resource_type => 'JobTemplate'
45
+
46
+ permission :view_job_invocations, { :job_invocations => [:index, :show, :auto_complete_search] }, :resource_type => 'JobInvocation'
47
+
48
+ permission :create_job_invocations, { :job_invocations => [:new, :create, :refresh] }, :resource_type => 'JobInvocation'
49
+ end
50
+
51
+ # Add a new role called 'ForemanRemoteExecution' if it doesn't exist
52
+ # role 'ForemanRemoteExecution', [:view_foreman_remote_execution]
53
+
54
+ # add menu entry
55
+ menu :top_menu, :job_templates,
56
+ url_hash: { controller: :job_templates, action: :index },
57
+ caption: N_('Job templates'),
58
+ parent: :hosts_menu,
59
+ after: :provisioning_templates
60
+
61
+ menu :top_menu, :job_invocations,
62
+ url_hash: { controller: :job_invocations, action: :index },
63
+ caption: N_('Jobs'),
64
+ parent: :monitor_menu,
65
+ after: :audits
66
+
67
+ # add dashboard widget
68
+ # widget 'foreman_remote_execution_widget', name: N_('Foreman plugin template widget'), sizex: 4, sizey: 1
69
+ end
70
+ end
71
+
72
+ # Precompile any JS or CSS files under app/assets/
73
+ # If requiring files from each other, list them explicitly here to avoid precompiling the same
74
+ # content twice.
75
+ assets_to_precompile =
76
+ Dir.chdir(root) do
77
+ Dir['app/assets/javascripts/**/*', 'app/assets/stylesheets/**/*'].map do |f|
78
+ f.split(File::SEPARATOR, 4).last
79
+ end
80
+ end
81
+ initializer 'foreman_remote_execution.assets.precompile' do |app|
82
+ app.config.assets.precompile += assets_to_precompile
83
+ end
84
+ initializer 'foreman_remote_execution.configure_assets', group: :assets do
85
+ SETTINGS[:foreman_remote_execution] = { assets: { precompile: assets_to_precompile } }
86
+ end
87
+
88
+ # Include concerns in this config.to_prepare block
89
+ config.to_prepare do
90
+ # we have to define associations in Template and all descendants because
91
+ # reflect_on_association does not work when we add association after parent and child class
92
+ # are already loaded, causing issues when you try to destroy any child which iterates
93
+ # over all associations because of :dependant => :destroy
94
+ #
95
+ # e.g. having ProvisioningTemplate < Template, adding has_many :template_inputs to Template from concern
96
+ # Template.reflect_on_association :template_inputs # => <#Association...
97
+ # ProvisioningTemplate.reflect_on_association :template_inputs # => nil
98
+ require_dependency 'job_template'
99
+ (Template.descendants + [Template]).each { |klass| klass.send(:include, ForemanRemoteExecution::TemplateRelations) }
100
+ # similarly, attr_accessible :template_inputs_attributes does not work with STI
101
+ (Template.descendants + [Template]).each { |klass| klass.send(:include, ForemanRemoteExecution::TemplateExtensions) }
102
+
103
+ User.send(:include, ForemanRemoteExecution::UserExtensions)
104
+ (Host::Base.descendants + [Host::Base]).each do |klass|
105
+ klass.send(:include, ForemanRemoteExecution::HostExtensions)
106
+ klass.send(:include, ForemanTasks::Concerns::HostActionSubject)
107
+ end
108
+ Bookmark.send(:include, ForemanRemoteExecution::BookmarkExtensions)
109
+ HostsHelper.send(:include, ForemanRemoteExecution::HostsHelperExtensions)
110
+ ForemanTasks::Task.send(:include, ForemanRemoteExecution::ForemanTasksTaskExtensions)
111
+ end
112
+
113
+ initializer 'foreman_remote_execution.register_gettext', after: :load_config_initializers do |_app|
114
+ locale_dir = File.join(File.expand_path('../../..', __FILE__), 'locale')
115
+ locale_domain = 'foreman_remote_execution'
116
+ Foreman::Gettext::Support.add_text_domain locale_domain, locale_dir
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,3 @@
1
+ module ForemanRemoteExecution
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,6 @@
1
+ require 'foreman-tasks'
2
+
3
+ require 'foreman_remote_execution/engine'
4
+
5
+ module ForemanRemoteExecution
6
+ end