Skip to content

Commit 6da3ee6

Browse files
committed
Fix: Fixes host edit to show correct vm_attributes which were used while creating host. Also normalizes volume attributes as volumes_attributes to be consistent.
1 parent 8eafb27 commit 6da3ee6

11 files changed

Lines changed: 118 additions & 41 deletions

File tree

app/lib/foreman_opentofu/concerns/base_template_scope_extensions.rb

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -94,15 +94,11 @@ def backend_block
9494
end
9595

9696
def build_disks
97-
disks = @cr_attrs['volumes'].presence || @cr_attrs['volumes_attributes'].presence || @compute_resource.default_volumes
98-
return '' if disks.blank?
99-
disks = [disks] if disks.is_a?(Hash)
100-
disks.each_with_index.map do |disk, index|
97+
disk_render_source.each_with_index.filter_map do |disk, index|
10198
# drop removed disks; tofu will drop them automatically, if they are no longer defined
102-
next if disk['_delete'].to_i == 1
99+
next if disk.respond_to?(:[]) && disk['_delete'].to_i == 1
103100

104-
data = @compute_resource.render_disk(disk, self, index)
105-
render_provider_data(data)
101+
rendered_disk(index, disk)
106102
end.join("\n")
107103
end
108104

@@ -115,6 +111,20 @@ def build_nics
115111

116112
private
117113

114+
def disk_render_source
115+
disks = @cr_attrs['volumes'].presence || @cr_attrs['volumes_attributes'].presence || @compute_resource.default_volumes
116+
disks = [disks] if disks.is_a?(Hash)
117+
disks = [] if disks.blank?
118+
disks.empty? ? [nil] : disks
119+
end
120+
121+
def rendered_disk(index, disk)
122+
data = @compute_resource.render_disk(disk, self, index)
123+
return if data.blank?
124+
125+
render_provider_data(data)
126+
end
127+
118128
def nics_from_cr_attrs
119129
nics = @cr_attrs['interfaces'].presence || @cr_attrs['interfaces_attributes'].presence || @compute_resource.default_interfaces
120130
normalize_interfaces(nics).map do |nic|
@@ -126,7 +136,9 @@ def nics_from_cr_attrs
126136
end
127137

128138
def render_provider_data(data)
129-
if data.is_a?(Hash) && data[:resource].present?
139+
if data.is_a?(String)
140+
data
141+
elsif data.is_a?(Hash) && data[:resource].present?
130142
resource = data[:resource]
131143
block_to_hcl(['resource', resource[:type], resource[:name]], resource[:content], depth: 0)
132144
elsif data.is_a?(Hash) && data.size == 1 && data.values.first.is_a?(Hash)

app/models/concerns/foreman_opentofu/vm_command_collection_normalization.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ module VMCommandCollectionNormalization
33
private
44

55
def normalize_vm_args_collections!(args)
6+
args[:image_id] = args[:image] if args.key?(:image)
7+
normalize_vm_boolean_args!(args)
68
[:volumes, :interfaces].each do |collection|
79
raw = args.delete(:"#{collection}_attributes") || args[collection]
810
next if raw.nil?
@@ -11,6 +13,21 @@ def normalize_vm_args_collections!(args)
1113
end
1214
end
1315

16+
def normalize_vm_boolean_args!(args)
17+
vm_boolean_keys.each do |key|
18+
next unless args.key?(key)
19+
args[key] = Foreman::Cast.to_bool(args[key])
20+
end
21+
end
22+
23+
def vm_boolean_keys
24+
return [] unless respond_to?(:tofu_provider) && tofu_provider.respond_to?(:attributes)
25+
26+
tofu_provider.attributes('vm')
27+
.select { |attr| attr['type'] == 'bool' }
28+
.map { |attr| attr['name'].to_sym }
29+
end
30+
1431
def normalize_collection_input(collection, value)
1532
return nested_attributes_for(collection, value) if value.is_a?(Hash) || value.is_a?(ActionController::Parameters)
1633

app/models/foreman_opentofu/compute_vm.rb

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,14 @@ def wait_for(&block)
6161
instance_eval(&block)
6262
end
6363

64-
def volumes
65-
attribute_value('volumes') || []
64+
def volumes_attributes
65+
vols_attrs = attribute_value('volumes_attributes')
66+
return vols_attrs if vols_attrs.is_a?(Hash)
67+
68+
list = vols_attrs.presence || attribute_value('volumes')
69+
return {} if list.blank?
70+
71+
Array(list).each_with_index.to_h { |vol, idx| [idx.to_s, vol] }
6672
end
6773

6874
private

app/models/foreman_opentofu/opentofu_vm_commands.rb

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,14 @@ def stop_vm(name)
5151
end
5252

5353
def save_vm(uuid, attrs)
54+
old_attrs = vm_compute_attributes_for(uuid).to_h.deep_stringify_keys
5455
tf_state = TfState.find_by(uuid: uuid)
5556
raise StandardError, "VM with UUID #{uuid} does not exist" unless tf_state
5657
vm_command_errors('update vm') do
57-
attrs = attrs.empty? ? {} : attrs.first
58-
attrs = attrs.to_h.symbolize_keys
59-
normalize_vm_args_collections!(attrs)
60-
data = client({ 'name' => tf_state.name }.merge(attrs)).run_create
58+
new_attrs = attrs.to_h.deep_stringify_keys
59+
merged_attrs = attrs.empty? ? {} : old_attrs.merge(new_attrs).deep_symbolize_keys
60+
normalize_vm_args_collections!(merged_attrs)
61+
data = client({ 'name' => tf_state.name }.merge(merged_attrs)).run_create
6162
ComputeVM.new(self, data)
6263
end
6364
end

app/models/foreman_opentofu/tofu.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,5 +120,22 @@ def available_resource(resource_name, options = {})
120120
def available_resource_ui_select(resource_name, options = {})
121121
available_resource(resource_name, options)&.map { |obj| [obj['name'], obj['id']] }
122122
end
123+
124+
private
125+
126+
def set_vm_volumes_attributes(vm, vm_attrs)
127+
volumes = if vm.respond_to?(:volumes_attributes)
128+
vm.volumes_attributes.values
129+
else
130+
[]
131+
end
132+
vm_attrs[:volumes_attributes] = Hash[
133+
volumes.each_with_index.map do |volume, idx|
134+
attrs = volume.respond_to?(:attributes) ? volume.attributes : volume
135+
[idx.to_s, attrs]
136+
end
137+
]
138+
vm_attrs
139+
end
123140
end
124141
end

app/views/foreman_opentofu/compute_resources_vms/_indexed_networks_fields.html.erb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<% if compute_resource.is_a?(ForemanOpentofu::Tofu) %>
2+
<% association_attributes = "#{compute_resource.interfaces_attrs_name}_attributes" %>
23
<div class="<%= compute_resource.interfaces_attrs_name %>_fields_template form_template" style="display: none;">
3-
<%= fields_for "#{f.object_name}[#{compute_resource.interfaces_attrs_name}][new_#{compute_resource.interfaces_attrs_name}]", compute_resource.new_interface do |i| %>
4+
<%= fields_for "#{f.object_name}[#{association_attributes}][new_#{compute_resource.interfaces_attrs_name}]", compute_resource.new_interface do |i| %>
45
<%= render :partial => provider_partial(compute_resource, 'network'),
56
:locals => { :f => i, :compute_resource => compute_resource, :new_host => new_host, :new_vm => new_vm, :remove_title => _('remove network interface'), :selected_cluster => selected_cluster },
67
:layout => 'compute_resources_vms/form/deletable_layout' %>

app/views/foreman_opentofu/compute_resources_vms/_indexed_volumes_fields.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<% if compute_resource.is_a?(ForemanOpentofu::Tofu) %>
22
<div class="volumes_fields_template form_template" style="display: none;">
3-
<%= fields_for "#{f.object_name}[volumes][new_volumes]", volume do |i| %>
3+
<%= fields_for "#{f.object_name}[volumes_attributes][new_volumes]", volume do |i| %>
44
<%= render :partial => provider_partial(compute_resource, 'volume'),
55
:locals => { :f => i, :compute_resource => compute_resource, :new_host => new_vm, :new_vm => new_vm, :remove_title => _('remove storage volume') },
66
:layout => "compute_resources_vms/form/#{item_layout}_layout" %>

app/views/foreman_opentofu/compute_resources_vms/form/tofu/_interfaces_fields.html.erb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
<%
22
association = compute_resource.interfaces_attrs_name
3+
association_attributes = "#{association}_attributes"
34
interfaces = Array.wrap(f.object.public_send(association))
45
interfaces = [compute_resource.new_interface].compact if interfaces.empty?
56
%>
67
<% interfaces.each_with_index do |interface_obj, idx| %>
7-
<%= fields_for "#{f.object_name}[#{association}][#{idx}]", interface_obj do |i| %>
8+
<%= fields_for "#{f.object_name}[#{association_attributes}][#{idx}]", interface_obj do |i| %>
89
<%= render :partial => provider_partial(compute_resource, 'network'),
910
:locals => { :f => i, :compute_resource => compute_resource, :new_host => new_host, :new_vm => new_vm, :remove_title => _('remove network interface'), :selected_cluster => selected_cluster },
1011
:layout => "compute_resources_vms/form/#{item_layout}_layout" %>

app/views/foreman_opentofu/compute_resources_vms/form/tofu/_volumes_fields.html.erb

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
<%
2-
volumes = Array.wrap(f.object.volumes)
2+
volumes = f.object.volumes_attributes
3+
volumes =
4+
if volumes.is_a?(Hash)
5+
volumes.sort_by { |key, _| key.to_s.sub('new_', '').to_i }
6+
else
7+
Array.wrap(volumes).each_with_index.map { |value, idx| [idx.to_s, value] }
8+
end
39
%>
4-
<% volumes.each_with_index do |volume_obj, idx| %>
5-
<%= fields_for "#{f.object_name}[volumes][#{idx}]", volume_obj do |i| %>
10+
<% volumes.each do |volume_key, volume_obj| %>
11+
<% volume_obj = compute_resource.new_volume(volume_obj.to_h) if volume_obj.is_a?(Hash) %>
12+
<%= fields_for "#{f.object_name}[volumes_attributes][#{volume_key}]", volume_obj do |i| %>
13+
<%= i.hidden_field :id if volume_obj.respond_to?(:id) && volume_obj.id.present? %>
614
<%= render :partial => provider_partial(compute_resource, 'volume'),
715
:locals => { :f => i, :compute_resource => compute_resource, :new_host => new_host, :new_vm => new_vm, :remove_title => _('remove storage volume') },
816
:layout => "compute_resources_vms/form/#{item_layout}_layout" %>

app/views/templates/provisioning/hetzner_provision_host.erb

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@ resource "hcloud_server" "node1" {
2525
<%= vm_attributes(['name', 'image']) %>
2626

2727
<%- if @cr_attrs['image_id'].present? %>
28-
image = <%= @cr_attrs['image_id'] %>
29-
<%- if @user_data_filename.present? %>
28+
image = <%= @cr_attrs['image_id'] %>
29+
<%- if @user_data_filename.present?%>
3030
user_data = "${file("<%= @user_data_filename %>")}"
31+
elsif @cr_attrs['user_data'].present? %>
32+
user_data = <%= @cr_attrs['user_data'] %>
3133
<%- end %>
3234
<%- end %>
3335

@@ -39,6 +41,10 @@ resource "hcloud_server" "node1" {
3941
}
4042
}
4143

44+
locals {
45+
disks = <%= to_hcl(@cr_attrs['volumes_attributes'] || {}, snippet: false) %>
46+
}
47+
4248
<%= build_disks %>
4349

4450
output "vm_attrs" {
@@ -47,6 +53,15 @@ output "vm_attrs" {
4753
vm_ip_address = hcloud_server.node1.ipv4_address
4854
vm_ip6_address = hcloud_server.node1.ipv6_address
4955
vm = hcloud_server.node1
56+
volumes_attributes = try({
57+
for v in values(hcloud_volume.volumes) : tostring(v.id) => {
58+
id = v.id
59+
name = v.name
60+
size = v.size
61+
automount = try(v.automount, null)
62+
format = try(v.format, null)
63+
}
64+
}, {})
5065
}
5166
}
5267

0 commit comments

Comments
 (0)