It hasn't been necessary to use create_with_id since
ebc79622df
, in December 2024.
I have plans to introduce:
```ruby
def create_with_id(id, values)
obj = new(values)
obj.id = id
obj.save_changes
end
```
This will make it easier to use the same id when creating
multiple objects. The first step is removing the existing
uses of create_with_id.
493 lines
21 KiB
Ruby
493 lines
21 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
RSpec.describe Prog::Vnet::SubnetNexus do
|
|
subject(:nx) {
|
|
described_class.new(st)
|
|
}
|
|
|
|
let(:st) { Strand.new }
|
|
let(:prj) { Project.create(name: "default") }
|
|
let(:ps) {
|
|
PrivateSubnet.create(name: "ps", location_id: Location::HETZNER_FSN1_ID, net6: "fd10:9b0b:6b4b:8fbb::/64",
|
|
net4: "1.1.1.0/26", state: "waiting", project_id: prj.id)
|
|
}
|
|
|
|
let(:ps2) {
|
|
PrivateSubnet.create(name: "ps2", location_id: Location::HETZNER_FSN1_ID, net6: "fd10:9b0b:6b4b:8fcc::/64",
|
|
net4: "1.1.1.128/26", state: "waiting", project_id: prj.id)
|
|
}
|
|
|
|
before do
|
|
nx.instance_variable_set(:@private_subnet, ps)
|
|
end
|
|
|
|
describe ".assemble" do
|
|
it "fails if project doesn't exist" do
|
|
expect {
|
|
described_class.assemble(nil)
|
|
}.to raise_error RuntimeError, "No existing project"
|
|
end
|
|
|
|
it "fails if location doesn't exist" do
|
|
expect {
|
|
described_class.assemble(prj.id, location_id: nil)
|
|
}.to raise_error RuntimeError, "No existing location"
|
|
end
|
|
|
|
it "uses ipv6_addr if passed and creates entities" do
|
|
expect(described_class).to receive(:random_private_ipv4).and_return("10.0.0.0/26")
|
|
ps = described_class.assemble(
|
|
prj.id,
|
|
name: "default-ps",
|
|
location_id: Location::HETZNER_FSN1_ID,
|
|
ipv6_range: "fd10:9b0b:6b4b:8fbb::/64"
|
|
)
|
|
|
|
expect(ps.subject.net6.to_s).to eq("fd10:9b0b:6b4b:8fbb::/64")
|
|
end
|
|
|
|
it "uses ipv4_addr if passed and creates entities" do
|
|
expect(described_class).to receive(:random_private_ipv6).and_return("fd10:9b0b:6b4b:8fbb::/64")
|
|
ps = described_class.assemble(
|
|
prj.id,
|
|
name: "default-ps",
|
|
location_id: Location::HETZNER_FSN1_ID,
|
|
ipv4_range: "10.0.0.0/26"
|
|
)
|
|
|
|
expect(ps.subject.net4.to_s).to eq("10.0.0.0/26")
|
|
end
|
|
|
|
it "uses firewall if provided" do
|
|
fw = Firewall.create(name: "default-firewall", location_id: Location::HETZNER_FSN1_ID, project_id: prj.id)
|
|
ps = described_class.assemble(prj.id, firewall_id: fw.id)
|
|
expect(ps.subject.firewalls.count).to eq(1)
|
|
expect(ps.subject.firewalls.first).to eq(fw)
|
|
end
|
|
|
|
it "fails if provided firewall does not exist" do
|
|
expect {
|
|
described_class.assemble(prj.id, firewall_id: "550e8400-e29b-41d4-a716-446655440000")
|
|
}.to raise_error RuntimeError, "Firewall with id 550e8400-e29b-41d4-a716-446655440000 and location hetzner-fsn1 does not exist"
|
|
end
|
|
|
|
it "fails if firewall is not in the project" do
|
|
fw = Firewall.create(name: "default-firewall", location_id: Location::HETZNER_FSN1_ID, project_id: Project.create(name: "t2").id)
|
|
expect {
|
|
described_class.assemble(prj.id, firewall_id: fw.id)
|
|
}.to raise_error RuntimeError, "Firewall with id #{fw.id} and location hetzner-fsn1 does not exist"
|
|
end
|
|
|
|
it "fails if both allow_only_ssh and firewall_id are specified" do
|
|
fw = Firewall.create(name: "default-firewall", location_id: Location::HETZNER_FSN1_ID, project_id: prj.id)
|
|
expect {
|
|
described_class.assemble(prj.id, firewall_id: fw.id, allow_only_ssh: true)
|
|
}.to raise_error RuntimeError, "Cannot specify both allow_only_ssh and firewall_id"
|
|
end
|
|
end
|
|
|
|
describe ".gen_spi" do
|
|
it "generates a random spi" do
|
|
expect(SecureRandom).to receive(:bytes).with(4).and_return("e3af3a04")
|
|
expect(nx.gen_spi).to eq("0x6533616633613034")
|
|
end
|
|
end
|
|
|
|
describe ".gen_reqid" do
|
|
it "generates a random reqid" do
|
|
expect(SecureRandom).to receive(:random_number).with(100000).and_return(10)
|
|
expect(nx.gen_reqid).to eq(11)
|
|
end
|
|
end
|
|
|
|
describe ".gen_encryption_key" do
|
|
it "generates a random encryption key" do
|
|
expect(SecureRandom).to receive(:bytes).with(36).and_return("e3af3a04")
|
|
expect(nx.gen_encryption_key).to eq("0x6533616633613034")
|
|
end
|
|
end
|
|
|
|
describe ".nics_to_rekey" do
|
|
it "returns nics that need rekeying" do
|
|
nic1 = Prog::Vnet::NicNexus.assemble(ps.id, name: "a").subject
|
|
nic2 = Prog::Vnet::NicNexus.assemble(ps.id, name: "b").subject
|
|
expect(nx.nics_to_rekey).to eq([])
|
|
nic1.strand.update(label: "wait")
|
|
expect(nx.nics_to_rekey.map(&:name)).to eq(["a"])
|
|
nic2.strand.update(label: "wait_setup")
|
|
expect(nx.nics_to_rekey.map(&:name).sort).to eq(["a", "b"])
|
|
end
|
|
end
|
|
|
|
describe "#before_run" do
|
|
it "hops to destroy if when_destroy_set?" do
|
|
expect(nx).to receive(:when_destroy_set?).and_yield
|
|
expect { nx.before_run }.to hop("destroy")
|
|
end
|
|
|
|
it "hops to destroy if when_destroy_set? from wait_fw_rules" do
|
|
expect(nx).to receive(:when_destroy_set?).and_yield
|
|
expect(nx.strand).to receive(:label).and_return("wait_fw_rules").at_least(:once)
|
|
expect { nx.before_run }.to hop("destroy")
|
|
end
|
|
|
|
it "does not hop to destroy if strand is destroy" do
|
|
expect(nx).to receive(:when_destroy_set?).and_yield
|
|
expect(nx.strand).to receive(:label).and_return("destroy")
|
|
expect { nx.before_run }.not_to hop("destroy")
|
|
end
|
|
end
|
|
|
|
describe "#start" do
|
|
it "creates a vpc if location is aws and starts to wait for it" do
|
|
loc = Location.create(name: "aws-us-west-2", provider: "aws", project_id: prj.id, display_name: "aws-us-west-2", ui_name: "AWS US East 1", visible: true)
|
|
expect(ps).to receive(:location).and_return(loc).at_least(:once)
|
|
expect(nx).to receive(:bud).with(Prog::Aws::Vpc, {"subject_id" => ps.id}, :create_vpc)
|
|
expect { nx.start }.to hop("wait_vpc_created")
|
|
end
|
|
|
|
it "does not create the PrivateSubnetAwsResource if it already exists" do
|
|
loc = Location.create(name: "aws-us-west-2", provider: "aws", project_id: prj.id, display_name: "aws-us-west-2", ui_name: "AWS US East 1", visible: true)
|
|
expect(ps).to receive(:location).and_return(loc).at_least(:once)
|
|
expect(ps).to receive(:private_subnet_aws_resource).and_return(instance_double(PrivateSubnetAwsResource, id: "123")).at_least(:once)
|
|
expect(nx).to receive(:bud).with(Prog::Aws::Vpc, {"subject_id" => ps.id}, :create_vpc)
|
|
expect { nx.start }.to hop("wait_vpc_created")
|
|
expect(PrivateSubnetAwsResource.count).to eq(0)
|
|
end
|
|
|
|
it "hops to wait if location is not aws" do
|
|
expect { nx.start }.to hop("wait")
|
|
end
|
|
end
|
|
|
|
describe "#wait_vpc_created" do
|
|
it "reaps and hops to wait if leaf" do
|
|
st.update(prog: "Vnet::SubnetNexus", label: "wait_vpc_created", stack: [{}])
|
|
expect { nx.wait_vpc_created }.to hop("wait")
|
|
end
|
|
|
|
it "naps if not leaf" do
|
|
st.update(prog: "Vnet::SubnetNexus", label: "wait_vpc_created", stack: [{}])
|
|
Strand.create(parent_id: st.id, prog: "Aws::Vpc", label: "create_vpc", stack: [{}], lease: Time.now + 10)
|
|
# Cover case where reap without reaper argument has results in reapable children
|
|
Strand.create(parent_id: st.id, prog: "Aws::Vpc", label: "create_vpc", stack: [{}]).this.update(exitval: '"subnet created"')
|
|
expect { nx.wait_vpc_created }.to nap(2)
|
|
end
|
|
end
|
|
|
|
describe "#wait" do
|
|
it "naps if location is aws" do
|
|
expect(ps.location).to receive(:aws?).and_return(true)
|
|
expect(ps).to receive(:semaphores).and_return([])
|
|
expect { nx.wait }.to nap(60 * 60 * 24 * 365)
|
|
end
|
|
|
|
it "hops to refresh_keys if when_refresh_keys_set?" do
|
|
expect(nx).to receive(:when_refresh_keys_set?).and_yield
|
|
expect(ps).to receive(:update).with(state: "refreshing_keys").and_return(true)
|
|
expect { nx.wait }.to hop("refresh_keys")
|
|
end
|
|
|
|
it "hops to add_new_nic if when_add_new_nic_set?" do
|
|
expect(nx).to receive(:when_add_new_nic_set?).and_yield
|
|
expect(ps).to receive(:update).with(state: "adding_new_nic").and_return(true)
|
|
expect { nx.wait }.to hop("add_new_nic")
|
|
end
|
|
|
|
it "increments refresh_keys if it passed more than a day" do
|
|
expect(ps).to receive(:last_rekey_at).and_return(Time.now - 60 * 60 * 24 - 1)
|
|
expect(ps).to receive(:incr_refresh_keys).and_return(true)
|
|
expect { nx.wait }.to nap(10 * 60)
|
|
end
|
|
|
|
it "triggers update_firewall_rules if when_update_firewall_rules_set?" do
|
|
expect(nx).to receive(:when_update_firewall_rules_set?).and_yield
|
|
expect(ps).to receive(:vms).and_return([instance_double(Vm, id: "vm1")]).at_least(:once)
|
|
expect(ps.vms.first).to receive(:incr_update_firewall_rules).and_return(true)
|
|
expect(nx).to receive(:decr_update_firewall_rules).and_return(true)
|
|
expect { nx.wait }.to nap(10 * 60)
|
|
end
|
|
|
|
it "naps if nothing to do" do
|
|
expect { nx.wait }.to nap(10 * 60)
|
|
end
|
|
end
|
|
|
|
describe "#add_new_nic" do
|
|
it "adds new nics and creates tunnels" do
|
|
st = instance_double(Strand, label: "wait_setup")
|
|
nic_to_add = instance_double(Nic, id: "57afa8a7-2357-4012-9632-07fbe13a3133", rekey_payload: {}, strand: st, lock_set?: false)
|
|
st = instance_double(Strand, label: "wait")
|
|
added_nic = instance_double(Nic, id: "8ce8a85c-c3d6-86ac-bfdf-022bad69440b", rekey_payload: {}, strand: st, lock_set?: false)
|
|
nics_to_rekey = [added_nic, nic_to_add]
|
|
expect(nx).to receive(:decr_add_new_nic)
|
|
expect(nic_to_add).to receive(:incr_lock)
|
|
expect(added_nic).to receive(:incr_lock)
|
|
expect(nic_to_add).to receive(:incr_start_rekey)
|
|
expect(added_nic).to receive(:incr_start_rekey)
|
|
expect(nx).to receive(:nics_to_rekey).and_return(nics_to_rekey)
|
|
expect(nx).to receive(:gen_spi).and_return("0xe3af3a04").at_least(:once)
|
|
expect(nx).to receive(:gen_reqid).and_return(86879).at_least(:once)
|
|
expect(nx).to receive(:gen_encryption_key).and_return("0x0a0b0c0d0e0f10111213141516171819").at_least(:once)
|
|
expect(nx.private_subnet).to receive(:create_tunnels).and_return(true).at_least(:once)
|
|
expect(added_nic).to receive(:update).with(encryption_key: "0x0a0b0c0d0e0f10111213141516171819", rekey_payload:
|
|
{
|
|
spi4: "0xe3af3a04",
|
|
spi6: "0xe3af3a04",
|
|
reqid: 86879
|
|
}).and_return(true)
|
|
expect(nic_to_add).to receive(:update).with(encryption_key: "0x0a0b0c0d0e0f10111213141516171819", rekey_payload:
|
|
{
|
|
spi4: "0xe3af3a04",
|
|
spi6: "0xe3af3a04",
|
|
reqid: 86879
|
|
}).and_return(true)
|
|
expect { nx.add_new_nic }.to hop("wait_inbound_setup")
|
|
end
|
|
|
|
it "naps if the nics are locked" do
|
|
st = instance_double(Strand, label: "wait_setup")
|
|
nic_to_add = instance_double(Nic, id: "57afa8a7-2357-4012-9632-07fbe13a3133", rekey_payload: {}, strand: st, lock_set?: false)
|
|
st = instance_double(Strand, label: "wait")
|
|
added_nic = instance_double(Nic, id: "8ce8a85c-c3d6-86ac-bfdf-022bad69440b", rekey_payload: {}, strand: st, lock_set?: false)
|
|
nics_to_rekey = [added_nic, nic_to_add]
|
|
expect(added_nic).to receive(:lock_set?).and_return(true)
|
|
expect(nx).to receive(:nics_to_rekey).and_return(nics_to_rekey)
|
|
expect { nx.add_new_nic }.to nap(10)
|
|
end
|
|
end
|
|
|
|
describe "#refresh_keys" do
|
|
let(:nic) {
|
|
Prog::Vnet::NicNexus.assemble(ps.id, name: "a").update(label: "wait").subject
|
|
}
|
|
let(:nx) {
|
|
described_class.new(Strand.create(prog: "Vnet::SubnetNexus", label: "refresh_keys", id: ps.id))
|
|
}
|
|
|
|
it "refreshes keys and hops to wait_refresh_keys" do
|
|
expect(nx).to receive(:gen_spi).and_return("0xe3af3a04").at_least(:once)
|
|
expect(nx).to receive(:gen_reqid).and_return(86879)
|
|
expect(nx).to receive(:gen_encryption_key).and_return("0x0a0b0c0d0e0f10111213141516171819")
|
|
expect(nic.start_rekey_set?).to be false
|
|
expect(nic.lock_set?).to be false
|
|
expect { nx.refresh_keys }.to hop("wait_inbound_setup")
|
|
nic.refresh
|
|
expect(nic.encryption_key).to eq "0x0a0b0c0d0e0f10111213141516171819"
|
|
expect(nic.rekey_payload).to eq("spi4" => "0xe3af3a04", "spi6" => "0xe3af3a04", "reqid" => 86879)
|
|
expect(nic.start_rekey_set?).to be true
|
|
expect(nic.lock_set?).to be true
|
|
end
|
|
|
|
it "naps if the nics are locked" do
|
|
nic.incr_lock
|
|
expect { nx.refresh_keys }.to nap(10)
|
|
end
|
|
end
|
|
|
|
describe "#wait_inbound_setup" do
|
|
let(:nic) {
|
|
Prog::Vnet::NicNexus.assemble(ps.id, name: "a").subject.update(rekey_payload: {})
|
|
}
|
|
let(:nx) {
|
|
described_class.new(Strand.create(prog: "Vnet::SubnetNexus", label: "wait_inbound_setup", id: ps.id))
|
|
}
|
|
|
|
it "naps 5 if state creation is ongoing" do
|
|
nic
|
|
expect { nx.wait_inbound_setup }.to nap(5)
|
|
end
|
|
|
|
it "hops to wait_policy_updated if state creation is done" do
|
|
nic.strand.update(label: "wait_rekey_outbound_trigger")
|
|
expect(nic.trigger_outbound_update_set?).to be false
|
|
expect { nx.wait_inbound_setup }.to hop("wait_outbound_setup")
|
|
nic.refresh
|
|
expect(nic.trigger_outbound_update_set?).to be true
|
|
end
|
|
end
|
|
|
|
describe "#wait_outbound_setup" do
|
|
let(:nic) {
|
|
Prog::Vnet::NicNexus.assemble(ps.id, name: "a").subject.update(rekey_payload: {})
|
|
}
|
|
let(:nx) {
|
|
described_class.new(Strand.create(prog: "Vnet::SubnetNexus", label: "wait_outbound_setup", id: ps.id))
|
|
}
|
|
|
|
it "donates if policy update is ongoing" do
|
|
nic
|
|
expect { nx.wait_outbound_setup }.to nap(5)
|
|
end
|
|
|
|
it "hops to wait_state_dropped if policy update is done" do
|
|
nic.strand.update(label: "wait_rekey_old_state_drop_trigger")
|
|
expect(nic.old_state_drop_trigger_set?).to be false
|
|
expect { nx.wait_outbound_setup }.to hop("wait_old_state_drop")
|
|
nic.refresh
|
|
expect(nic.old_state_drop_trigger_set?).to be true
|
|
end
|
|
end
|
|
|
|
describe "#wait_old_state_drop" do
|
|
let(:nic) {
|
|
Prog::Vnet::NicNexus.assemble(ps.id, name: "a").subject.update(rekey_payload: {})
|
|
}
|
|
let(:nx) {
|
|
described_class.new(Strand.create(prog: "Vnet::SubnetNexus", label: "wait_old_state_drop", id: ps.id))
|
|
}
|
|
|
|
it "donates if policy update is ongoing" do
|
|
nic
|
|
expect { nx.wait_old_state_drop }.to nap(5)
|
|
end
|
|
|
|
it "hops to wait if all is done" do
|
|
nic.strand.update(label: "wait")
|
|
nic.incr_lock
|
|
ps.update(last_rekey_at: Time.now - 100)
|
|
expect { nx.wait_old_state_drop }.to hop("wait")
|
|
ps.refresh
|
|
expect(ps.state).to eq "waiting"
|
|
expect(ps.last_rekey_at > Time.now - 10).to be true
|
|
nic.refresh
|
|
expect(nic.encryption_key).to be_nil
|
|
expect(nic.rekey_payload).to be_nil
|
|
expect(nic.lock_set?).to be false
|
|
end
|
|
end
|
|
|
|
describe ".random_private_ipv4" do
|
|
it "returns a random private ipv4 range" do
|
|
expect(described_class.random_private_ipv4(Location[name: "hetzner-fsn1"], prj)).to be_a NetAddr::IPv4Net
|
|
end
|
|
|
|
it "finds a new subnet if the one it found is taken" do
|
|
expect(PrivateSubnet).to receive(:random_subnet).and_return("10.0.0.0/8").at_least(:once)
|
|
project = Project.create(name: "test-project")
|
|
described_class.assemble(project.id, location_id: Location::HETZNER_FSN1_ID, name: "test-subnet", ipv4_range: "10.0.0.128/26")
|
|
allow(SecureRandom).to receive(:random_number).with(2**(26 - 8) - 1).and_return(1, 2)
|
|
expect(described_class.random_private_ipv4(Location[name: "hetzner-fsn1"], project).to_s).to eq("10.0.0.192/26")
|
|
end
|
|
|
|
it "finds a new subnet if the one it found is banned" do
|
|
expect(PrivateSubnet).to receive(:random_subnet).and_return("172.16.0.0/16", "10.0.0.0/8")
|
|
project = Project.create(name: "test-project")
|
|
allow(SecureRandom).to receive(:random_number).with(2**(26 - 16) - 1).and_return(1)
|
|
allow(SecureRandom).to receive(:random_number).with(2**(26 - 8) - 1).and_return(1)
|
|
expect(described_class.random_private_ipv4(Location[name: "hetzner-fsn1"], project).to_s).to eq("10.0.0.128/26")
|
|
end
|
|
|
|
it "finds a new subnet if the initial range is smaller than the requested cidr range" do
|
|
expect(PrivateSubnet).to receive(:random_subnet).and_return("172.16.0.0/16", "10.0.0.0/8")
|
|
project = Project.create(name: "test-project")
|
|
expect(SecureRandom).not_to receive(:random_number).with(2**(16 - 16) - 1)
|
|
allow(SecureRandom).to receive(:random_number).with(2**(16 - 8) - 1).and_return(15)
|
|
expect(described_class.random_private_ipv4(Location[name: "hetzner-fsn1"], project, 16).to_s).to eq("10.16.0.0/16")
|
|
end
|
|
|
|
it "raises an error when invalid CIDR is given" do
|
|
project = Project.create(name: "test-project")
|
|
expect { described_class.random_private_ipv4(Location[name: "hetzner-fsn1"], project, 33) }.to raise_error(ArgumentError)
|
|
end
|
|
end
|
|
|
|
describe ".random_private_ipv6" do
|
|
it "returns a random private ipv6 range" do
|
|
expect(described_class.random_private_ipv6(Location[name: "hetzner-fsn1"], prj)).to be_a NetAddr::IPv6Net
|
|
end
|
|
|
|
it "finds a new subnet if the one it found is taken" do
|
|
project = Project.create(name: "test-project")
|
|
described_class.assemble(project.id, location_id: Location::HETZNER_FSN1_ID, name: "test-subnet", ipv6_range: "fd61:6161:6161:6161::/64")
|
|
expect(SecureRandom).to receive(:bytes).with(7).and_return("a" * 7, "b" * 7)
|
|
expect(described_class.random_private_ipv6(Location[name: "hetzner-fsn1"], project).to_s).to eq("fd62:6262:6262:6262::/64")
|
|
end
|
|
end
|
|
|
|
describe "#destroy" do
|
|
let(:nic) {
|
|
instance_double(Nic, vm_id: nil)
|
|
}
|
|
|
|
it "extends deadline if a vm prevents destroy" do
|
|
vm = Vm.new(family: "standard", cores: 1, name: "dummy-vm", location_id: Location::HETZNER_FSN1_ID).tap {
|
|
it.id = "788525ed-d6f0-4937-a844-323d4fd91946"
|
|
}
|
|
expect(ps).to receive(:nics).and_return([nic]).twice
|
|
expect(nic).to receive(:vm_id).and_return("vm-id")
|
|
expect(nic).to receive(:vm).and_return(vm)
|
|
expect(vm).to receive(:prevent_destroy_set?).and_return(true)
|
|
expect(nx).to receive(:register_deadline).with(nil, 10 * 60, allow_extension: true)
|
|
|
|
expect { nx.destroy }.to nap(5)
|
|
end
|
|
|
|
it "fails if there are active resources" do
|
|
expect(ps).to receive(:nics).and_return([nic]).twice
|
|
expect(nic).to receive(:vm_id).and_return("vm-id")
|
|
expect(nic).to receive(:vm).and_return(nil)
|
|
expect(Clog).to receive(:emit).with("Cannot destroy subnet with active nics, first clean up the attached resources").and_call_original
|
|
|
|
expect { nx.destroy }.to nap(5)
|
|
end
|
|
|
|
it "hops to wait_aws_vpc_destroyed if location is aws" do
|
|
expect(ps).to receive(:location).and_return(Location.create(name: "aws-us-west-2", provider: "aws", project_id: prj.id, display_name: "aws-us-west-2", ui_name: "AWS US East 1", visible: true))
|
|
expect(nx).to receive(:private_subnet).and_return(ps).at_least(:once)
|
|
expect(ps).to receive(:nics).and_return([nic]).at_least(:once)
|
|
expect(nic).to receive(:incr_destroy)
|
|
expect(nx).to receive(:bud).with(Prog::Aws::Vpc, {"subject_id" => ps.id}, :destroy)
|
|
expect { nx.destroy }.to hop("wait_aws_vpc_destroyed")
|
|
end
|
|
|
|
it "increments the destroy semaphore of nics" do
|
|
expect(ps).to receive(:nics).and_return([nic]).at_least(:once)
|
|
expect(nic).to receive(:incr_destroy).and_return(true)
|
|
expect { nx.destroy }.to nap(1)
|
|
end
|
|
|
|
it "deletes and pops if nics are destroyed" do
|
|
expect(ps).to receive(:destroy).and_return(true)
|
|
expect(ps).to receive(:nics).and_return([]).at_least(:once)
|
|
expect { nx.destroy }.to exit({"msg" => "subnet destroyed"})
|
|
end
|
|
|
|
it "disconnects all subnets" do
|
|
prj = Project.create(name: "test-project")
|
|
ps1 = described_class.assemble(prj.id, name: "ps1").subject
|
|
ps2 = described_class.assemble(prj.id, name: "ps2").subject
|
|
ps1.connect_subnet(ps2)
|
|
expect(ps1.connected_subnets.map(&:id)).to eq [ps2.id]
|
|
expect(ps2.connected_subnets.map(&:id)).to eq [ps1.id]
|
|
|
|
expect(nx).to receive(:private_subnet).and_return(ps1).at_least(:once)
|
|
expect(ps1).to receive(:disconnect_subnet).with(ps2).and_call_original
|
|
expect { nx.destroy }.to exit({"msg" => "subnet destroyed"})
|
|
end
|
|
end
|
|
|
|
describe "#wait_aws_vpc_destroyed" do
|
|
it "naps if there are nics" do
|
|
st.update(prog: "Vnet::SubnetNexus", label: "wait_aws_vpc_destroyed", stack: [{}])
|
|
expect(nx).to receive(:private_subnet).and_return(ps).at_least(:once)
|
|
expect(ps).to receive(:nics).and_return([1]).at_least(:once)
|
|
expect { nx.wait_aws_vpc_destroyed }.to nap(5)
|
|
end
|
|
|
|
it "deletes the vpc and pops if leaf" do
|
|
st.update(prog: "Vnet::SubnetNexus", label: "wait_aws_vpc_destroyed", stack: [{}])
|
|
expect(nx).to receive(:private_subnet).and_return(ps).at_least(:once)
|
|
expect(ps).to receive(:destroy).and_return(true)
|
|
expect(ps).to receive(:private_subnet_aws_resource).and_return(instance_double(PrivateSubnetAwsResource, id: "123", destroy: true))
|
|
expect { nx.wait_aws_vpc_destroyed }.to exit({"msg" => "vpc destroyed"})
|
|
end
|
|
|
|
it "naps if not leaf" do
|
|
st.update(prog: "Vnet::SubnetNexus", label: "wait_aws_vpc_destroyed", stack: [{}])
|
|
Strand.create(parent_id: st.id, prog: "Aws::Vpc", label: "destroy", stack: [{}])
|
|
expect { nx.wait_aws_vpc_destroyed }.to nap(10)
|
|
end
|
|
end
|
|
end
|