Files
ubicloud/spec/model/private_subnet_spec.rb
Daniel Farina 4f32e62153 Avoid /api/ path mangling for testing purposes
This makes test and production more similar.  By piggybacking on the
existing rspec metadata used to add other common headers, it's
possible to likewise set the Host header by default.

Most of the bulk of this patch is from running `sed`, but the
interesting hunks are in:

* clover.rb: eliminate a branch to add `/api/` mangling

* routes/api/spec_helper.rb: add the Host header by default

* routes/api/project_spec.rb: remove a special test for the host
   calling convention, as it's now used in every test.

The motivation for this was to make the program easier to validate
with OpenAPI and `committee`, where having two calling conventions
(even for the one test that set `Host`) would need more workarounds.

Of a minor note, the efforts to remove helsinki e.g.
e114669438, generated quite a few
conflicts as norms in what to use in test as an example region change.
I used `sed` through the code base to rewrite identifiers, so I picked
up some extra ones that are not strictly necessary for this patch, but
I left them in.
2024-11-20 14:33:06 -08:00

228 lines
9.1 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: "hetzner-fsn1",
state: "waiting",
name: "ps"
)
}
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")
}
describe "random ip generation" do
it "returns random private ipv4" do
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
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
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
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=>\"hetzner-fsn1\", :state=>\"waiting\", :name=>\"ps\"}>"
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=>\"hetzner-fsn1\", :state=>\"waiting\", :name=>\"ps\"}>"
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
it "returns tag name" do
pr = instance_double(Project, ubid: "prjubid")
expect(private_subnet.hyper_tag_name(pr)).to eq "project/prjubid/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: "hetzner-fsn1", net6: "2001:db8::/64", net4: "10.0.0.0/24")
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) {
prj = Project.create_with_id(name: "test-prj")
prj.associate_with_project(prj)
prj
}
let(:ps1) {
Prog::Vnet::SubnetNexus.assemble(prj.id, name: "test-ps1", location: "hetzner-fsn1").subject
}
it ".connected_subnets" do
ps2 = Prog::Vnet::SubnetNexus.assemble(prj.id, name: "test-ps2", location: "hetzner-fsn1").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: "hetzner-fsn1").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: "hetzner-fsn1").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: "hetzner-fsn1").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