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

311 lines
18 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: "hetzner-fsn1").subject
}
let(:ps_single) {
Prog::Vnet::SubnetNexus.assemble(project.id, name: "ps-single", location: "hetzner-fsn1").subject
}
let(:project) {
prj = Project.create_with_id(name: "project1")
prj.associate_with_project(prj)
prj
}
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, sshable: sshable, ephemeral_net4: NetAddr::IPv4Net.parse("0.0.0.0"), boot_image: "debian-12")
vm2 = instance_double(Vm, 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({"connected" => true})
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({"connected" => 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"))])
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"))])
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
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