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,49 @@
1
+ # Tasks
2
+ namespace :foreman_remote_execution do
3
+ namespace :example do
4
+ desc 'Example Task'
5
+ task task: :environment do
6
+ # Task goes here
7
+ end
8
+ end
9
+ end
10
+
11
+ # Tests
12
+ namespace :test do
13
+ desc 'Test ForemanRemoteExecution'
14
+ Rake::TestTask.new(:foreman_remote_execution) do |t|
15
+ test_dir = File.join(File.dirname(__FILE__), '../..', 'test')
16
+ t.libs << ['test', test_dir]
17
+ t.pattern = "#{test_dir}/**/*_test.rb"
18
+ t.verbose = true
19
+ end
20
+ end
21
+
22
+ namespace :foreman_remote_execution do
23
+ task :rubocop do
24
+ begin
25
+ require 'rubocop/rake_task'
26
+ RuboCop::RakeTask.new(:rubocop_foreman_remote_execution) do |task|
27
+ task.patterns = ["#{ForemanRemoteExecution::Engine.root}/app/**/*.rb",
28
+ "#{ForemanRemoteExecution::Engine.root}/lib/**/*.rb",
29
+ "#{ForemanRemoteExecution::Engine.root}/test/**/*.rb"]
30
+ end
31
+ rescue
32
+ puts 'Rubocop not loaded.'
33
+ end
34
+
35
+ Rake::Task['rubocop_foreman_remote_execution'].invoke
36
+ end
37
+ end
38
+
39
+ Rake::Task[:test].enhance do
40
+ Rake::Task['test:foreman_remote_execution'].invoke
41
+ end
42
+
43
+ load 'tasks/jenkins.rake'
44
+ if Rake::Task.task_defined?(:'jenkins:unit')
45
+ Rake::Task['jenkins:unit'].enhance do
46
+ Rake::Task['test:foreman_remote_execution'].invoke
47
+ Rake::Task['foreman_remote_execution:rubocop'].invoke
48
+ end
49
+ end
data/locale/Makefile ADDED
@@ -0,0 +1,62 @@
1
+ #
2
+ # Makefile for PO merging and MO generation. More info in the README.
3
+ #
4
+ # make all-mo (default) - generate MO files
5
+ # make check - check translations using translate-tool
6
+ # make tx-update - download and merge translations from Transifex
7
+ # make clean - clean everything
8
+ #
9
+ DOMAIN = foreman_remote_execution
10
+ VERSION = $(shell ruby -e 'require "rubygems";spec = Gem::Specification::load(Dir.glob("../*.gemspec")[0]);puts spec.version')
11
+ POTFILE = $(DOMAIN).pot
12
+ MOFILE = $(DOMAIN).mo
13
+ POFILES = $(shell find . -name '*.po')
14
+ MOFILES = $(patsubst %.po,%.mo,$(POFILES))
15
+ POXFILES = $(patsubst %.po,%.pox,$(POFILES))
16
+
17
+ %.mo: %.po
18
+ mkdir -p $(shell dirname $@)/LC_MESSAGES
19
+ msgfmt -o $(shell dirname $@)/LC_MESSAGES/$(MOFILE) $<
20
+
21
+ # Generate MO files from PO files
22
+ all-mo: $(MOFILES)
23
+
24
+ # Check for malformed strings
25
+ %.pox: %.po
26
+ msgfmt -c $<
27
+ pofilter --nofuzzy -t variables -t blank -t urls -t emails -t long -t newlines \
28
+ -t endwhitespace -t endpunc -t puncspacing -t options -t printf -t validchars --gnome $< > $@
29
+ cat $@
30
+ ! grep -q msgid $@
31
+
32
+ check: $(POXFILES)
33
+ msgfmt -c ${POTFILE}
34
+
35
+ # Merge PO files
36
+ update-po:
37
+ for f in $(shell find ./ -name "*.po") ; do \
38
+ msgmerge -N --backup=none -U $$f ${POTFILE} ; \
39
+ done
40
+
41
+ # Unify duplicate translations
42
+ uniq-po:
43
+ for f in $(shell find ./ -name "*.po") ; do \
44
+ msguniq $$f -o $$f ; \
45
+ done
46
+
47
+ tx-pull:
48
+ tx pull -f
49
+ for f in $(POFILES) ; do \
50
+ sed -i 's/^\("Project-Id-Version: \).*$$/\1$(DOMAIN) $(VERSION)\\n"/' $$f; \
51
+ done
52
+ -git commit -a -m "i18n - pulling from tx"
53
+
54
+ reset-po:
55
+ # merging po files is unnecessary when using transifex.com
56
+ git checkout -- ../locale/*/*po
57
+
58
+ tx-update: tx-pull reset-po $(MOFILES)
59
+ # amend mo files
60
+ git add ../locale/*/LC_MESSAGES
61
+ git commit -a --amend -m "i18n - pulling from tx"
62
+ -echo Changes commited!
@@ -0,0 +1,19 @@
1
+ # foreman_remote_execution
2
+ #
3
+ # This file is distributed under the same license as foreman_remote_execution.
4
+ #
5
+ #, fuzzy
6
+ msgid ""
7
+ msgstr ""
8
+ "Project-Id-Version: version 0.0.1\n"
9
+ "Report-Msgid-Bugs-To: \n"
10
+ "POT-Creation-Date: 2014-08-20 08:46+0100\n"
11
+ "PO-Revision-Date: 2014-08-20 08:54+0100\n"
12
+ "Last-Translator: Foreman Team <foreman-dev@googlegroups.com>\n"
13
+ "Language-Team: Foreman Team <foreman-dev@googlegroups.com>\n"
14
+ "Language: \n"
15
+ "MIME-Version: 1.0\n"
16
+ "Content-Type: text/plain; charset=UTF-8\n"
17
+ "Content-Transfer-Encoding: 8bit\n"
18
+ "Plural-Forms: nplurals=2; plural=(n != 1);\n"
19
+
@@ -0,0 +1,19 @@
1
+ # foreman_remote_execution
2
+ #
3
+ # This file is distributed under the same license as foreman_remote_execution.
4
+ #
5
+ #, fuzzy
6
+ msgid ""
7
+ msgstr ""
8
+ "Project-Id-Version: version 0.0.1\n"
9
+ "Report-Msgid-Bugs-To: \n"
10
+ "POT-Creation-Date: 2014-08-20 08:46+0100\n"
11
+ "PO-Revision-Date: 2014-08-20 08:46+0100\n"
12
+ "Last-Translator: Foreman Team <foreman-dev@googlegroups.com>\n"
13
+ "Language-Team: Foreman Team <foreman-dev@googlegroups.com>\n"
14
+ "Language: \n"
15
+ "MIME-Version: 1.0\n"
16
+ "Content-Type: text/plain; charset=UTF-8\n"
17
+ "Content-Transfer-Encoding: 8bit\n"
18
+ "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
19
+
data/locale/gemspec.rb ADDED
@@ -0,0 +1,2 @@
1
+ # Matches foreman_remote_execution.gemspec
2
+ _('A plugin bringing remote execution to the Foreman, completing the config management functionality with remote management functionality.')
@@ -0,0 +1,48 @@
1
+ FactoryGirl.define do
2
+ factory :job_template do |f|
3
+ f.sequence(:name) { |n| "Job template #{n}" }
4
+ f.template 'id'
5
+ f.provider_type 'Ssh'
6
+
7
+ trait :with_input do
8
+ after(:build) do |template, evaluator|
9
+ template.template_inputs << FactoryGirl.build(:template_input)
10
+ end
11
+ end
12
+ end
13
+
14
+ factory :template_input do |f|
15
+ f.sequence(:name) { |n| "Template input #{n}" }
16
+ f.input_type TemplateInput::TYPES.keys.first
17
+ end
18
+
19
+ factory :targeting do |f|
20
+ search_query "name = foo"
21
+ targeting_type "static_query"
22
+ user
23
+ end
24
+
25
+ factory :job_invocation do |f|
26
+ targeting
27
+ f.sequence(:job_name) { |n| "Job name #{n}" }
28
+ trait :with_template do
29
+ after(:build) do |invocation, evaluator|
30
+ invocation.template_invocations << FactoryGirl.build(:template_invocation)
31
+ end
32
+
33
+ end
34
+ end
35
+
36
+ factory :remote_execution_provider do |f|
37
+ f.sequence(:name) { |n| "Provider #{n}" }
38
+ end
39
+
40
+ factory :template_invocation do |f|
41
+ job_invocation
42
+ association :template, :factory => :job_template
43
+ end
44
+
45
+ factory :template_invocation_input_value do |f|
46
+ f.sequence(:value) { |n| "Input Value #{n}" }
47
+ end
48
+ end
@@ -0,0 +1,74 @@
1
+ require 'test_plugin_helper'
2
+
3
+ module Api
4
+ module V2
5
+ class JobTemplatesControllerTest < ActionController::TestCase
6
+ setup do
7
+ @template = FactoryGirl.create :job_template
8
+ end
9
+
10
+ test 'should get index' do
11
+ get :index
12
+ templates = ActiveSupport::JSON.decode(@response.body)
13
+ assert !templates.empty?, "Should response with template"
14
+ assert_response :success
15
+ end
16
+
17
+ test 'should get template detail' do
18
+ get :show, :id => @template.to_param
19
+ assert_response :success
20
+ template = ActiveSupport::JSON.decode(@response.body)
21
+ assert !template.empty?
22
+ assert_equal template["name"], @template.name
23
+ end
24
+
25
+ test 'should create valid' do
26
+ JobTemplate.any_instance.stubs(:valid?).returns(true)
27
+ valid_attrs = { :template => "This is a test template", :name => "RandomName", :provider_type => 'ssh' }
28
+ post :create, :job_template => valid_attrs
29
+ template = ActiveSupport::JSON.decode(@response.body)
30
+ assert template["name"] == "RandomName"
31
+ assert_response :success
32
+ end
33
+
34
+ test 'should not create invalid' do
35
+ post :create
36
+ assert_response :unprocessable_entity
37
+ end
38
+
39
+ test 'should update valid' do
40
+ JobTemplate.any_instance.stubs(:valid?).returns(true)
41
+ put :update, :id => @template.to_param,
42
+ :job_template => { :template => "blah" }
43
+ assert_response :ok
44
+ end
45
+
46
+ test 'should not update invalid' do
47
+ put :update, :id => @template.to_param,
48
+ :job_template => { :name => "" }
49
+ assert_response :unprocessable_entity
50
+ end
51
+
52
+ test 'should destroy' do
53
+ delete :destroy, :id => @template.to_param
54
+ assert_response :ok
55
+ refute JobTemplate.exists?(@template.id)
56
+ end
57
+
58
+ test 'should clone template' do
59
+ post :clone, :id => @template.to_param,
60
+ :job_template => {:name => 'MyClone'}
61
+ assert_response :success
62
+ template = ActiveSupport::JSON.decode(@response.body)
63
+ assert_equal(template['name'], 'MyClone')
64
+ assert_equal(template['template'], @template.template)
65
+ end
66
+
67
+ test 'clone name should not be blank' do
68
+ post :clone, :id => @template.to_param,
69
+ :job_template => {:name => ''}
70
+ assert_response :unprocessable_entity
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,8 @@
1
+ # This calls the main test_helper in Foreman-core
2
+ require 'test_helper'
3
+
4
+ require 'dynflow/testing'
5
+
6
+ # Add plugin to FactoryGirl's paths
7
+ FactoryGirl.definition_file_paths << File.join(File.dirname(__FILE__), 'factories')
8
+ FactoryGirl.reload
@@ -0,0 +1,40 @@
1
+
2
+ require "test_plugin_helper"
3
+
4
+ module ForemanRemoteExecution
5
+ class RunHostsJobTest < ActiveSupport::TestCase
6
+ include Dynflow::Testing
7
+
8
+ let(:proxy) { FactoryGirl.build(:smart_proxy) }
9
+ let(:hostname) { 'myhost.example.com' }
10
+ let(:script) { 'ping -c 5 redhat.com' }
11
+ let(:targeting) { FactoryGirl.create(:targeting, :search_query => "name = #{host.name}", :user => User.current) }
12
+ let(:job_invocation) do
13
+ FactoryGirl.build(:job_invocation).tap do |invocation|
14
+ invocation.targeting = targeting
15
+ invocation.save
16
+ end
17
+ end
18
+ let(:host) { FactoryGirl.create(:host) }
19
+ let(:action) do
20
+ action = create_action(Actions::RemoteExecution::RunHostsJob)
21
+ action.expects(:action_subject).with(job_invocation)
22
+ action.expects(:task).returns(OpenStruct.new(:id => '123'))
23
+ plan_action(action, job_invocation)
24
+ end
25
+
26
+ before do
27
+ User.current = users :admin
28
+ action
29
+ end
30
+
31
+ it 'resolves the hosts on targeting in plan phase' do
32
+ targeting.hosts.must_include(host)
33
+ end
34
+
35
+ it 'triggers the RunHostJob actions on the resolved hosts in run phase' do
36
+ action.expects(:trigger).with(Actions::RemoteExecution::RunHostJob, job_invocation, host)
37
+ action.create_sub_plans
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,30 @@
1
+ require "test_plugin_helper"
2
+
3
+ module ForemanRemoteExecution
4
+ class RunProxyCommandTest < ActiveSupport::TestCase
5
+ include Dynflow::Testing
6
+
7
+ let(:proxy) { FactoryGirl.build(:smart_proxy) }
8
+ let(:hostname) { 'myhost.example.com' }
9
+ let(:script) { 'ping -c 5 redhat.com' }
10
+ let(:action) do
11
+ create_and_plan_action(Actions::RemoteExecution::RunProxyCommand, proxy, hostname, script)
12
+ end
13
+
14
+ it 'plans for running the command action on server' do
15
+ assert_run_phase action, { :hostname => hostname,
16
+ :script => script,
17
+ :proxy_url => proxy.url,
18
+ :effective_user => nil }
19
+ end
20
+
21
+ it 'sends to command to ssh provider' do
22
+ action.proxy_action_name.must_equal 'Proxy::RemoteExecution::Ssh::CommandAction'
23
+ end
24
+
25
+ it "doesn't block on failure" do
26
+ action.rescue_strategy.must_equal ::Dynflow::Action::Rescue::Skip
27
+ end
28
+
29
+ end
30
+ end