Files
ubicloud/spec/model/private_subnet_spec.rb
Furkan Sahin 12dbeb57a1 Update location references with foreign key in the controlplane
We are basically updating the location references everywhere with a
location id and adding the location relationship to the models to be
able to fetch location names when needed.
This also makes the LocationNameConverter model obsolete, so we are
removing it.

Use model id as value for Sequel::Model in resource creation form

Use id of the location as preselected value in Postgres update form
2025-03-23 15:48:19 +01:00

238 lines
9.7 KiB
Ruby

# frozen_string_literal: true
require_relative "spec_helper"
RSpec.describe PrivateSubnet do
subject(:private_subnet) {
described_class.new(
net6: NetAddr.parse_net("fd1b:9793:dcef:cd0a::/64"),
net4: NetAddr.parse_net("10.9.39.0/26"),
location_id: Location::HETZNER_FSN1_ID,
state: "waiting",
name: "ps",
project_id: Project.create(name: "test").id
)
}
let(:nic) { instance_double(Nic, id: "0a9a166c-e7e7-4447-ab29-7ea442b5bb0e") }
let(:existing_nic) {
instance_double(Nic,
id: "46ca6ded-b056-4723-bd91-612959f52f6f",
private_ipv4: "10.9.39.5/32",
private_ipv6: "fd1b:9793:dcef:cd0a:c::/79")
}
it "disallows VM ubid format as name" do
ps = described_class.new(name: described_class.generate_ubid.to_s)
ps.validate
expect(ps.errors[:name]).to eq ["cannot be exactly 26 numbers/lowercase characters starting with ps to avoid overlap with id format"]
end
it "allows inference endpoint ubid format as name" do
ps = described_class.new(name: InferenceEndpoint.generate_ubid.to_s)
ps.validate
expect(ps.errors[:name]).to be_nil
end
describe "random ip generation" do
it "returns random private ipv4" do
private_subnet
expect(SecureRandom).to receive(:random_number).with(59).and_return(5)
expect(private_subnet.random_private_ipv4.to_s).to eq "10.9.39.9/32"
end
it "returns random private ipv6" do
private_subnet
expect(SecureRandom).to receive(:random_number).with(32766).and_return(5)
expect(private_subnet.random_private_ipv6.to_s).to eq "fd1b:9793:dcef:cd0a:c::/79"
end
it "returns random private ipv4 when ip exists" do
private_subnet
expect(SecureRandom).to receive(:random_number).with(59).and_return(1, 2)
expect(private_subnet).to receive(:nics).and_return([existing_nic]).twice
expect(private_subnet.random_private_ipv4.to_s).to eq "10.9.39.6/32"
end
it "returns random private ipv6 when ip exists" do
private_subnet
expect(SecureRandom).to receive(:random_number).with(32766).and_return(5, 6)
expect(private_subnet).to receive(:nics).and_return([existing_nic]).twice
expect(private_subnet.random_private_ipv6.to_s).to eq "fd1b:9793:dcef:cd0a:e::/79"
end
end
describe ".[]" do
let(:private_subnet) {
subnet = super()
subnet.net6 = subnet.net6.to_s
subnet.net4 = subnet.net4.to_s
subnet.id = described_class.generate_ubid.to_uuid.to_s
subnet.save_changes
}
it "looks up by ubid object" do
expect(described_class[UBID.parse(private_subnet.ubid)].id).to eq private_subnet.id
end
it "looks up by ubid string" do
expect(described_class[private_subnet.ubid].id).to eq private_subnet.id
end
it "looks up by uuid string" do
expect(described_class[private_subnet.id].id).to eq private_subnet.id
end
it "looks up by hash" do
expect(described_class[id: private_subnet.id].id).to eq private_subnet.id
end
it "doesn't raise if given something that looks like a ubid but isn't" do
expect(described_class["a" * 26]).to be_nil
end
end
describe "#inspect" do
it "includes ubid if id is available" do
ubid = described_class.generate_ubid
private_subnet.id = ubid.to_uuid.to_s
expect(private_subnet.inspect).to eq "#<PrivateSubnet[\"#{ubid}\"] @values={:net6=>\"fd1b:9793:dcef:cd0a::/64\", :net4=>\"10.9.39.0/26\", :location_id=>\"10saktg1sprp3mxefj1m3kppq2\", :state=>\"waiting\", :name=>\"ps\", :project_id=>\"#{private_subnet.project.ubid}\"}>"
end
it "does not includes ubid if id is missing" do
expect(private_subnet.inspect).to eq "#<PrivateSubnet @values={:net6=>\"fd1b:9793:dcef:cd0a::/64\", :net4=>\"10.9.39.0/26\", :location_id=>\"10saktg1sprp3mxefj1m3kppq2\", :state=>\"waiting\", :name=>\"ps\", :project_id=>\"#{private_subnet.project.ubid}\"}>"
end
end
describe "uuid to name" do
it "returns the name" do
expect(described_class.ubid_to_name("psetv2ff83xj6h3prt2jwavh0q")).to eq "psetv2ff"
end
end
describe "ui utility methods" do
it "returns path" do
expect(private_subnet.path).to eq "/location/eu-central-h1/private-subnet/ps"
end
end
describe "display_state" do
it "returns available when waiting" do
expect(private_subnet.display_state).to eq "available"
end
it "returns state if not waiting" do
private_subnet.state = "failed"
expect(private_subnet.display_state).to eq "failed"
end
end
describe "destroy" do
it "destroys firewalls private subnets" do
ps = described_class.create_with_id(name: "test-ps", location_id: Location::HETZNER_FSN1_ID, net6: "2001:db8::/64", net4: "10.0.0.0/24", project_id: Project.create(name: "test").id)
fwps = instance_double(FirewallsPrivateSubnets)
expect(FirewallsPrivateSubnets).to receive(:where).with(private_subnet_id: ps.id).and_return(instance_double(Sequel::Dataset, all: [fwps]))
expect(fwps).to receive(:destroy).once
ps.destroy
end
end
describe ".create_tunnels" do
let(:src_nic) {
instance_double(Nic, id: "8ce8a85c-c3d6-86ac-bfdf-022bad69440b")
}
let(:dst_nic) {
instance_double(Nic, id: "6a187cc1-291b-8eac-bdfc-96801fa3118d")
}
it "creates tunnels if doesn't exist" do
expect(IpsecTunnel).to receive(:create).with(src_nic_id: "8ce8a85c-c3d6-86ac-bfdf-022bad69440b", dst_nic_id: "6a187cc1-291b-8eac-bdfc-96801fa3118d").and_return(true)
expect(IpsecTunnel).to receive(:create).with(src_nic_id: "6a187cc1-291b-8eac-bdfc-96801fa3118d", dst_nic_id: "8ce8a85c-c3d6-86ac-bfdf-022bad69440b").and_return(true)
private_subnet.create_tunnels([src_nic, dst_nic], dst_nic)
end
it "skips existing tunnels" do
expect(IpsecTunnel).to receive(:[]).with(src_nic_id: "8ce8a85c-c3d6-86ac-bfdf-022bad69440b", dst_nic_id: "6a187cc1-291b-8eac-bdfc-96801fa3118d").and_return(true)
expect(IpsecTunnel).to receive(:[]).with(src_nic_id: "6a187cc1-291b-8eac-bdfc-96801fa3118d", dst_nic_id: "8ce8a85c-c3d6-86ac-bfdf-022bad69440b").and_return(false)
expect(IpsecTunnel).to receive(:create).with(src_nic_id: "6a187cc1-291b-8eac-bdfc-96801fa3118d", dst_nic_id: "8ce8a85c-c3d6-86ac-bfdf-022bad69440b").and_return(true)
private_subnet.create_tunnels([src_nic, dst_nic], dst_nic)
end
it "skips existing tunnels - 2" do
expect(IpsecTunnel).to receive(:[]).with(src_nic_id: "8ce8a85c-c3d6-86ac-bfdf-022bad69440b", dst_nic_id: "6a187cc1-291b-8eac-bdfc-96801fa3118d").and_return(false)
expect(IpsecTunnel).to receive(:[]).with(src_nic_id: "6a187cc1-291b-8eac-bdfc-96801fa3118d", dst_nic_id: "8ce8a85c-c3d6-86ac-bfdf-022bad69440b").and_return(true)
expect(IpsecTunnel).to receive(:create).with(src_nic_id: "8ce8a85c-c3d6-86ac-bfdf-022bad69440b", dst_nic_id: "6a187cc1-291b-8eac-bdfc-96801fa3118d").and_return(true)
private_subnet.create_tunnels([src_nic, dst_nic], dst_nic)
end
end
describe "connected subnets related methods" do
let(:prj) {
Project.create_with_id(name: "test-prj")
}
let(:ps1) {
Prog::Vnet::SubnetNexus.assemble(prj.id, name: "test-ps1", location_id: Location::HETZNER_FSN1_ID).subject
}
it ".connected_subnets" do
ps2 = Prog::Vnet::SubnetNexus.assemble(prj.id, name: "test-ps2", location_id: Location::HETZNER_FSN1_ID).subject
expect(ps1.connected_subnets).to eq []
ps1.connect_subnet(ps2)
expect(ps1.connected_subnets.map(&:id)).to eq [ps2.id]
expect(ps2.connected_subnets.map(&:id)).to eq [ps1.id]
ps3 = Prog::Vnet::SubnetNexus.assemble(prj.id, name: "test-ps3", location_id: Location::HETZNER_FSN1_ID).subject
ps2.connect_subnet(ps3)
expect(ps1.connected_subnets.map(&:id)).to eq [ps2.id]
expect(ps2.connected_subnets.map(&:id).sort).to eq [ps1.id, ps3.id].sort
expect(ps3.connected_subnets.map(&:id)).to eq [ps2.id]
ps1.disconnect_subnet(ps2)
expect(ps1.connected_subnets.map(&:id)).to eq []
expect(ps2.connected_subnets.map(&:id).sort).to eq [ps3.id].sort
expect(ps3.connected_subnets.map(&:id)).to eq [ps2.id]
end
it ".all_nics" do
ps2 = Prog::Vnet::SubnetNexus.assemble(prj.id, name: "test-ps2", location_id: Location::HETZNER_FSN1_ID).subject
ps1_nic = Prog::Vnet::NicNexus.assemble(ps1.id, name: "test-ps1-nic1").subject
ps2_nic = Prog::Vnet::NicNexus.assemble(ps2.id, name: "test-ps2-nic1").subject
expect(ps1.all_nics.map(&:id)).to eq [ps1_nic.id]
expect(ps1).to receive(:create_tunnels).with([ps2_nic], ps1_nic).and_call_original
ps1.connect_subnet(ps2)
expect(ps1.all_nics.map(&:id).sort).to eq [ps1_nic.id, ps2_nic.id].sort
ps1.disconnect_subnet(ps2)
expect(ps1.all_nics.map(&:id)).to eq [ps1_nic.id]
end
it "disconnect_subnet does not destroy in subnet tunnels" do
ps2 = Prog::Vnet::SubnetNexus.assemble(prj.id, name: "test-ps2", location_id: Location::HETZNER_FSN1_ID).subject
ps1_nic = Prog::Vnet::NicNexus.assemble(ps1.id, name: "test-ps1-nic1").subject
ps1_nic2 = Prog::Vnet::NicNexus.assemble(ps1.id, name: "test-ps1-nic2").subject
ps1.create_tunnels([ps1_nic], ps1_nic2)
ps2_nic = Prog::Vnet::NicNexus.assemble(ps2.id, name: "test-ps2-nic1").subject
ps1.connect_subnet(ps2)
expect(ps1.find_all_connected_nics.map(&:id).sort).to eq [ps1_nic.id, ps1_nic2.id, ps2_nic.id].sort
expect(IpsecTunnel.count).to eq 6
ps1.disconnect_subnet(ps2)
expect(ps1.find_all_connected_nics.map(&:id).sort).to eq [ps1_nic.id, ps1_nic2.id].sort
tunnels = ps1_nic.src_ipsec_tunnels + ps1_nic.dst_ipsec_tunnels
expect(IpsecTunnel.all.map(&:id).sort).to eq(tunnels.map(&:id).sort)
expect(IpsecTunnel.count).to eq 2
end
end
end