foreman_opentofu 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 (54) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +674 -0
  3. data/README.md +134 -0
  4. data/Rakefile +30 -0
  5. data/app/controllers/api/v2/tf_states_controller.rb +52 -0
  6. data/app/controllers/concerns/foreman_opentofu/compute_resources_vms_controller.rb +17 -0
  7. data/app/controllers/concerns/foreman_opentofu/controller/parameters/compute_resource.rb +42 -0
  8. data/app/lib/foreman_opentofu/concerns/base_template_scope_extensions.rb +89 -0
  9. data/app/models/concerns/orchestration/tofu/compute.rb +24 -0
  10. data/app/models/foreman_opentofu/compute_vm.rb +99 -0
  11. data/app/models/foreman_opentofu/opentofu_vm_commands.rb +88 -0
  12. data/app/models/foreman_opentofu/tf_state.rb +8 -0
  13. data/app/models/foreman_opentofu/tofu.rb +77 -0
  14. data/app/overrides/compute_resources/remove_virtual_machines_tab.rb +8 -0
  15. data/app/services/foreman_opentofu/app_wrapper.rb +107 -0
  16. data/app/services/foreman_opentofu/compute_fetcher.rb +51 -0
  17. data/app/services/foreman_opentofu/opentofu_executer.rb +78 -0
  18. data/app/services/foreman_opentofu/provider_type.rb +30 -0
  19. data/app/services/foreman_opentofu/provider_type_manager.rb +36 -0
  20. data/app/views/compute_resources/form/_tofu.html.erb +9 -0
  21. data/app/views/compute_resources/show/_tofu.html.erb +8 -0
  22. data/app/views/compute_resources_vms/form/tofu/_base.html.erb +13 -0
  23. data/app/views/compute_resources_vms/form/tofu/_dynamic_attrs.html.erb +18 -0
  24. data/app/views/compute_resources_vms/form/tofu/_network.html.erb +5 -0
  25. data/app/views/compute_resources_vms/index/_tofu.html.erb +0 -0
  26. data/app/views/templates/provisioning/nutanix_provision_host.erb +51 -0
  27. data/app/views/templates/provisioning/ovirt_provision_host.erb +68 -0
  28. data/config/initializers/compute_attrs.rb +19 -0
  29. data/config/nutanix.json +27 -0
  30. data/config/ovirt.json +18 -0
  31. data/config/routes.rb +13 -0
  32. data/db/migrate/20250625192757_create_tf_state.foreman_opentofu.rb +10 -0
  33. data/db/seeds.d/71_provisioning_templates.rb +9 -0
  34. data/lib/foreman_opentofu/engine.rb +75 -0
  35. data/lib/foreman_opentofu/provider_types/nutanix.rb +6 -0
  36. data/lib/foreman_opentofu/provider_types/ovirt.rb +2 -0
  37. data/lib/foreman_opentofu/provider_types.rb +3 -0
  38. data/lib/foreman_opentofu/version.rb +3 -0
  39. data/lib/foreman_opentofu.rb +6 -0
  40. data/lib/tasks/foreman_opentofu_tasks.rake +30 -0
  41. data/locale/Makefile +73 -0
  42. data/locale/en/foreman_opentofu.po +19 -0
  43. data/locale/foreman_opentofu.pot +19 -0
  44. data/locale/gemspec.rb +2 -0
  45. data/test/controllers/api/v2/tf_states_controller_test.rb +67 -0
  46. data/test/factories/compute_resources.rb +26 -0
  47. data/test/factories/foreman_opentofu_factories.rb +7 -0
  48. data/test/factories/tf_state_factories.rb +7 -0
  49. data/test/models/foreman_opentofu/opentofu_vm_commands_test.rb +147 -0
  50. data/test/models/foreman_opentofu_test.rb +43 -0
  51. data/test/services/app_wrapper_test.rb +31 -0
  52. data/test/services/opentofu_executer_test.rb +62 -0
  53. data/test/test_plugin_helper.rb +6 -0
  54. metadata +119 -0
@@ -0,0 +1,6 @@
1
+ # FIXME: should not be necessary due to autoloading :-(
2
+ require "#{ForemanOpentofu::Engine.root}/app/services/foreman_opentofu/provider_type_manager"
3
+ require "#{ForemanOpentofu::Engine.root}/app/services/foreman_opentofu/provider_type"
4
+
5
+ ForemanOpentofu::ProviderTypeManager.register('nutanix') do
6
+ end
@@ -0,0 +1,2 @@
1
+ ForemanOpentofu::ProviderTypeManager.register('ovirt') do
2
+ end
@@ -0,0 +1,3 @@
1
+ Dir["#{File.expand_path('provider_types', __dir__)}/*.rb"].each do |file|
2
+ load file
3
+ end
@@ -0,0 +1,3 @@
1
+ module ForemanOpentofu
2
+ VERSION = '0.0.1'.freeze
3
+ end
@@ -0,0 +1,6 @@
1
+ require 'deface'
2
+
3
+ module ForemanOpentofu
4
+ require 'foreman_opentofu/version'
5
+ require 'foreman_opentofu/engine'
6
+ end
@@ -0,0 +1,30 @@
1
+ require 'rake/testtask'
2
+
3
+ # Tasks
4
+ namespace :foreman_opentofu do
5
+ require 'rubocop/rake_task'
6
+ RuboCop::RakeTask.new(:rubocop) do |task|
7
+ # details are handled in .rubocop.yml
8
+ task.patterns = [ForemanOpentofu::Engine.root.to_s]
9
+ end
10
+ rescue LoadError => e
11
+ raise e unless Rails.env.production?
12
+ end
13
+
14
+ # Tests
15
+ namespace :test do
16
+ desc 'Test ForemanOpentofu'
17
+ Rake::TestTask.new(:foreman_opentofu) do |t|
18
+ test_dir = File.expand_path('../../test', __dir__)
19
+ t.libs << 'test'
20
+ t.libs << test_dir
21
+ t.pattern = "#{test_dir}/**/*_test.rb"
22
+ t.verbose = true
23
+ t.warning = false
24
+ end
25
+ end
26
+
27
+ Rake::Task[:test].enhance ['test:foreman_opentofu']
28
+
29
+ load 'tasks/jenkins.rake'
30
+ Rake::Task['jenkins:unit'].enhance ['test:foreman_opentofu', 'foreman_opentofu:rubocop'] if Rake::Task.task_defined?(:'jenkins:unit')
data/locale/Makefile ADDED
@@ -0,0 +1,73 @@
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 = $(shell ruby -rrubygems -e 'puts Gem::Specification::load(Dir.glob("../*.gemspec")[0]).name')
10
+ VERSION = $(shell ruby -rrubygems -e 'puts Gem::Specification::load(Dir.glob("../*.gemspec")[0]).version')
11
+ POTFILE = $(DOMAIN).pot
12
+ MOFILE = $(DOMAIN).mo
13
+ POFILES = $(shell find . -name '$(DOMAIN).po')
14
+ MOFILES = $(patsubst %.po,%.mo,$(POFILES))
15
+ POXFILES = $(patsubst %.po,%.pox,$(POFILES))
16
+ EDITFILES = $(patsubst %.po,%.edit.po,$(POFILES))
17
+ JSFILES = $(shell find ../app/assets/javascripts/*/locale -name '$(DOMAIN).js')
18
+
19
+ %.mo: %.po
20
+ mkdir -p $(shell dirname $@)/LC_MESSAGES
21
+ msgfmt -o $(shell dirname $@)/LC_MESSAGES/$(MOFILE) $<
22
+
23
+ # Generate MO files from PO files
24
+ all-mo: $(MOFILES)
25
+
26
+ # Check for malformed strings
27
+ %.pox: %.po
28
+ msgfmt -c $<
29
+ pofilter --nofuzzy -t variables -t blank -t urls -t emails -t long -t newlines \
30
+ -t endwhitespace -t endpunc -t puncspacing -t options -t printf -t validchars --gnome $< > $@
31
+ cat $@
32
+ ! grep -q msgid $@
33
+
34
+ %.edit.po: %.po.time_stamp
35
+ touch $@
36
+
37
+ # gettext will trash the .edit.po file if the time stamp doesn't exist or is older than the po file
38
+ %.po.time_stamp: %.po
39
+ touch --reference $< $@
40
+
41
+ # Prevent make from treating this as an intermediate file to be cleaned up
42
+ .PRECIOUS: %.po.time_stamp
43
+
44
+ check: $(POXFILES)
45
+
46
+ # Unify duplicate translations
47
+ uniq-po:
48
+ for f in $(shell find ./ -name "*.po") ; do \
49
+ msguniq $$f -o $$f ; \
50
+ done
51
+
52
+ tx-pull: $(EDITFILES)
53
+ # Initialize new languages
54
+ cd .. && tx pull -f --all --minimum-perc 50
55
+ # Force update all existing languages
56
+ cd .. && tx pull -f --minimum-perc 0
57
+ for f in $(EDITFILES) ; do \
58
+ sed -i 's/^\("Project-Id-Version: \).*$$/\1$(DOMAIN) $(VERSION)\\n"/' $$f; \
59
+ done
60
+
61
+ tx-update: tx-pull
62
+ @echo
63
+ @echo Run rake plugin:gettext[$(DOMAIN)] from the Foreman installation
64
+ @echo then run rake plugin:po_to_json[$(DOMAIN)] from the Foreman installation
65
+ @echo then run make -C locale mo-files to finish
66
+ @echo
67
+
68
+ mo-files: $(MOFILES)
69
+ git add $(POFILES) $(POTFILE) $(JSFILES) ../locale/*/LC_MESSAGES
70
+ git commit -m "i18n - pulling from tx"
71
+ @echo
72
+ @echo Changes commited!
73
+ @echo
@@ -0,0 +1,19 @@
1
+ # foreman_opentofu
2
+ #
3
+ # This file is distributed under the same license as foreman_opentofu.
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_opentofu
2
+ #
3
+ # This file is distributed under the same license as foreman_opentofu.
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_opentofu.gemspec
2
+ _('TODO: Description of ForemanPluginTemplate.')
@@ -0,0 +1,67 @@
1
+ require 'test_helper'
2
+
3
+ module Api
4
+ module V2
5
+ class TfStatesControllerTest < ActionController::TestCase
6
+ setup do
7
+ @tf_one = FactoryBot.create(:tf_state)
8
+ @tf_two = FactoryBot.create(:tf_state)
9
+ end
10
+
11
+ test 'should show tf_state when present' do
12
+ get :show, params: { name: @tf_one.name }
13
+
14
+ assert_response :success
15
+ assert_equal 'application/json; charset=utf-8', @response.content_type
16
+
17
+ body = ActiveSupport::JSON.decode(@response.body)
18
+ assert_equal 'bar', body['foo']
19
+ end
20
+
21
+ test 'should return 404 when tf_state is missing' do
22
+ get :show, params: { name: 'missing-vm' }
23
+
24
+ assert_response :not_found
25
+ assert_equal '', @response.body
26
+ end
27
+
28
+ test 'should create tf_state with valid json body' do
29
+ attrs = { hello: 'world' }
30
+ assert_difference('ForemanOpentofu::TfState.count', 1) do
31
+ @request.env['CONTENT_TYPE'] = 'application/json'
32
+ post :create, params: { name: 'new-vm' }, body: attrs.to_json
33
+ end
34
+
35
+ assert_response :success
36
+
37
+ tf_state = ForemanOpentofu::TfState.find_by(name: 'new-vm')
38
+ assert_not_nil tf_state
39
+ assert_equal 'world', ActiveSupport::JSON.decode(tf_state.state)['hello']
40
+ end
41
+
42
+ test 'should update tf_state when already exists' do
43
+ post :create, params: { name: @tf_one.name }, body: { updated: true }.to_json
44
+
45
+ assert_response :success
46
+
47
+ assert ActiveSupport::JSON.decode(@tf_one.reload.state)['updated']
48
+ end
49
+
50
+ test 'should destroy tf_state when present' do
51
+ assert_difference('ForemanOpentofu::TfState.count', -1) do
52
+ delete :destroy, params: { name: @tf_two.name }
53
+ end
54
+
55
+ assert_response :success
56
+ end
57
+
58
+ test 'should return ok when destroying missing tf_state' do
59
+ assert_no_difference('ForemanOpentofu::TfState.count') do
60
+ delete :destroy, params: { name: 'missing-vm' }
61
+ end
62
+
63
+ assert_response :success
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,26 @@
1
+ FactoryBot.modify do
2
+ factory :compute_resource do
3
+ provider { 'Tofu' }
4
+
5
+ trait :opentofu_nutanix do
6
+ opentofu_provider { :nutanix }
7
+ user { 'nuser' }
8
+ password { 'npassword' }
9
+ sequence(:url) { |n| "#{n}.example.com" }
10
+ # uuid { 'vdatacenter' } # alias for datacenter
11
+ # after(:build) { |cr| cr.stubs(:update_public_key) }
12
+ end
13
+
14
+ trait :opentofu_ovirt do
15
+ opentofu_provider { :ovirt }
16
+ user { 'ovuser' }
17
+ password { 'ovpassword' }
18
+ sequence(:url) { |n| "#{n}.example.com" }
19
+ end
20
+ end
21
+ end
22
+
23
+ FactoryBot.define do
24
+ factory :opentofu_nutanix_cr, parent: :compute_resource, class: ForemanOpentofu::Tofu, traits: [:opentofu_nutanix]
25
+ factory :opentofu_ovirt_cr, parent: :compute_resource, class: ForemanOpentofu::Tofu, traits: [:opentofu_ovirt]
26
+ end
@@ -0,0 +1,7 @@
1
+ FactoryBot.modify do
2
+ factory :host do
3
+ trait :foreman_opentofu do
4
+ name { 'foreman_opentofu' }
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ FactoryBot.define do
2
+ factory :tf_state, class: 'ForemanOpentofu::TfState' do
3
+ sequence(:name) { |n| "vm-#{n}" }
4
+ sequence(:uuid) { |n| "uuid-#{n}" }
5
+ state { '{"foo":"bar"}' }
6
+ end
7
+ end
@@ -0,0 +1,147 @@
1
+ require 'test_helper'
2
+
3
+ module ForemanOpentofu
4
+ class OpentofuVMCommandsTest < ActiveSupport::TestCase
5
+ setup do
6
+ @nutanix_cr = FactoryBot.build_stubbed(:opentofu_nutanix_cr)
7
+ @executor = mock('OpentofuExecuter')
8
+ @nutanix_cr.stubs(:client).returns(@executor)
9
+ end
10
+
11
+ test '#find_vm_by_uuid returns ComputeVM' do
12
+ FactoryBot.create(:tf_state)
13
+ @executor.stubs(:run).with('output').returns({ 'id' => 'vm-1' })
14
+ vm = @nutanix_cr.find_vm_by_uuid('uuid-1')
15
+ assert_instance_of ComputeVM, vm
16
+ end
17
+
18
+ test '#find_vm_by_uuid wraps exceptions' do
19
+ @executor.stubs(:run).raises(StandardError.new('boom'))
20
+
21
+ assert_raises(Foreman::WrappedException) do
22
+ @nutanix_cr.find_vm_by_uuid('uuid-1')
23
+ end
24
+ end
25
+
26
+ test '#new_vm returns OpenStruct with attributes' do
27
+ @executor.stubs(:run).with('new').returns(
28
+ 'resource_changes' => [
29
+ { 'change' => { 'after' => { 'name' => 'vm1' } } },
30
+ ]
31
+ )
32
+
33
+ vm = @nutanix_cr.new_vm('name' => 'vm1')
34
+
35
+ assert_instance_of OpenStruct, vm
36
+ assert_equal 'vm1', vm.name
37
+ end
38
+
39
+ test '#create_vm returns ComputeVM' do
40
+ @executor.stubs(:run).with('create').returns({ 'id' => 'vm1' })
41
+
42
+ vm = @nutanix_cr.create_vm('name' => 'vm1')
43
+
44
+ assert_instance_of ComputeVM, vm
45
+ end
46
+
47
+ test '#create_vm wraps exceptions' do
48
+ @executor.stubs(:run).raises(StandardError.new('boom'))
49
+
50
+ assert_raises(Foreman::WrappedException) do
51
+ @nutanix_cr.create_vm('name' => 'vm1')
52
+ end
53
+ end
54
+
55
+ test '#destroy_vm deletes tf_state' do
56
+ tf_state = FactoryBot.create(:tf_state)
57
+
58
+ @executor.stubs(:run).with('destroy')
59
+ assert_difference('ForemanOpentofu::TfState.count', -1) do
60
+ @nutanix_cr.destroy_vm(tf_state.uuid)
61
+ end
62
+ end
63
+
64
+ test '#destroy_vm does nothing when tf_state missing' do
65
+ @executor.stubs(:run).with('destroy')
66
+
67
+ assert_nothing_raised do
68
+ @nutanix_cr.destroy_vm('missing')
69
+ end
70
+ end
71
+
72
+ test '#start_vm returns true when powered on' do
73
+ @executor.stubs(:run).with('create').returns(
74
+ 'vm' => { 'power_state' => 'on' }
75
+ )
76
+
77
+ assert @nutanix_cr.start_vm('vm1')
78
+ end
79
+
80
+ test '#stop_vm returns true when powered off' do
81
+ @executor.stubs(:run).with('create').returns(
82
+ 'vm' => { 'power_state' => 'off' }
83
+ )
84
+
85
+ assert @nutanix_cr.stop_vm('vm1')
86
+ end
87
+
88
+ test '#save_vm updates existing vm and returns ComputeVM without creating new TfState' do
89
+ tf_state = FactoryBot.create(:tf_state, uuid: 'uuid1', name: 'existing-vm')
90
+
91
+ @executor.stubs(:run).with('create').returns({ 'id' => tf_state.uuid })
92
+
93
+ assert_no_difference('ForemanOpentofu::TfState.count') do
94
+ vm = @nutanix_cr.save_vm('uuid1', [{ 'cpu' => 4 }])
95
+ assert_instance_of ComputeVM, vm
96
+ end
97
+ end
98
+
99
+ test '#save_vm updates vm with no attributes without creating new TfState' do
100
+ tf_state = FactoryBot.create(:tf_state, uuid: 'uuid1', name: 'existing-vm')
101
+
102
+ @executor.stubs(:run).with('create').returns({ 'id' => tf_state.uuid })
103
+
104
+ assert_no_difference('ForemanOpentofu::TfState.count') do
105
+ vm = @nutanix_cr.save_vm('uuid1', [])
106
+ assert_instance_of ComputeVM, vm
107
+ end
108
+ end
109
+
110
+ test '#save_vm wraps exceptions and does not create new TfState' do
111
+ FactoryBot.create(:tf_state, uuid: 'uuid1', name: 'existing-vm')
112
+
113
+ @executor.stubs(:run).raises(StandardError.new('update failed'))
114
+
115
+ assert_no_difference('ForemanOpentofu::TfState.count') do
116
+ assert_raises(Foreman::WrappedException) do
117
+ @nutanix_cr.save_vm('uuid1', [{ 'cpu' => 8 }])
118
+ end
119
+ end
120
+ end
121
+
122
+ test '#save_vm fails when TfState is missing' do
123
+ assert_no_difference('ForemanOpentofu::TfState.count') do
124
+ ex = assert_raises(StandardError) do
125
+ @nutanix_cr.save_vm('missing', [{ 'cpu' => 4 }])
126
+ end
127
+ assert_match(/VM with UUID missing does not exist/, ex.message)
128
+ end
129
+ end
130
+
131
+ test '#test_connection runs tofu test_connection' do
132
+ @executor.stubs(:run).with('test_connection')
133
+
134
+ @nutanix_cr.test_connection
135
+
136
+ assert_empty @nutanix_cr.errors
137
+ end
138
+
139
+ test '#test_connection adds error on failure' do
140
+ @executor.stubs(:run).raises(StandardError.new('fail'))
141
+
142
+ @nutanix_cr.test_connection
143
+
144
+ assert @nutanix_cr.errors.any?
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,43 @@
1
+ require 'test_plugin_helper'
2
+ # require 'models/compute_resources/compute_resource_test_helpers'
3
+
4
+ module ForemanOpentofu
5
+ class NutanixTest < ActiveSupport::TestCase
6
+ # include ComputeResourceTestHelpers
7
+
8
+ let(:subject) { FactoryBot.build_stubbed(:opentofu_nutanix_cr) }
9
+
10
+ should validate_presence_of(:url)
11
+ should validate_presence_of(:user)
12
+ should validate_presence_of(:password)
13
+ # should validate_presence_of(:datacenter)
14
+ # should allow_values('vcenter.example.com', 'vcenter').for(:server)
15
+
16
+ test 'valid nutanix resource' do
17
+ assert subject.valid?
18
+ end
19
+
20
+ # FIXME: some of the following might be redundant
21
+ test 'invalid without URL' do
22
+ subject.url = nil
23
+ assert_not subject.valid?
24
+ assert_includes subject.errors[:url], "can't be blank"
25
+ end
26
+
27
+ test 'invalid without username' do
28
+ subject.user = nil
29
+ assert_not subject.valid?
30
+ assert_includes subject.errors[:user], "can't be blank"
31
+ end
32
+
33
+ test 'invalid without password' do
34
+ subject.password = nil
35
+ assert_not subject.valid?
36
+ assert_includes subject.errors[:password], "can't be blank"
37
+ end
38
+
39
+ test 'provided attributes includes mac' do
40
+ assert_includes subject.provided_attributes.keys, :mac
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,31 @@
1
+ require 'test_plugin_helper'
2
+
3
+ module ForemanOpentofu
4
+ class AppWrapperTest < ActiveSupport::TestCase
5
+ let(:app_wrapper) { AppWrapper.new('/tmp') }
6
+
7
+ test 'params parsed' do
8
+ params = app_wrapper.send(:parse_params, ['tofu', 'init', '--json'])
9
+ assert_kind_of(Array, params)
10
+ assert_equal(3, params.length)
11
+ params = app_wrapper.send(:parse_params, '--json')
12
+ assert_kind_of(Array, params)
13
+ assert_equal(1, params.length)
14
+ end
15
+
16
+ test 'command is assembled' do
17
+ cmdline = app_wrapper.send(:command, ['tofu', 'init', '--json'])
18
+ assert_kind_of(String, cmdline)
19
+ assert_equal("'tofu' 'init' '--json' 2>&1", cmdline)
20
+ end
21
+
22
+ test 'tofu_execute() adds default_params' do
23
+ def_p = ['--always', '--added']
24
+ base_c = 'none'
25
+ app_wrapper.expects(:base_command).returns(base_c)
26
+ app_wrapper.expects(:default_params).returns(def_p)
27
+ app_wrapper.expects(:execute).with([base_c, 'noop'] + def_p)
28
+ app_wrapper.send(:tofu_execute, 'noop')
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,62 @@
1
+ require 'test_helper'
2
+
3
+ module ForemanOpentofu
4
+ class OpentofuExecuterTest < ActiveSupport::TestCase
5
+ setup do
6
+ @compute_resource = FactoryBot.build_stubbed(:opentofu_nutanix_cr)
7
+ @cr_attrs = { 'name' => 'vm-1' }
8
+ @executor = OpentofuExecuter.new(@compute_resource, @cr_attrs)
9
+
10
+ @template = FactoryBot.create(:provisioning_template, name: Setting[:provision_nutanix_host_template])
11
+ @executor.stubs(:provision_template).returns(@template)
12
+
13
+ @app_mock = mock('AppWrapper')
14
+ AppWrapper.stubs(:new).returns(@app_mock)
15
+ @app_mock.stubs(:main_configuration=)
16
+ @app_mock.stubs(:init)
17
+ @app_mock.stubs(:plan)
18
+ @app_mock.stubs(:show_plan)
19
+ @app_mock.stubs(:apply)
20
+ @app_mock.stubs(:destroy).returns { ForemanOpentofu::TfState.find_by(uuid: 'uuid-1')&.destroy }
21
+ @app_mock.stubs(:output).with('vm_attrs').returns('identity' => 'uuid-1')
22
+ end
23
+
24
+ test '#run create updates tf_state and returns attrs' do
25
+ tf_state = FactoryBot.create(:tf_state, name: 'vm-1')
26
+ result = @executor.run('create')
27
+ tf_state.reload
28
+ assert_equal tf_state.uuid, result['identity']
29
+ assert_not_nil tf_state.uuid, 'tf_state UUID should be set'
30
+ end
31
+
32
+ test '#run output returns vm_attrs' do
33
+ result = @executor.run('output')
34
+ assert_equal 'uuid-1', result['identity']
35
+ end
36
+
37
+ test '#run destroy calls destroy' do
38
+ @executor.run('destroy')
39
+ assert_nil ForemanOpentofu::TfState.find_by(uuid: 'uuid-1')
40
+ end
41
+
42
+ test '#run new calls plan and show_plan' do
43
+ @app_mock.expects(:plan)
44
+ @app_mock.expects(:show_plan)
45
+ @executor.run('new')
46
+ end
47
+
48
+ test '#run test_connection only plans' do
49
+ @app_mock.expects(:plan)
50
+ @executor.run('test_connection')
51
+ end
52
+
53
+ test '#run with invalid mode raises RuntimeError' do
54
+ assert_raises(RuntimeError) { @executor.run('invalid_mode') }
55
+ end
56
+
57
+ test '#render_template raises exception if nil returned' do
58
+ Foreman::Renderer::UnsafeModeRenderer.stubs(:render).returns(nil)
59
+ assert_raises(Foreman::Exception) { @executor.send(:render_template) }
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,6 @@
1
+ # This calls the main test_helper in Foreman-core
2
+ require 'test_helper'
3
+
4
+ # Add plugin to FactoryBot's paths
5
+ FactoryBot.definition_file_paths << File.join(File.dirname(__FILE__), 'factories')
6
+ FactoryBot.reload