foreman_git_templates 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +619 -0
  3. data/README.md +66 -0
  4. data/Rakefile +47 -0
  5. data/app/lib/foreman_git_templates/renderer.rb +20 -0
  6. data/app/lib/foreman_git_templates/renderer/source/repository.rb +27 -0
  7. data/app/lib/foreman_git_templates/tar.rb +26 -0
  8. data/app/models/concerns/foreman_git_templates/host_extensions.rb +11 -0
  9. data/app/models/concerns/foreman_git_templates/hostext/operating_system.rb +34 -0
  10. data/app/models/concerns/foreman_git_templates/orchestration/tftp.rb +29 -0
  11. data/app/models/foreman_git_templates/base_repository_template.rb +15 -0
  12. data/app/models/foreman_git_templates/default_local_boot_repository_template.rb +11 -0
  13. data/app/models/foreman_git_templates/main_repository_template.rb +11 -0
  14. data/app/models/foreman_git_templates/snippet_repository_template.rb +11 -0
  15. data/app/services/foreman_git_templates/repository_fetcher.rb +29 -0
  16. data/app/services/foreman_git_templates/repository_reader.rb +42 -0
  17. data/config/routes.rb +4 -0
  18. data/lib/foreman_git_templates.rb +6 -0
  19. data/lib/foreman_git_templates/engine.rb +26 -0
  20. data/lib/foreman_git_templates/version.rb +5 -0
  21. data/lib/tasks/foreman_git_templates_tasks.rake +47 -0
  22. data/test/controllers/hosts_controller_test.rb +26 -0
  23. data/test/controllers/unattended_controller_test.rb +71 -0
  24. data/test/factories/foreman_git_templates_factories.rb +11 -0
  25. data/test/models/concerns/foreman_git_templates/hostext/operating_system_test.rb +24 -0
  26. data/test/models/concerns/foreman_git_templates/orchestration/tftp_test.rb +114 -0
  27. data/test/models/foreman_git_templates/host_test.rb +28 -0
  28. data/test/models/foreman_git_templates/snippet_repository_template_test.rb +13 -0
  29. data/test/test_plugin_helper.rb +21 -0
  30. data/test/unit/foreman/renderer/source/repository_test.rb +29 -0
  31. data/test/unit/repository_fetcher_test.rb +37 -0
  32. data/test/unit/repository_reader_test.rb +76 -0
  33. metadata +128 -0
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rake/testtask'
4
+
5
+ # Tasks
6
+ namespace :foreman_git_templates do
7
+ namespace :example do
8
+ desc 'Example Task'
9
+ task task: :environment do
10
+ # Task goes here
11
+ end
12
+ end
13
+ end
14
+
15
+ # Tests
16
+ namespace :test do
17
+ desc 'Test ForemanGitTemplates'
18
+ Rake::TestTask.new(:foreman_git_templates) do |t|
19
+ test_dir = File.join(File.dirname(__FILE__), '../..', 'test')
20
+ t.libs << ['test', test_dir]
21
+ t.pattern = "#{test_dir}/**/*_test.rb"
22
+ t.verbose = true
23
+ t.warning = false
24
+ end
25
+ end
26
+
27
+ namespace :foreman_git_templates do
28
+ task :rubocop do
29
+ begin
30
+ require 'rubocop/rake_task'
31
+ RuboCop::RakeTask.new(:rubocop_foreman_git_templates) do |task|
32
+ task.patterns = ["#{ForemanGitTemplates::Engine.root}/app/**/*.rb",
33
+ "#{ForemanGitTemplates::Engine.root}/lib/**/*.rb",
34
+ "#{ForemanGitTemplates::Engine.root}/test/**/*.rb"]
35
+ end
36
+ rescue StandardError
37
+ puts 'Rubocop not loaded.'
38
+ end
39
+
40
+ Rake::Task['rubocop_foreman_git_templates'].invoke
41
+ end
42
+ end
43
+
44
+ Rake::Task[:test].enhance ['test:foreman_git_templates']
45
+
46
+ load 'tasks/jenkins.rake'
47
+ Rake::Task['jenkins:unit'].enhance ['test:foreman_git_templates', 'foreman_git_templates:rubocop'] if Rake::Task.task_defined?(:'jenkins:unit')
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_plugin_helper'
4
+
5
+ class HostsControllerTest < ActionController::TestCase
6
+ let(:host) { FactoryBot.create(:host, :managed, :with_template_url) }
7
+
8
+ describe '#templates' do
9
+ it 'returns only templates that are defined in the archive' do
10
+ Dir.mktmpdir do |dir|
11
+ expected_kinds = ['PXEGrub', 'PXELinux', 'iPXE', 'PXEGrub2', 'provision']
12
+
13
+ stub_repository host.params['template_url'], "#{dir}/repo.tar.gz" do |tar|
14
+ expected_kinds.each do |kind|
15
+ tar.add_file_simple("templates/#{kind}/template.erb", 644, host.name.length) { |io| io.write(host.name) }
16
+ end
17
+ end
18
+
19
+ get :templates, params: { id: host.name }, session: set_session_user
20
+
21
+ actual_kinds = @controller.instance_variable_get('@templates').map(&:name)
22
+ assert_same_elements expected_kinds, actual_kinds
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_plugin_helper'
4
+
5
+ class UnattendedControllerTest < ActionController::TestCase
6
+ let(:os) { FactoryBot.create(:operatingsystem, :with_archs, :with_ptables, type: 'Redhat') }
7
+ let(:host) do
8
+ FactoryBot.create(:host, :managed, :with_template_url, operatingsystem: os, ptable: os.ptables.first)
9
+ end
10
+
11
+ test 'should render template from repository' do
12
+ assert_not_nil host.params['template_url']
13
+
14
+ Dir.mktmpdir do |dir|
15
+ kind = 'provision'
16
+
17
+ stub_repository host.params['template_url'], "#{dir}/repo.tar.gz" do |tar|
18
+ tar.add_file_simple("templates/#{kind}/template.erb", 644, host.name.length) { |io| io.write(host.name) }
19
+ end
20
+
21
+ get :host_template, params: { kind: kind, hostname: host.name }, session: set_session_user
22
+ assert_response :success
23
+ assert_equal host.name, response.body.strip
24
+ end
25
+ end
26
+
27
+ test 'should render snippet from repository' do
28
+ assert_not_nil host.params['template_url']
29
+
30
+ Dir.mktmpdir do |dir|
31
+ kind = 'PXELinux'
32
+ snippet_name = 'MySnippet'
33
+ snippet_content = 'foo: <%= @foo %>'
34
+ template_content = "<%= snippet('#{snippet_name}', variables: { foo: 'bar' }) %>"
35
+
36
+ stub_repository host.params['template_url'], "#{dir}/repo.tar.gz" do |tar|
37
+ tar.add_file_simple("templates/#{kind}/template.erb", 644, template_content.length) { |io| io.write(template_content) }
38
+ tar.add_file_simple("templates/snippets/#{snippet_name.downcase}.erb", 644, snippet_content.length) { |io| io.write(snippet_content) }
39
+ end
40
+
41
+ get :host_template, params: { kind: kind, hostname: host.name }, session: set_session_user
42
+ assert_response :success
43
+ assert_equal 'foo: bar', response.body.strip
44
+ end
45
+ end
46
+
47
+ test 'snippet should render nested snippet' do
48
+ assert_not_nil host.params['template_url']
49
+
50
+ Dir.mktmpdir do |dir|
51
+ nested_snippet_name = 'MyNestedSnippet'
52
+ nested_snippet_content = '<%= @foo %><%= @bar %>'
53
+
54
+ snippet_name = 'MySnippet'
55
+ snippet_content = "<%= @foo %> <%= snippet('#{nested_snippet_name}', variables: { bar: 'bar' }) %>"
56
+
57
+ kind = 'PXELinux'
58
+ template_content = "<%= snippet('#{snippet_name}', variables: { foo: 'foo' }) %>"
59
+
60
+ stub_repository host.params['template_url'], "#{dir}/repo.tar.gz" do |tar|
61
+ tar.add_file_simple("templates/#{kind}/template.erb", 644, template_content.length) { |io| io.write(template_content) }
62
+ tar.add_file_simple("templates/snippets/#{snippet_name.downcase}.erb", 644, snippet_content.length) { |io| io.write(snippet_content) }
63
+ tar.add_file_simple("templates/snippets/#{nested_snippet_name.downcase}.erb", 644, nested_snippet_content.length) { |io| io.write(nested_snippet_content) }
64
+ end
65
+
66
+ get :host_template, params: { kind: kind, hostname: host.name }, session: set_session_user
67
+ assert_response :success
68
+ assert_equal 'foo bar', response.body.strip
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.modify do
4
+ factory :host do
5
+ trait :with_template_url do
6
+ after(:create) do |host, _evaluator|
7
+ FactoryBot.create(:host_parameter, host: host, name: 'template_url', value: 'http://www.api.com/repository.tar.gz')
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_plugin_helper'
4
+
5
+ module Hostext
6
+ class OperatingSystemTest < ActiveSupport::TestCase
7
+ let(:host) { FactoryBot.create(:host, :managed, :with_template_url) }
8
+
9
+ test 'available_template_kinds finds only templates that are defined in the repository' do
10
+ Dir.mktmpdir do |dir|
11
+ expected_kinds = ['PXEGrub', 'PXELinux', 'iPXE', 'PXEGrub2', 'provision']
12
+
13
+ stub_repository host.params['template_url'], "#{dir}/repo.tar.gz" do |tar|
14
+ expected_kinds.each do |kind|
15
+ tar.add_file_simple("templates/#{kind}/template.erb", 644, host.name.length) { |io| io.write(host.name) }
16
+ end
17
+ end
18
+
19
+ actual = host.available_template_kinds('build').map(&:name)
20
+ assert_same_elements expected_kinds, actual
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_plugin_helper'
4
+
5
+ class TFTPOrchestrationTest < ActiveSupport::TestCase
6
+ describe '#generate_pxe_template' do
7
+ let(:kind) { 'PXELinux' }
8
+ let(:template_content) { 'main template content' }
9
+ let(:default_local_boot_template_content) { 'default local boot template content' }
10
+
11
+ context 'host is in build mode' do
12
+ let(:host) { FactoryBot.create(:host, :managed, :with_template_url, build: true) }
13
+
14
+ it 'renders main template' do
15
+ Dir.mktmpdir do |dir|
16
+ stub_repository host.params['template_url'], "#{dir}/repo.tar.gz" do |tar|
17
+ tar.add_file_simple("templates/#{kind}/template.erb", 644, template_content.length) { |io| io.write(template_content) }
18
+ tar.add_file_simple("templates/#{kind}/default_local_boot.erb", 644, default_local_boot_template_content.length) { |io| io.write(default_local_boot_template_content) }
19
+ end
20
+
21
+ assert_equal template_content, host.generate_pxe_template('PXELinux')
22
+ end
23
+ end
24
+ end
25
+
26
+ context 'host is not in build mode' do
27
+ let(:host) { FactoryBot.create(:host, :managed, :with_template_url, build: false) }
28
+
29
+ it 'renders default local boot template' do
30
+ Dir.mktmpdir do |dir|
31
+ stub_repository host.params['template_url'], "#{dir}/repo.tar.gz" do |tar|
32
+ tar.add_file_simple("templates/#{kind}/template.erb", 644, template_content.length) { |io| io.write(template_content) }
33
+ tar.add_file_simple("templates/#{kind}/default_local_boot.erb", 644, default_local_boot_template_content.length) { |io| io.write(default_local_boot_template_content) }
34
+ end
35
+
36
+ assert_equal default_local_boot_template_content, host.generate_pxe_template('PXELinux')
37
+ end
38
+ end
39
+
40
+ it 'does not generate a pxe template if the corresponding template file is missing in the repo' do
41
+ Dir.mktmpdir do |dir|
42
+ stub_repository host.params['template_url'], "#{dir}/repo.tar.gz" do |tar|
43
+ tar.add_file_simple("templates/#{kind}/template.erb", 644, template_content.length) { |io| io.write(template_content) }
44
+ tar.add_file_simple("templates/#{kind}/default_local_boot.erb", 644, default_local_boot_template_content.length) { |io| io.write(default_local_boot_template_content) }
45
+ end
46
+
47
+ assert_nil host.generate_pxe_template('PXEGrub2')
48
+ end
49
+ end
50
+
51
+ it 'raises an exception when generating a pxe template if the corresponding default local boot template file is missing in the repo' do
52
+ Dir.mktmpdir do |dir|
53
+ stub_repository host.params['template_url'], "#{dir}/repo.tar.gz" do |tar|
54
+ tar.add_file_simple("templates/#{kind}/template.erb", 644, template_content.length) { |io| io.write(template_content) }
55
+ end
56
+
57
+ assert_raises ForemanGitTemplates::RepositoryReader::MissingFileError do
58
+ host.generate_pxe_template('PXELinux')
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ describe '#setTFTP' do
66
+ let(:host) { FactoryBot.create(:host, :with_tftp_orchestration, :with_template_url) }
67
+
68
+ let(:kind) { 'PXELinux' }
69
+ let(:template_content) { 'main template content' }
70
+ let(:default_local_boot_template_content) { 'default local boot template content' }
71
+
72
+ context 'host is in build mode' do
73
+ setup do
74
+ host.update(build: true)
75
+ end
76
+
77
+ it 'sends the main template from the tar archive to the smart proxy' do
78
+ assert_not_nil host.params['template_url']
79
+
80
+ Dir.mktmpdir do |dir|
81
+ stub_repository host.params['template_url'], "#{dir}/repo.tar.gz" do |tar|
82
+ tar.add_file_simple("templates/#{kind}/template.erb", 644, template_content.length) { |io| io.write(template_content) }
83
+ tar.add_file_simple("templates/#{kind}/default_local_boot.erb", 644, default_local_boot_template_content.length) { |io| io.write(default_local_boot_template_content) }
84
+ end
85
+
86
+ ProxyAPI::TFTP.any_instance.expects(:set).with(kind, host.interfaces.first.mac, pxeconfig: template_content).once
87
+
88
+ host.provision_interface.send(:setTFTP, kind)
89
+ end
90
+ end
91
+ end
92
+
93
+ context 'host is not in build mode' do
94
+ setup do
95
+ host.update(build: false)
96
+ end
97
+
98
+ it 'sends the local boot template from the tar archive to the smart proxy' do
99
+ assert_not_nil host.params['template_url']
100
+
101
+ Dir.mktmpdir do |dir|
102
+ stub_repository host.params['template_url'], "#{dir}/repo.tar.gz" do |tar|
103
+ tar.add_file_simple("templates/#{kind}/template.erb", 644, template_content.length) { |io| io.write(template_content) }
104
+ tar.add_file_simple("templates/#{kind}/default_local_boot.erb", 644, default_local_boot_template_content.length) { |io| io.write(default_local_boot_template_content) }
105
+ end
106
+
107
+ ProxyAPI::TFTP.any_instance.expects(:set).with(kind, host.interfaces.first.mac, pxeconfig: default_local_boot_template_content).once
108
+
109
+ host.provision_interface.send(:setTFTP, kind)
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_plugin_helper'
4
+
5
+ module ForemanGitTemplates
6
+ class HostTest < ActiveSupport::TestCase
7
+ describe '#repository_path' do
8
+ context 'with template_url parameter' do
9
+ let(:repo_path) { '/tmp/repo.tar.gz' }
10
+ let(:host) { FactoryBot.create(:host, :managed, :with_template_url) }
11
+
12
+ it 'returns path to the repository' do
13
+ ForemanGitTemplates::RepositoryFetcher.expects(:call).once.with(host.params['template_url']).returns(repo_path)
14
+ assert_equal repo_path, host.repository_path
15
+ end
16
+ end
17
+
18
+ context 'without template_url parameter' do
19
+ let(:host) { FactoryBot.create(:host, :managed) }
20
+
21
+ it 'returns nil' do
22
+ ForemanGitTemplates::RepositoryFetcher.expects(:call).never
23
+ assert_nil host.repository_path
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_plugin_helper'
4
+
5
+ module ForemanGitTemplates
6
+ class SnippetRepositoryTest < ActiveSupport::TestCase
7
+ let(:snippet_repository_template) { ForemanGitTemplates::SnippetRepositoryTemplate.new(name: 'CoreOS provision Ignition') }
8
+
9
+ test '#path downcases the filename and replaces spaces with underscores' do
10
+ assert_equal 'templates/snippets/coreos_provision_ignition.erb', snippet_repository_template.path
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This calls the main test_helper in Foreman-core
4
+ require 'test_helper'
5
+
6
+ # Add plugin to FactoryBot's paths
7
+ FactoryBot.definition_file_paths << File.join(File.dirname(__FILE__), 'factories')
8
+ FactoryBot.reload
9
+
10
+ def stub_repository(template_url, repository_path)
11
+ build_repository(repository_path) do |tar|
12
+ yield tar if block_given?
13
+ end
14
+ stub_request(:get, template_url).to_return(status: 200, body: File.new(repository_path))
15
+ end
16
+
17
+ def build_repository(repository_path)
18
+ ForemanGitTemplates::Tar.tar(repository_path) do |tar|
19
+ yield tar if block_given?
20
+ end
21
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_plugin_helper'
4
+
5
+ class RepositorySourceTest < ActiveSupport::TestCase
6
+ let(:repo_path) { '/tmp/repository.tar.gz' }
7
+ let(:template) { ForemanGitTemplates::MainRepositoryTemplate.new(name: 'MyTemplate') }
8
+ let(:subject) { ForemanGitTemplates::Renderer::Source::Repository.new(template, repo_path) }
9
+
10
+ describe '#content' do
11
+ let(:content) { 'content' }
12
+
13
+ test 'should return content' do
14
+ ForemanGitTemplates::RepositoryReader.expects(:call).once.with(repo_path, template.path).returns(content)
15
+ assert_equal content, subject.content
16
+ end
17
+ end
18
+
19
+ describe '#find_snippet' do
20
+ let(:snippet_name) { 'MySnippet' }
21
+ let(:snippet) { subject.find_snippet(snippet_name) }
22
+
23
+ test 'should return snippet template' do
24
+ assert snippet.is_a?(ForemanGitTemplates::SnippetRepositoryTemplate)
25
+ assert snippet.respond_to?(:render)
26
+ assert_equal snippet_name, snippet.name
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_plugin_helper'
4
+
5
+ class RepositoryFetcherTest < ActiveSupport::TestCase
6
+ test 'should save a file' do
7
+ Dir.mktmpdir do |dir|
8
+ url = 'http://api.com/repository'
9
+ repository_path = "#{dir}/repo.tar.gz"
10
+
11
+ build_repository repository_path
12
+ stub_request(:get, url).to_return(status: 200, body: repository_path)
13
+ file_path = ForemanGitTemplates::RepositoryFetcher.call(url)
14
+
15
+ assert File.exist?(file_path)
16
+ end
17
+ end
18
+
19
+ test 'should raise RepositoryFetcherError when file does not exist' do
20
+ url = 'http://api.com/repository'
21
+ stub_request(:get, url).to_return(status: 404)
22
+
23
+ msg = 'Response code: 404'
24
+ assert_raises_with_message(ForemanGitTemplates::RepositoryFetcher::RepositoryFetcherError, msg) do
25
+ ForemanGitTemplates::RepositoryFetcher.call(url)
26
+ end
27
+ end
28
+
29
+ test 'should raise RepositoryFetcherError when url is incorrect' do
30
+ url = 'incorrect_url'
31
+
32
+ msg = 'URL scheme needs to be http or https'
33
+ assert_raises_with_message(ForemanGitTemplates::RepositoryFetcher::RepositoryFetcherError, msg) do
34
+ ForemanGitTemplates::RepositoryFetcher.call(url)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_plugin_helper'
4
+
5
+ class RepositoryReaderTest < ActiveSupport::TestCase
6
+ test 'should return file content' do
7
+ Dir.mktmpdir do |dir|
8
+ repository_path = "#{dir}/repo.tar.gz"
9
+ file_name = 'files/README.md'
10
+ file_content = 'Hello'
11
+
12
+ build_repository repository_path do |tar|
13
+ tar.add_file_simple(file_name, 644, file_content.length) { |io| io.write(file_content) }
14
+ end
15
+
16
+ result = ForemanGitTemplates::RepositoryReader.call(repository_path, file_name)
17
+ assert_equal file_content, result
18
+ end
19
+ end
20
+
21
+ context 'with custom root directory' do
22
+ test 'should return file content' do
23
+ Dir.mktmpdir do |dir|
24
+ repository_path = "#{dir}/repo.tar.gz"
25
+ root_dir = 'root'
26
+ file_name = 'files/README.md'
27
+ file_content = 'Hello'
28
+
29
+ build_repository repository_path do |tar|
30
+ tar.add_file_simple("#{root_dir}/#{file_name}", 644, file_content.length) { |io| io.write(file_content) }
31
+ end
32
+
33
+ result = ForemanGitTemplates::RepositoryReader.call(repository_path, file_name)
34
+ assert_equal file_content, result
35
+ end
36
+ end
37
+ end
38
+
39
+ test 'should raise RepositoryUnreadableError when repository does not exist' do
40
+ Dir.mktmpdir do |dir|
41
+ repository_path = "#{dir}/repo.tar.gz"
42
+
43
+ assert_raises(ForemanGitTemplates::RepositoryReader::RepositoryUnreadableError) do
44
+ ForemanGitTemplates::RepositoryReader.call(repository_path, 'file')
45
+ end
46
+ end
47
+ end
48
+
49
+ test 'should raise MissingFileError when file does not exist' do
50
+ Dir.mktmpdir do |dir|
51
+ repository_path = "#{dir}/repo.tar.gz"
52
+ filename = 'file.erb'
53
+ build_repository repository_path
54
+
55
+ assert_raises(ForemanGitTemplates::RepositoryReader::MissingFileError) do
56
+ ForemanGitTemplates::RepositoryReader.call(repository_path, filename)
57
+ end
58
+ end
59
+ end
60
+
61
+ test 'should raise EmptyFileError when file is empty' do
62
+ Dir.mktmpdir do |dir|
63
+ repository_path = "#{dir}/repo.tar.gz"
64
+ file_name = 'files/README.md'
65
+ file_content = ''
66
+
67
+ build_repository repository_path do |tar|
68
+ tar.add_file_simple(file_name, 644, file_content.length) { |io| io.write(file_content) }
69
+ end
70
+
71
+ assert_raises(ForemanGitTemplates::RepositoryReader::EmptyFileError) do
72
+ ForemanGitTemplates::RepositoryReader.call(repository_path, file_name)
73
+ end
74
+ end
75
+ end
76
+ end