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.
- checksums.yaml +15 -0
- data/LICENSE +619 -0
- data/README.md +54 -0
- data/Rakefile +47 -0
- data/app/assets/javascripts/template_input.js +9 -0
- data/app/assets/javascripts/template_invocation.js +32 -0
- data/app/assets/stylesheets/job_invocations.css.scss +35 -0
- data/app/assets/stylesheets/template_invocation.css.scss +16 -0
- data/app/controllers/api/v2/job_templates_controller.rb +108 -0
- data/app/controllers/job_invocations_controller.rb +35 -0
- data/app/controllers/job_templates_controller.rb +35 -0
- data/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb +40 -0
- data/app/helpers/remote_execution_helper.rb +88 -0
- data/app/lib/actions/remote_execution/run_host_job.rb +93 -0
- data/app/lib/actions/remote_execution/run_hosts_job.rb +35 -0
- data/app/lib/actions/remote_execution/run_proxy_command.rb +34 -0
- data/app/models/concerns/foreman_remote_execution/bookmark_extensions.rb +9 -0
- data/app/models/concerns/foreman_remote_execution/foreman_tasks_task_extensions.rb +9 -0
- data/app/models/concerns/foreman_remote_execution/host_extensions.rb +19 -0
- data/app/models/concerns/foreman_remote_execution/template_extensions.rb +20 -0
- data/app/models/concerns/foreman_remote_execution/template_relations.rb +10 -0
- data/app/models/concerns/foreman_remote_execution/user_extensions.rb +9 -0
- data/app/models/input_template_renderer.rb +42 -0
- data/app/models/job_invocation.rb +21 -0
- data/app/models/job_invocation_composer.rb +210 -0
- data/app/models/job_template.rb +52 -0
- data/app/models/remote_execution_provider.rb +17 -0
- data/app/models/setting/remote_execution.rb +19 -0
- data/app/models/ssh_execution_provider.rb +2 -0
- data/app/models/targeting.rb +56 -0
- data/app/models/targeting_host.rb +9 -0
- data/app/models/template_input.rb +154 -0
- data/app/models/template_invocation.rb +13 -0
- data/app/models/template_invocation_input_value.rb +8 -0
- data/app/views/api/v2/job_templates/base.json.rabl +3 -0
- data/app/views/api/v2/job_templates/create.json.rabl +3 -0
- data/app/views/api/v2/job_templates/index.json.rabl +3 -0
- data/app/views/api/v2/job_templates/main.json.rabl +5 -0
- data/app/views/api/v2/job_templates/show.json.rabl +9 -0
- data/app/views/job_invocations/_form.html.erb +67 -0
- data/app/views/job_invocations/_tab_hosts.html.erb +33 -0
- data/app/views/job_invocations/_tab_overview.html.erb +41 -0
- data/app/views/job_invocations/index.html.erb +30 -0
- data/app/views/job_invocations/new.html.erb +8 -0
- data/app/views/job_invocations/refresh.js.erb +1 -0
- data/app/views/job_invocations/show.html.erb +21 -0
- data/app/views/job_templates/_custom_tab_headers.html.erb +2 -0
- data/app/views/job_templates/_custom_tabs.html.erb +28 -0
- data/app/views/job_templates/auto_complete_job_name.json.erb +3 -0
- data/app/views/job_templates/edit.html.erb +6 -0
- data/app/views/job_templates/index.html.erb +33 -0
- data/app/views/job_templates/new.html.erb +6 -0
- data/app/views/template_inputs/_form.html.erb +22 -0
- data/config/routes.rb +35 -0
- data/db/migrate/20150612121541_add_job_template_to_template.rb +6 -0
- data/db/migrate/20150616080015_create_template_input.rb +19 -0
- data/db/migrate/20150708133241_add_targeting.rb +25 -0
- data/db/migrate/20150708133242_add_invocation.rb +11 -0
- data/db/migrate/20150708133305_add_template_invocation.rb +22 -0
- data/db/migrate/20150812110800_add_resolved_at_to_targeting.rb +5 -0
- data/db/migrate/20150812145900_add_last_task_id_to_job_invocation.rb +6 -0
- data/db/seeds.d/60-ssh_proxy_feature.rb +2 -0
- data/lib/foreman_remote_execution/engine.rb +119 -0
- data/lib/foreman_remote_execution/version.rb +3 -0
- data/lib/foreman_remote_execution.rb +6 -0
- data/lib/tasks/foreman_remote_execution_tasks.rake +49 -0
- data/locale/Makefile +62 -0
- data/locale/en/foreman_remote_execution.po +19 -0
- data/locale/foreman_remote_execution.pot +19 -0
- data/locale/gemspec.rb +2 -0
- data/test/factories/foreman_remote_execution_factories.rb +48 -0
- data/test/functional/api/v2/job_templates_controller_test.rb +74 -0
- data/test/test_plugin_helper.rb +8 -0
- data/test/unit/actions/run_hosts_job_test.rb +40 -0
- data/test/unit/actions/run_proxy_command_test.rb +30 -0
- data/test/unit/input_template_renderer_test.rb +366 -0
- data/test/unit/job_invocation_composer_test.rb +415 -0
- data/test/unit/job_invocation_test.rb +31 -0
- data/test/unit/job_template_test.rb +5 -0
- data/test/unit/remote_execution_provider_test.rb +51 -0
- data/test/unit/targeting_test.rb +107 -0
- data/test/unit/template_input_test.rb +25 -0
- 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,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,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
|