Files
ubicloud/prog/test/vm_group.rb
Enes Cakir 4766f57315 Ensure all boot images are tested
Previously, we randomly selected a boot image for VM tests. This means
that if a specific boot image broke, we might miss it since we don’t
test all boot images all the time.

However, the green mark can mislead developers into thinking everything
is fine.

We should test all boot images to ensure they are functioning properly.
This PR creates virtual machines to test at least a minimum number of
boot images.

If the number of provided boot images is less than 3, it will repeat the
boot images to reach a total of 3. This way, we will create at least 3
virtual machines for testing.
2025-01-23 15:30:06 +03:00

134 lines
3.5 KiB
Ruby

# frozen_string_literal: true
require "net/ssh"
class Prog::Test::VmGroup < Prog::Test::Base
def self.assemble(boot_images:, storage_encrypted: true, test_reboot: true)
Strand.create_with_id(
prog: "Test::VmGroup",
label: "start",
stack: [{
"storage_encrypted" => storage_encrypted,
"test_reboot" => test_reboot,
"vms" => [],
"boot_images" => boot_images
}]
)
end
label def start
hop_setup_vms
end
label def setup_vms
project = Project.create_with_id(name: "project-1")
subnets = Array.new(2) { Prog::Vnet::SubnetNexus.assemble(project.id, name: "subnet-#{_1}", location: "hetzner-fsn1") }
encrypted = frame.fetch("storage_encrypted", true)
boot_images = frame.fetch("boot_images")
storage_options = [
[{encrypted:, skip_sync: true}, {encrypted:, size_gib: 5}],
[{encrypted:, skip_sync: false, max_read_mbytes_per_sec: 200, max_write_mbytes_per_sec: 150, max_ios_per_sec: 25600}],
[{encrypted:, skip_sync: false}]
]
vm_count = [boot_images.size, storage_options.size].max
vms = Array.new(vm_count) do |index|
Prog::Vm::Nexus.assemble_with_sshable(
"ubi", project.id,
private_subnet_id: subnets[index % subnets.size].id,
storage_volumes: storage_options[index % storage_options.size],
boot_image: boot_images[index % boot_images.size],
enable_ip4: true
)
end
update_stack({
"vms" => vms.map(&:id),
"subnets" => subnets.map(&:id),
"project_id" => project.id
})
hop_wait_vms
end
label def wait_vms
nap 10 if frame["vms"].any? { Vm[_1].display_state != "running" }
hop_verify_vms
end
label def verify_vms
frame["vms"].each { bud(Prog::Test::Vm, {subject_id: _1}) }
hop_wait_verify_vms
end
label def wait_verify_vms
reap
hop_verify_firewall_rules if leaf?
donate
end
label def verify_firewall_rules
if retval&.dig("msg") == "Verified Firewall Rules!"
hop_verify_connected_subnets
end
push Prog::Test::FirewallRules, {subject_id: PrivateSubnet[frame["subnets"].first].firewalls.first.id}
end
label def verify_connected_subnets
if retval&.dig("msg") == "Verified Connected Subnets!"
if frame["test_reboot"]
hop_test_reboot
else
hop_destroy_resources
end
end
ps1, ps2 = frame["subnets"].map { PrivateSubnet[_1] }
push Prog::Test::ConnectedSubnets, {subnet_id_multiple: ((ps1.vms.count > 1) ? ps1.id : ps2.id), subnet_id_single: ((ps1.vms.count > 1) ? ps2.id : ps1.id)}
end
label def test_reboot
vm_host.incr_reboot
hop_wait_reboot
end
label def wait_reboot
if vm_host.strand.label == "wait" && vm_host.strand.semaphores.empty?
# Run VM tests again, but avoid rebooting again
update_stack({"test_reboot" => false})
hop_verify_vms
end
nap 20
end
label def destroy_resources
frame["vms"].each { Vm[_1].incr_destroy }
frame["subnets"].each { PrivateSubnet[_1].tap { |ps| ps.firewalls.each { |fw| fw.destroy } }.incr_destroy }
hop_wait_resources_destroyed
end
label def wait_resources_destroyed
unless frame["vms"].all? { Vm[_1].nil? } && frame["subnets"].all? { PrivateSubnet[_1].nil? }
nap 5
end
hop_finish
end
label def finish
Project[frame["project_id"]].destroy
pop "VmGroup tests finished!"
end
label def failed
nap 15
end
def vm_host
@vm_host ||= Vm[frame["vms"].first].vm_host
end
end