Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Firewalls on subnet UI #4

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions lib/validation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,15 @@ def self.validate_postgres_superuser_password(original_password, repeat_password
end

def self.validate_cidr(cidr)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good enhancement to support both IPv4 and IPv6 CIDR formats.

NetAddr::IPv4Net.parse(cidr)
if cidr.include?(".")
NetAddr::IPv4Net.parse(cidr)
elsif cidr.include?(":")
NetAddr::IPv6Net.parse(cidr)
else
fail ValidationFailed.new({cidr: "Invalid CIDR"})
end
rescue NetAddr::ValidationError
fail ValidationFailed.new({CIDR: "Invalid CIDR"})
fail ValidationFailed.new({cidr: "Invalid CIDR"})
Comment on lines 123 to +132

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure that the new IPv6 validation logic correctly handles edge cases, such as abbreviated IPv6 addresses and dual-stack (IPv4-mapped IPv6) addresses, to prevent potential validation bypasses or incorrect rejections.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using consistent key casing in the error message (e.g., 'cidr' instead of 'CIDR').

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed error message key from 'CIDR' to 'cidr' for consistency.

end

def self.validate_port_range(port_range)
Expand Down
33 changes: 33 additions & 0 deletions migrate/20240409_move_firewall_to_subnet.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# frozen_string_literal: true

Sequel.migration do
up do
alter_table(:firewall) do
add_foreign_key :private_subnet_id, :private_subnet, type: :uuid

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding null: false to the private_subnet_id column if it should always be present.

end

run <<~SQL
UPDATE firewall f
SET private_subnet_id = (
SELECT n.private_subnet_id
FROM nic n
WHERE n.vm_id = f.vm_id
);
Comment on lines +10 to +15

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure that the subquery in the UPDATE statement does not return multiple rows, which would cause an error.

SQL
end

down do
run <<~SQL
UPDATE firewall f
SET vm_id = (
SELECT n.vm_id
FROM nic n
WHERE n.private_subnet_id = f.private_subnet_id
);
Comment on lines +20 to +26
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The down migration assumes a one-to-one relationship between nic and private_subnet, which might not hold true if a subnet is linked to multiple VMs. This could result in incorrect vm_id assignments when rolling back. Consider adding a validation or a more complex SQL query to handle cases where a private_subnet is associated with multiple VMs.

Comment on lines +21 to +26

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure that the subquery in the down migration's UPDATE statement does not return multiple rows, which would cause an error.

SQL

alter_table(:firewall) do
drop_column :private_subnet_id
end
end
end
21 changes: 21 additions & 0 deletions migrate/20240412_add_multiple_fw_to_subnet.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

Sequel.migration do
up do
create_table(:firewalls_private_subnets) do
foreign_key :private_subnet_id, :private_subnet, type: :uuid
foreign_key :firewall_id, :firewall, type: :uuid
Comment on lines +6 to +7

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure the referenced table names are plural as per convention. It should be :private_subnets and :firewalls.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we use singular, which is unusual. And so, this is incorrect.

primary_key %i[private_subnet_id firewall_id]
end

run <<~SQL
INSERT INTO firewalls_private_subnets (private_subnet_id, firewall_id)
SELECT private_subnet_id, id AS firewall_id

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure that the private_subnet_id column in the firewall table is not used elsewhere in the codebase before dropping it.

FROM firewall;
SQL

alter_table(:firewall) do
drop_column :private_subnet_id

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verify cascading effects of dropping private_subnet_id on existing data and functionalities.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't really say anything...

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before dropping the private_subnet_id column, ensure that no code paths rely on this column. This change might require updates in model associations or queries that reference private_subnet_id directly.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

end
end
end
15 changes: 15 additions & 0 deletions migrate/20240425_remove_fw_vm_id.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

Sequel.migration do
up do
alter_table(:firewall) do
drop_column :vm_id

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure that all references to vm_id in the firewall table have been updated or removed in the codebase to prevent any runtime errors.

end
end

down do
alter_table(:firewall) do
add_foreign_key :vm_id, :vm, type: :uuid
end
end
end
43 changes: 41 additions & 2 deletions model/firewall.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,28 @@

class Firewall < Sequel::Model
one_to_many :firewall_rules, key: :firewall_id
many_to_one :vm, key: :vm_id
many_to_many :private_subnets

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure many_to_many :private_subnets association is correctly set up with the corresponding join table and foreign keys, especially since this is a shift from a many_to_one relationship with VMs.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The many_to_one :vm, key: :vm_id association has been removed. Ensure that this change is intentional and that all references to vm in other parts of the codebase have been updated accordingly.


plugin :association_dependencies, firewall_rules: :destroy

include ResourceMethods
include Authorization::TaggableMethods
include Authorization::HyperTagMethods
def hyper_tag_name(project)
"project/#{project.ubid}/firewall/#{ubid}"
end

dataset_module Pagination
dataset_module Authorization::Dataset

def path
"/firewall/#{ubid}"
end

def remove_firewall_rule(firewall_rule)
firewall_rule.destroy
private_subnets.map(&:incr_update_firewall_rules)
Comment on lines +25 to +27

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verify that firewall_rule.destroy and the subsequent private_subnets.map(&:incr_update_firewall_rules) call in remove_firewall_rule method handle exceptions or failures gracefully, to maintain data integrity.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using each(&:incr_update_firewall_rules) instead of map(&:incr_update_firewall_rules) for better readability and to avoid confusion, as map is typically used for transforming arrays.

end

def insert_firewall_rule(cidr, port_range)
fwr = FirewallRule.create_with_id(
Expand All @@ -17,7 +34,29 @@ def insert_firewall_rule(cidr, port_range)
port_range: port_range
)

vm&.incr_update_firewall_rules
private_subnets.each(&:incr_update_firewall_rules)
fwr
end

def destroy
DB.transaction do
private_subnets.each(&:incr_update_firewall_rules)
FirewallsPrivateSubnets.where(firewall_id: id).all.each(&:destroy)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure that the FirewallsPrivateSubnets.where(firewall_id: id).all.each(&:destroy) line correctly handles the deletion of associated records to prevent orphaned records in the database.

super
Comment on lines +41 to +45

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the destroy method, ensure that the transaction correctly handles the deletion of associated FirewallsPrivateSubnets records and that super is called in a way that ensures the Firewall record itself is only deleted if no errors occur in the transaction.

end
end

def associate_with_private_subnet(private_subnet, apply_firewalls: true)
add_private_subnet(private_subnet)
private_subnet.incr_update_firewall_rules if apply_firewalls
Comment on lines +49 to +51

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For associate_with_private_subnet, confirm that add_private_subnet(private_subnet) correctly updates both the Firewall and PrivateSubnet models, especially regarding the apply_firewalls flag's impact.

end

def disassociate_from_private_subnet(private_subnet, apply_firewalls: true)
FirewallsPrivateSubnets.where(
private_subnet_id: private_subnet.id,
firewall_id: id
).destroy

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using destroy_all instead of destroy for better readability and to ensure all matching records are deleted in one go.


private_subnet.incr_update_firewall_rules if apply_firewalls
Comment on lines +54 to +60

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In disassociate_from_private_subnet, ensure that the deletion of the association does not inadvertently affect other related records or leave orphaned entries in the database.

end
end
7 changes: 7 additions & 0 deletions model/firewalls_private_subnets.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

require_relative "../model"

class FirewallsPrivateSubnets < Sequel::Model
include ResourceMethods
end
Comment on lines +5 to +7

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The model lacks associations (e.g., many_to_many with Firewalls and PrivateSubnets) and methods to support operations described in the PR. Consider implementing these to fully integrate with the rest of the changes.

Comment on lines +5 to +7
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Define associations for FirewallsPrivateSubnets. Consider adding many_to_many associations to link Firewalls and PrivateSubnets, enabling the management of their relationship.

3 changes: 2 additions & 1 deletion model/postgres/postgres_server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,8 @@ def health_monitor_socket_path
end

def create_resource_firewall_rules
fw = Firewall.create_with_id(vm_id: vm.id, name: ubid.to_s, description: "Postgres default firewall")
fw = Firewall.create_with_id(name: ubid.to_s, description: "Postgres default firewall")
fw.add_private_subnet(vm.private_subnets.first)
Comment on lines +168 to +169

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure that vm.private_subnets.first is not nil to avoid potential runtime errors.

resource.firewall_rules.each do |pg_fwr|
fw.insert_firewall_rule(pg_fwr.cidr.to_s, Sequel.pg_range(5432..5432))
end
Comment on lines 167 to 172

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure add_private_subnet correctly handles cases where a VM might not have a private subnet associated, or where multiple subnets could be considered. This change assumes the first private subnet is always the correct choice, which might not hold in all configurations.

Expand Down
11 changes: 9 additions & 2 deletions model/private_subnet.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class PrivateSubnet < Sequel::Model
many_to_many :vms, join_table: Nic.table_name, left_key: :private_subnet_id, right_key: :vm_id
one_to_many :nics, key: :private_subnet_id
one_to_one :strand, key: :id
one_to_many :firewall_rules
many_to_many :firewalls

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Transition from one_to_many :firewall_rules to many_to_many :firewalls aligns with the broader architectural changes. Ensure that all existing and new functionalities that depend on firewall rules within private subnets are updated to work with this new relationship model.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure that the FirewallsPrivateSubnets model is correctly defined and that the join table exists in the database schema.


PRIVATE_SUBNET_RANGES = [
"10.0.0.0/8",
Expand All @@ -23,6 +23,13 @@ def hyper_tag_name(project)

include Authorization::TaggableMethods

def destroy
DB.transaction do
FirewallsPrivateSubnets.where(private_subnet_id: id).all.each(&:destroy)
super
Comment on lines +27 to +29

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using delete instead of destroy for FirewallsPrivateSubnets records if callbacks are not needed.

end
Comment on lines +26 to +30

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The custom destroy method ensures that FirewallsPrivateSubnets associations are cleaned up before the subnet itself is destroyed. This is crucial for maintaining database integrity. Verify that all related entities are considered for cleanup to avoid orphaned records.

end

def display_location
LocationNameConverter.to_display_name(location)
end
Expand All @@ -42,7 +49,7 @@ def display_state
end

include SemaphoreMethods
semaphore :destroy, :refresh_keys, :add_new_nic
semaphore :destroy, :refresh_keys, :add_new_nic, :update_firewall_rules

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The addition of :update_firewall_rules to the semaphore actions list indicates new or modified functionality related to firewall rule management. It's important to review related processes to ensure they correctly implement semaphore locking, especially in concurrent environments.


def self.random_subnet
PRIVATE_SUBNET_RANGES.sample
Expand Down
1 change: 1 addition & 0 deletions model/project.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class Project < Sequel::Model
many_to_many :minio_clusters, join_table: AccessTag.table_name, left_key: :project_id, right_key: :hyper_tag_id
many_to_many :private_subnets, join_table: AccessTag.table_name, left_key: :project_id, right_key: :hyper_tag_id
many_to_many :postgres_resources, join_table: AccessTag.table_name, left_key: :project_id, right_key: :hyper_tag_id
many_to_many :firewalls, join_table: AccessTag.table_name, left_key: :project_id, right_key: :hyper_tag_id

one_to_many :invoices, order: Sequel.desc(:created_at)

Expand Down
7 changes: 5 additions & 2 deletions model/vm.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ class Vm < Sequel::Model
one_to_one :assigned_vm_address, key: :dst_vm_id, class: :AssignedVmAddress
one_to_many :vm_storage_volumes, key: :vm_id, order: Sequel.desc(:boot)
one_to_one :active_billing_record, class: :BillingRecord, key: :resource_id do |ds| ds.active end
one_to_many :firewalls, key: :vm_id

plugin :association_dependencies, sshable: :destroy, assigned_vm_address: :destroy, vm_storage_volumes: :destroy, firewalls: :destroy
plugin :association_dependencies, sshable: :destroy, assigned_vm_address: :destroy, vm_storage_volumes: :destroy
Comment on lines 14 to +15

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure that removing the direct association of firewalls with VMs (one_to_many :firewalls, key: :vm_id) and the corresponding association_dependencies does not leave orphaned firewall records or dependencies that might affect VM deletion or firewall management.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the firewalls association from the association_dependencies plugin.


dataset_module Pagination
dataset_module Authorization::Dataset
Expand All @@ -31,6 +30,10 @@ def hyper_tag_name(project)

include Authorization::TaggableMethods

def firewalls
private_subnets.flat_map(&:firewalls)
end
Comment on lines +33 to +35

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verify that the new firewalls method correctly aggregates all firewalls from associated private subnets, especially in scenarios where a VM might be associated with multiple private subnets, ensuring that firewall rules are applied and managed accurately across the system.

Comment on lines +33 to +35

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a new firewalls method to retrieve firewalls from associated private subnets.


def display_location
LocationNameConverter.to_display_name(location)
end
Expand Down
1 change: 0 additions & 1 deletion prog/postgres/postgres_server_nexus.rb
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,6 @@ def before_run

# create a new set of firewall rules
postgres_server.create_resource_firewall_rules
Comment on lines 291 to 293

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure that the removal of vm.incr_update_firewall_rules does not impact the logic for tracking changes to firewall rules, especially in contexts where this counter might still be expected to reflect changes.

vm.incr_update_firewall_rules

hop_wait
end
Expand Down
5 changes: 4 additions & 1 deletion prog/vm/github_runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,10 @@ def setup_info
end

if vm
vm.private_subnets.each { _1.incr_destroy }
vm.private_subnets.each do |subnet|
subnet.firewalls.map(&:destroy)
subnet.incr_destroy
Comment on lines +355 to +357

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure that destroying firewalls does not violate any foreign key constraints or trigger unintended side effects in the database. Consider adding checks or transactions to handle potential exceptions.

Comment on lines +355 to +357

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure that destroying firewalls before incrementing the destroy counter for subnets does not introduce any race conditions or dependencies that might affect the destruction process.

Comment on lines +355 to +357

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure firewalls are destroyed before subnets.

end

# If the runner is not assigned any job and we destroy it after a
# timeline, the workflow_job is nil, in that case, we want to be able to
Expand Down
6 changes: 1 addition & 5 deletions prog/vm/nexus.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def self.assemble(public_key, project_id, name: nil, size: "standard-2",
raise "Given subnet doesn't exist with the id #{private_subnet_id}" unless subnet
raise "Given subnet is not available in the given project" unless project.private_subnets.any? { |ps| ps.id == subnet.id }
else
subnet_s = Prog::Vnet::SubnetNexus.assemble(project_id, name: "#{name}-subnet", location: location)
subnet_s = Prog::Vnet::SubnetNexus.assemble(project_id, name: "#{name}-subnet", location: location, allow_only_ssh: allow_only_ssh)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure allow_only_ssh is consistently applied across subnet creations to maintain expected firewall behavior.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing allow_only_ssh to Prog::Vnet::SubnetNexus.assemble ensures that the subnet is configured correctly based on the SSH-only access requirement.

subnet = PrivateSubnet[subnet_s.id]
end
nic_s = Prog::Vnet::NicNexus.assemble(subnet.id, name: "#{name}-nic")
Expand All @@ -90,10 +90,6 @@ def self.assemble(public_key, project_id, name: nil, size: "standard-2",
boot_image: boot_image, ip4_enabled: enable_ip4, pool_id: pool_id, arch: arch) { _1.id = ubid.to_uuid }
nic.update(vm_id: vm.id)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The removal of default firewall rule creation for the VM is appropriate given the new subnet-level firewall management approach.


port_range = allow_only_ssh ? 22..22 : 0..65535
fw = Firewall.create_with_id(vm_id: vm.id, name: "#{name}-default")
["0.0.0.0/0", "::/0"].each { |cidr| FirewallRule.create_with_id(firewall_id: fw.id, cidr: cidr, port_range: Sequel.pg_range(port_range)) }

vm.associate_with_project(project)
Comment on lines 90 to 93
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The removal of direct firewall rule creation for VMs here is crucial. Confirm that all necessary firewall configurations are now adequately managed at the subnet level and that this change does not inadvertently affect VM security or connectivity.


Strand.create(
Expand Down
16 changes: 14 additions & 2 deletions prog/vnet/subnet_nexus.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

class Prog::Vnet::SubnetNexus < Prog::Base
subject_is :private_subnet
semaphore :destroy, :refresh_keys, :add_new_nic
semaphore :destroy, :refresh_keys, :add_new_nic, :update_firewall_rules

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure the new semaphore :update_firewall_rules is properly handled in all relevant control flows.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a semaphore for disassociate_firewall to manage concurrent disassociation operations.


def self.assemble(project_id, name: nil, location: "hetzner-hel1", ipv6_range: nil, ipv4_range: nil)
def self.assemble(project_id, name: nil, location: "hetzner-hel1", ipv6_range: nil, ipv4_range: nil, allow_only_ssh: false)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verify the default firewall creation logic, especially the condition for SSH-only access, aligns with security policies.

unless (project = Project[project_id])
fail "No existing project"
end
Expand All @@ -20,6 +20,12 @@ def self.assemble(project_id, name: nil, location: "hetzner-hel1", ipv6_range: n
DB.transaction do
ps = PrivateSubnet.create(name: name, location: location, net6: ipv6_range, net4: ipv4_range, state: "waiting") { _1.id = ubid.to_uuid }
ps.associate_with_project(project)
port_range = allow_only_ssh ? 22..22 : 0..65535

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure that the allow_only_ssh parameter is correctly passed and utilized in all relevant contexts to avoid unintended open ports.

fw = Firewall.create_with_id(name: "#{name}-default")
fw.associate_with_project(project)
["0.0.0.0/0", "::/0"].each { |cidr| FirewallRule.create_with_id(firewall_id: fw.id, cidr: cidr, port_range: Sequel.pg_range(port_range)) }
fw.associate_with_private_subnet(ps, apply_firewalls: false)

Strand.create(prog: "Vnet::SubnetNexus", label: "wait") { _1.id = ubid.to_uuid }
end
end
Expand All @@ -44,6 +50,11 @@ def before_run
hop_add_new_nic
end

when_update_firewall_rules_set? do
private_subnet.vms.map(&:incr_update_firewall_rules)
decr_update_firewall_rules
end

if private_subnet.last_rekey_at < Time.now - 60 * 60 * 24
private_subnet.incr_refresh_keys
end
Expand Down Expand Up @@ -128,6 +139,7 @@ def gen_reqid

decr_destroy
strand.children.each { _1.destroy }
private_subnet.firewalls.map { _1.disassociate_from_private_subnet(private_subnet, apply_firewalls: false) }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirm that disassociating firewalls from the private subnet does not inadvertently affect other resources or leave orphaned firewall rules.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check if disassociate_from_private_subnet handles all necessary cleanup to prevent orphaned firewall rules.


if private_subnet.nics.empty?
DB.transaction do
Expand Down
Loading