foreman_git_templates 1.0.0

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