Files
ubicloud/spec/prog/test/connected_subnets_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

318 lines
19 KiB
Ruby

# frozen_string_literal: true
require_relative "../../model/spec_helper"
require "netaddr"
RSpec.describe Prog::Test::ConnectedSubnets do
subject(:connected_subnets_test) {
described_class.new(Strand.new(prog: "Test::ConnectedSubnets"))
}
let(:ps_multiple) {
Prog::Vnet::SubnetNexus.assemble(project.id, name: "ps-multiple", location_id: Location::HETZNER_FSN1_ID).subject
}
let(:ps_single) {
Prog::Vnet::SubnetNexus.assemble(project.id, name: "ps-single", location_id: Location::HETZNER_FSN1_ID).subject
}
let(:project) {
Project.create_with_id(name: "project1")
}
let(:sshable) {
instance_double(Sshable)
}
before do
ps_multiple
ps_single
allow(connected_subnets_test).to receive(:frame).and_return({"subnet_id_multiple" => ps_multiple.id, "subnet_id_single" => ps_single.id}).at_least(:once)
end
describe "#start" do
it "updates firewall rules for both subnets, connects them, updates the stack, and naps" do
expect(connected_subnets_test).to receive(:update_firewall_rules).with(ps_single, ps_multiple, config: :perform_tests_public_blocked)
expect(connected_subnets_test).to receive(:update_firewall_rules).with(ps_multiple, ps_single, config: :perform_tests_public_blocked)
expect(connected_subnets_test).to receive(:ps_multiple).and_return(ps_multiple).at_least(:once)
vm1 = instance_double(Vm, id: "1ae5f1c2-2f48-4eac-84e3-cfe35b2a9865", sshable: sshable, ephemeral_net4: NetAddr::IPv4Net.parse("0.0.0.0"), boot_image: "debian-12")
vm2 = instance_double(Vm, id: "3f2f4ed0-88b1-49c6-b66a-0d2ed4910ad0", sshable: sshable, boot_image: "almalinux-9")
expect(ps_multiple).to receive(:vms).and_return([vm1, vm2]).at_least(:once)
expect(sshable).to receive(:cmd).with("sudo yum install -y nc")
expect(sshable).to receive(:cmd).with("sudo apt-get update && sudo apt-get install -y netcat-openbsd")
expect(sshable).to receive(:cmd).with("echo '[Unit]
Description=A lightweight port 8080 listener
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/nc -l 8080
' | sudo tee /etc/systemd/system/listening_ipv4.service > /dev/null")
expect(sshable).to receive(:cmd).with("echo '[Unit]
Description=A lightweight port 8080 listener
After=network.target
[Service]
Type=simple
ExecStart=nc -l 8080 -6
' | sudo tee /etc/systemd/system/listening_ipv6.service > /dev/null")
expect(sshable).to receive(:cmd).with("sudo systemctl daemon-reload")
expect(sshable).to receive(:cmd).with("sudo systemctl enable listening_ipv4.service")
expect(sshable).to receive(:cmd).with("sudo systemctl enable listening_ipv6.service")
expect(connected_subnets_test).to receive(:update_stack).with({"vm_to_be_connected_id" => vm1.id})
expect { connected_subnets_test.start }.to nap(5)
end
it "hops to perform_tests_public_blocked" do
expect(connected_subnets_test).to receive(:frame).and_return({"vm_to_be_connected_id" => true})
ps_multiple.strand.update(label: "wait")
ps_single.strand.update(label: "wait")
Semaphore.all.map(&:destroy)
expect { connected_subnets_test.start }.to hop("perform_tests_public_blocked")
end
end
describe "#perform_tests_public_blocked" do
it "tests connection between the two subnets and fails" do
expect(connected_subnets_test).to receive(:ps_multiple).and_return(ps_multiple).at_least(:once)
expect(connected_subnets_test).to receive(:ps_single).and_return(ps_single).at_least(:once)
vm1 = instance_double(Vm, sshable: sshable, ephemeral_net4: NetAddr::IPv4Net.parse("0.0.0.0"))
vm2 = instance_double(Vm, sshable: sshable)
expect(ps_multiple).to receive(:vms).and_return([vm1, vm2]).at_least(:once)
expect(ps_single).to receive(:vms).and_return([vm2]).at_least(:once)
expect(sshable).to receive(:cmd).with("ping -c 2 google.com").at_least(:once)
expect(sshable).to receive(:cmd).with("sudo systemctl start listening_ipv4.service")
expect(sshable).to receive(:cmd).with("sudo systemctl stop listening_ipv6.service")
expect(connected_subnets_test).to receive(:test_connection).with(vm1.ephemeral_net4, vm2, should_fail: true, ipv4: true)
expect { connected_subnets_test.perform_tests_public_blocked }.to hop("perform_tests_private_ipv4")
end
end
describe "#perform_tests_private_ipv4" do
it "updates firewall rules, updates the stack, and naps" do
expect(connected_subnets_test).to receive(:ps_multiple).and_return(ps_multiple).at_least(:once)
expect(connected_subnets_test).to receive(:update_firewall_rules).with(ps_multiple, ps_single, config: :perform_connected_private_ipv4)
expect(connected_subnets_test).to receive(:update_stack).with({"firewalls" => "connected_private_ipv4"})
expect(ps_multiple).to receive(:update_firewall_rules_set?).and_return(true)
expect { connected_subnets_test.perform_tests_private_ipv4 }.to nap(5)
end
it "tests connection between the two subnets and hops to perform_tests_private_ipv6" do
expect(connected_subnets_test).to receive(:frame).and_return({"firewalls" => "connected_private_ipv4"})
expect(connected_subnets_test).to receive(:ps_multiple).and_return(ps_multiple).at_least(:once)
vm1 = instance_double(Vm, sshable: sshable, nics: [instance_double(Nic, private_ipv4: NetAddr::IPv4Net.parse("10.0.0.0/26"))])
vm2 = instance_double(Vm)
expect(connected_subnets_test).to receive(:vm_to_be_connected).and_return(vm1).at_least(:once)
expect(connected_subnets_test).to receive(:vm_to_connect).and_return(vm2).at_least(:once)
expect(connected_subnets_test).to receive(:vm_to_connect_outside).and_return(vm2).at_least(:once)
expect(sshable).to receive(:cmd).with("sudo systemctl start listening_ipv4.service")
expect(sshable).to receive(:cmd).with("sudo systemctl stop listening_ipv6.service")
expect(connected_subnets_test).to receive(:test_connection).with(vm1.nics.first.private_ipv4.nth(0).to_s, vm2, should_fail: false, ipv4: true)
expect(connected_subnets_test).to receive(:test_connection).with(vm1.nics.first.private_ipv4.nth(0).to_s, vm2, should_fail: true, ipv4: true)
expect { connected_subnets_test.perform_tests_private_ipv4 }.to hop("perform_tests_private_ipv6")
end
end
describe "#perform_tests_private_ipv6" do
it "updates firewall rules, updates the stack, and naps" do
expect(connected_subnets_test).to receive(:ps_multiple).and_return(ps_multiple).at_least(:once)
expect(connected_subnets_test).to receive(:update_firewall_rules).with(ps_multiple, ps_single, config: :perform_connected_private_ipv6)
expect(connected_subnets_test).to receive(:update_stack).with({"firewalls" => "connected_private_ipv6"})
expect(ps_multiple).to receive(:update_firewall_rules_set?).and_return(true)
expect { connected_subnets_test.perform_tests_private_ipv6 }.to nap(5)
end
it "tests connection between the two subnets and hops to perform_blocked_private_ipv4" do
expect(connected_subnets_test).to receive(:frame).and_return({"firewalls" => "connected_private_ipv6"})
vm1 = instance_double(Vm, sshable: sshable, nics: [instance_double(Nic, private_ipv6: NetAddr::IPv6Net.parse("2001:db8::/64"))], private_ipv6: NetAddr::IPv6.parse("2001:db8::2"))
vm2 = instance_double(Vm)
expect(connected_subnets_test).to receive(:vm_to_be_connected).and_return(vm1).at_least(:once)
expect(connected_subnets_test).to receive(:vm_to_connect).and_return(vm2).at_least(:once)
expect(connected_subnets_test).to receive(:vm_to_connect_outside).and_return(vm2).at_least(:once)
expect(sshable).to receive(:cmd).with("sudo systemctl start listening_ipv6.service")
expect(sshable).to receive(:cmd).with("sudo systemctl stop listening_ipv4.service")
expect(connected_subnets_test).to receive(:test_connection).with(vm1.nics.first.private_ipv6.nth(2).to_s, vm2, should_fail: false, ipv4: false)
expect(connected_subnets_test).to receive(:test_connection).with(vm1.nics.first.private_ipv6.nth(2).to_s, vm2, should_fail: true, ipv4: false)
expect { connected_subnets_test.perform_tests_private_ipv6 }.to hop("perform_blocked_private_ipv4")
end
end
describe "#perform_blocked_private_ipv4" do
it "updates firewall rules, updates the stack, and naps" do
expect(connected_subnets_test).to receive(:ps_multiple).and_return(ps_multiple).at_least(:once)
expect(connected_subnets_test).to receive(:update_firewall_rules).with(ps_multiple, ps_multiple, config: :perform_blocked_private_ipv4)
expect(connected_subnets_test).to receive(:update_stack).with({"firewalls" => "blocked_private_ipv4"})
expect(ps_multiple).to receive(:update_firewall_rules_set?).and_return(true)
expect { connected_subnets_test.perform_blocked_private_ipv4 }.to nap(5)
end
it "tests connection between the two subnets and hops to perform_blocked_private_ipv6" do
expect(connected_subnets_test).to receive(:frame).and_return({"firewalls" => "blocked_private_ipv4"})
vm1 = instance_double(Vm, sshable: sshable, nics: [instance_double(Nic, private_ipv4: NetAddr::IPv4Net.parse("10.0.0.0/26"))])
vm2 = instance_double(Vm)
expect(connected_subnets_test).to receive(:vm_to_be_connected).and_return(vm1).at_least(:once)
expect(connected_subnets_test).to receive(:vm_to_connect).and_return(vm2).at_least(:once)
expect(connected_subnets_test).to receive(:vm_to_connect_outside).and_return(vm2).at_least(:once)
expect(sshable).to receive(:cmd).with("sudo systemctl start listening_ipv4.service")
expect(sshable).to receive(:cmd).with("sudo systemctl stop listening_ipv6.service")
expect(connected_subnets_test).to receive(:test_connection).with(vm1.nics.first.private_ipv4.nth(0).to_s, vm2, should_fail: false, ipv4: true)
expect(connected_subnets_test).to receive(:test_connection).with(vm1.nics.first.private_ipv4.nth(0).to_s, vm2, should_fail: true, ipv4: true)
expect { connected_subnets_test.perform_blocked_private_ipv4 }.to hop("perform_blocked_private_ipv6")
end
end
describe "#perform_blocked_private_ipv6" do
it "updates firewall rules, updates the stack, and naps" do
expect(connected_subnets_test).to receive(:ps_multiple).and_return(ps_multiple).at_least(:once)
expect(connected_subnets_test).to receive(:update_firewall_rules).with(ps_multiple, ps_multiple, config: :perform_blocked_private_ipv6)
expect(connected_subnets_test).to receive(:update_stack).with({"firewalls" => "blocked_private_ipv6"})
expect(ps_multiple).to receive(:update_firewall_rules_set?).and_return(true)
expect { connected_subnets_test.perform_blocked_private_ipv6 }.to nap(5)
end
it "tests connection between the two subnets and hops to perform_tests_public_blocked" do
expect(connected_subnets_test).to receive(:frame).and_return({"firewalls" => "blocked_private_ipv6"})
vm1 = instance_double(Vm, sshable: sshable, nics: [instance_double(Nic, private_ipv6: NetAddr::IPv6Net.parse("2001:db8::/64"))], private_ipv6: NetAddr::IPv6.parse("2001:db8::2"))
vm2 = instance_double(Vm)
expect(connected_subnets_test).to receive(:vm_to_be_connected).and_return(vm1).at_least(:once)
expect(connected_subnets_test).to receive(:vm_to_connect).and_return(vm2).at_least(:once)
expect(connected_subnets_test).to receive(:vm_to_connect_outside).and_return(vm2).at_least(:once)
expect(sshable).to receive(:cmd).with("sudo systemctl start listening_ipv6.service")
expect(sshable).to receive(:cmd).with("sudo systemctl stop listening_ipv4.service")
expect(connected_subnets_test).to receive(:test_connection).with(vm1.nics.first.private_ipv6.nth(2).to_s, vm2, should_fail: false, ipv4: false)
expect(connected_subnets_test).to receive(:test_connection).with(vm1.nics.first.private_ipv6.nth(2).to_s, vm2, should_fail: true, ipv4: false)
expect { connected_subnets_test.perform_blocked_private_ipv6 }.to hop("finish")
end
end
describe "#finish" do
it "pops a message" do
expect(connected_subnets_test).to receive(:ps_multiple).and_return(ps_multiple).at_least(:once)
expect(connected_subnets_test).to receive(:ps_single).and_return(ps_single).at_least(:once)
expect(connected_subnets_test).to receive(:update_firewall_rules).with(ps_multiple, nil, config: :allow_all_traffic)
expect(connected_subnets_test).to receive(:update_firewall_rules).with(ps_single, nil, config: :allow_all_traffic)
expect(ps_multiple).to receive(:disconnect_subnet).with(ps_single)
expect(connected_subnets_test).to receive(:pop).with("Verified Connected Subnets!")
connected_subnets_test.finish
end
end
describe "#failed" do
it "naps" do
expect(connected_subnets_test).to receive(:nap).with(15)
connected_subnets_test.failed
end
end
describe ".update_firewall_rules" do
it "updates the firewall rules for different configurations" do
expect(Sequel).to receive(:pg_range).with(22..22).and_return("22..22").at_least(:once)
expect(Sequel).to receive(:pg_range).with(8080..8080).and_return("8080..8080").at_least(:once)
expect(Sequel).to receive(:pg_range).with(0..65535).and_return("0..65535").at_least(:once)
expect(Net::HTTP).to receive(:get).with(URI("https://api.ipify.org")).and_return("100.100.100.100").at_least(:once)
expect(ps_multiple.firewalls.first).to receive(:replace_firewall_rules).with([{cidr: "100.100.100.100/32", port_range: "22..22"}])
connected_subnets_test.update_firewall_rules(ps_multiple, ps_multiple, config: :perform_tests_public_blocked)
expect(ps_multiple.firewalls.first).to receive(:replace_firewall_rules).with([{cidr: "100.100.100.100/32", port_range: "22..22"}, {cidr: ps_single.net4.to_s, port_range: "8080..8080"}])
connected_subnets_test.update_firewall_rules(ps_multiple, ps_single, config: :perform_connected_private_ipv4)
expect(ps_multiple.firewalls.first).to receive(:replace_firewall_rules).with([{cidr: "100.100.100.100/32", port_range: "22..22"}, {cidr: ps_single.net6.to_s, port_range: "8080..8080"}])
connected_subnets_test.update_firewall_rules(ps_multiple, ps_single, config: :perform_connected_private_ipv6)
expect(ps_multiple.firewalls.first).to receive(:replace_firewall_rules).with([{cidr: "100.100.100.100/32", port_range: "22..22"}, {cidr: ps_multiple.net4.to_s, port_range: "8080..8080"}])
connected_subnets_test.update_firewall_rules(ps_multiple, ps_multiple, config: :perform_blocked_private_ipv4)
expect(ps_multiple.firewalls.first).to receive(:replace_firewall_rules).with([{cidr: "100.100.100.100/32", port_range: "22..22"}, {cidr: ps_multiple.net6.to_s, port_range: "8080..8080"}])
connected_subnets_test.update_firewall_rules(ps_multiple, ps_multiple, config: :perform_blocked_private_ipv6)
expect(ps_multiple.firewalls.first).to receive(:replace_firewall_rules).with([{cidr: "100.100.100.100/32", port_range: "22..22"}, {cidr: "0.0.0.0/0", port_range: "0..65535"}, {cidr: "::/0", port_range: "0..65535"}])
connected_subnets_test.update_firewall_rules(ps_multiple, nil, config: :allow_all_traffic)
expect { connected_subnets_test.update_firewall_rules(ps_multiple, ps_multiple, config: :unknown) }.to raise_error("Unknown config: unknown")
end
end
describe ".vm_to_be_connected" do
it "returns the vm to be connected" do
expect(connected_subnets_test).to receive(:ps_multiple).and_return(ps_multiple).at_least(:once)
vm1 = instance_double(Vm)
vm2 = instance_double(Vm)
expect(ps_multiple).to receive(:vms).and_return([vm1, vm2]).at_least(:once)
expect(connected_subnets_test.vm_to_be_connected).to eq(vm1)
end
it "returns the vm to be connected when already connected" do
expect(connected_subnets_test).to receive(:ps_multiple).and_return(ps_multiple).at_least(:once)
vm1 = instance_double(Vm, id: "vm1")
vm2 = instance_double(Vm, id: "vm2")
expect(ps_multiple).to receive(:vms).and_return([vm1, vm2]).at_least(:once)
expect(connected_subnets_test).to receive(:frame).and_return({"vm_to_be_connected_id" => vm2.id})
expect(connected_subnets_test.vm_to_be_connected).to eq(vm2)
end
end
describe ".vm_to_connect" do
it "returns the vm to connect" do
expect(connected_subnets_test).to receive(:ps_multiple).and_return(ps_multiple).at_least(:once)
vm1 = instance_double(Vm, id: "vm1")
vm2 = instance_double(Vm, id: "vm2")
expect(ps_multiple).to receive(:vms).and_return([vm1, vm2]).at_least(:once)
expect(connected_subnets_test.vm_to_connect).to eq(vm2)
end
end
describe ".vm_to_connect_outside" do
it "returns the vm to connect outside" do
expect(connected_subnets_test).to receive(:ps_single).and_return(ps_single).at_least(:once)
vm = instance_double(Vm)
expect(ps_single).to receive(:vms).and_return([vm]).at_least(:once)
expect(connected_subnets_test.vm_to_connect_outside).to eq(vm)
end
end
describe ".test_connection" do
it "tests the connection" do
to_connect_ip = "1.1.1.1"
connecting = instance_double(Vm, sshable: sshable, inhost_name: "connecting")
expect(sshable).to receive(:cmd).with("nc -zvw 1 1.1.1.1 8080 ").and_return("success!")
expect { connected_subnets_test.test_connection(to_connect_ip, connecting, should_fail: false, ipv4: true) }.not_to raise_error
expect(sshable).to receive(:cmd).with("nc -zvw 1 1.1.1.1 8080 ").and_raise("error")
expect(connected_subnets_test).to receive(:fail_test).with("connecting should be able to connect to 1.1.1.1 on port 8080")
connected_subnets_test.test_connection(to_connect_ip, connecting, should_fail: false, ipv4: true)
expect(sshable).to receive(:cmd).with("nc -zvw 1 1.1.1.1 8080 -6").and_return("success!")
expect { connected_subnets_test.test_connection(to_connect_ip, connecting, should_fail: false, ipv4: false) }.not_to raise_error
expect(sshable).to receive(:cmd).with("nc -zvw 1 1.1.1.1 8080 -6").and_raise("error")
expect(connected_subnets_test).to receive(:fail_test).with("connecting should be able to connect to 1.1.1.1 on port 8080")
connected_subnets_test.test_connection(to_connect_ip, connecting, should_fail: false, ipv4: false)
expect(sshable).to receive(:cmd).with("nc -zvw 1 1.1.1.1 8080 ").and_return("success!")
expect(connected_subnets_test).to receive(:fail_test).with("connecting should not be able to connect to 1.1.1.1 on port 8080")
connected_subnets_test.test_connection(to_connect_ip, connecting, should_fail: true, ipv4: true)
expect(sshable).to receive(:cmd).with("nc -zvw 1 1.1.1.1 8080 -6").and_return("success!")
expect(connected_subnets_test).to receive(:fail_test).with("connecting should not be able to connect to 1.1.1.1 on port 8080")
connected_subnets_test.test_connection(to_connect_ip, connecting, should_fail: true, ipv4: false)
expect(sshable).to receive(:cmd).with("nc -zvw 1 1.1.1.1 8080 ").and_raise("error")
expect(connected_subnets_test.test_connection(to_connect_ip, connecting, should_fail: true, ipv4: true)).to eq(0)
expect(sshable).to receive(:cmd).with("nc -zvw 1 1.1.1.1 8080 -6").and_raise("error")
expect(connected_subnets_test.test_connection(to_connect_ip, connecting, should_fail: true, ipv4: false)).to eq(0)
end
end
end