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.
- checksums.yaml +7 -0
- data/LICENSE +674 -0
- data/README.md +134 -0
- data/Rakefile +30 -0
- data/app/controllers/api/v2/tf_states_controller.rb +52 -0
- data/app/controllers/concerns/foreman_opentofu/compute_resources_vms_controller.rb +17 -0
- data/app/controllers/concerns/foreman_opentofu/controller/parameters/compute_resource.rb +42 -0
- data/app/lib/foreman_opentofu/concerns/base_template_scope_extensions.rb +89 -0
- data/app/models/concerns/orchestration/tofu/compute.rb +24 -0
- data/app/models/foreman_opentofu/compute_vm.rb +99 -0
- data/app/models/foreman_opentofu/opentofu_vm_commands.rb +88 -0
- data/app/models/foreman_opentofu/tf_state.rb +8 -0
- data/app/models/foreman_opentofu/tofu.rb +77 -0
- data/app/overrides/compute_resources/remove_virtual_machines_tab.rb +8 -0
- data/app/services/foreman_opentofu/app_wrapper.rb +107 -0
- data/app/services/foreman_opentofu/compute_fetcher.rb +51 -0
- data/app/services/foreman_opentofu/opentofu_executer.rb +78 -0
- data/app/services/foreman_opentofu/provider_type.rb +30 -0
- data/app/services/foreman_opentofu/provider_type_manager.rb +36 -0
- data/app/views/compute_resources/form/_tofu.html.erb +9 -0
- data/app/views/compute_resources/show/_tofu.html.erb +8 -0
- data/app/views/compute_resources_vms/form/tofu/_base.html.erb +13 -0
- data/app/views/compute_resources_vms/form/tofu/_dynamic_attrs.html.erb +18 -0
- data/app/views/compute_resources_vms/form/tofu/_network.html.erb +5 -0
- data/app/views/compute_resources_vms/index/_tofu.html.erb +0 -0
- data/app/views/templates/provisioning/nutanix_provision_host.erb +51 -0
- data/app/views/templates/provisioning/ovirt_provision_host.erb +68 -0
- data/config/initializers/compute_attrs.rb +19 -0
- data/config/nutanix.json +27 -0
- data/config/ovirt.json +18 -0
- data/config/routes.rb +13 -0
- data/db/migrate/20250625192757_create_tf_state.foreman_opentofu.rb +10 -0
- data/db/seeds.d/71_provisioning_templates.rb +9 -0
- data/lib/foreman_opentofu/engine.rb +75 -0
- data/lib/foreman_opentofu/provider_types/nutanix.rb +6 -0
- data/lib/foreman_opentofu/provider_types/ovirt.rb +2 -0
- data/lib/foreman_opentofu/provider_types.rb +3 -0
- data/lib/foreman_opentofu/version.rb +3 -0
- data/lib/foreman_opentofu.rb +6 -0
- data/lib/tasks/foreman_opentofu_tasks.rake +30 -0
- data/locale/Makefile +73 -0
- data/locale/en/foreman_opentofu.po +19 -0
- data/locale/foreman_opentofu.pot +19 -0
- data/locale/gemspec.rb +2 -0
- data/test/controllers/api/v2/tf_states_controller_test.rb +67 -0
- data/test/factories/compute_resources.rb +26 -0
- data/test/factories/foreman_opentofu_factories.rb +7 -0
- data/test/factories/tf_state_factories.rb +7 -0
- data/test/models/foreman_opentofu/opentofu_vm_commands_test.rb +147 -0
- data/test/models/foreman_opentofu_test.rb +43 -0
- data/test/services/app_wrapper_test.rb +31 -0
- data/test/services/opentofu_executer_test.rb +62 -0
- data/test/test_plugin_helper.rb +6 -0
- 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,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,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,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
|