When the destroy semaphore is increased, we want to hop to the destroy label from any label. If the prog defines a before_run method, we call it just before running each label. Some of these methods have custom logic, but most of them are similar. To achieve 100% line coverage, we test similar functionality repeatedly. I’ve moved this to the base program. So, if the prog doesn’t override before_run and has a destroy label and semaphore, we call this basic flow in all progs. The custom logics in progs also follow similar patterns. We can gradually move them to the base prog as well.
200 lines
7.2 KiB
Ruby
200 lines
7.2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "spec_helper"
|
|
require_relative "../../../prog/vm/vm_host_slice_nexus"
|
|
require_relative "../../../prog/vm/host_nexus"
|
|
|
|
RSpec.describe Prog::Vm::VmHostSliceNexus do
|
|
subject(:nx) { described_class.new(Strand.create(id: "b231a172-8f56-8b10-bbed-8916ea4e5c28", prog: "Prog::Vm::VmHostSliceNexus", label: "create")) }
|
|
|
|
let(:sshable) { vm_host.sshable }
|
|
|
|
let(:vm_host) { create_vm_host(total_cores: 4, used_cores: 1) }
|
|
|
|
let(:vm_host_slice) {
|
|
VmHostSlice.create(
|
|
vm_host_id: vm_host.id,
|
|
name: "standard",
|
|
family: "standard",
|
|
is_shared: false,
|
|
cores: 1,
|
|
total_cpu_percent: 200,
|
|
used_cpu_percent: 0,
|
|
total_memory_gib: 4,
|
|
used_memory_gib: 0
|
|
)
|
|
}
|
|
|
|
before do
|
|
allow(nx).to receive_messages(vm_host_slice: vm_host_slice)
|
|
allow(vm_host_slice).to receive_messages(vm_host: vm_host)
|
|
(0..15).each { |i|
|
|
VmHostCpu.create(
|
|
spdk: i < 2,
|
|
vm_host_slice_id: (i == 2 || i == 3) ? vm_host_slice.id : nil
|
|
) {
|
|
_1.vm_host_id = vm_host.id
|
|
_1.cpu_number = i
|
|
}
|
|
}
|
|
end
|
|
|
|
describe ".assemble_with_host" do
|
|
it "creates vm host slice" do
|
|
# prepare the host for the test
|
|
st_vh = Prog::Vm::HostNexus.assemble("1.2.3.4")
|
|
host = st_vh.subject
|
|
expect(host).not_to be_nil
|
|
host.update(total_cpus: 8, total_cores: 4)
|
|
|
|
(0..15).each { |i|
|
|
VmHostCpu.create(vm_host_id: host.id, cpu_number: i, spdk: i < 2)
|
|
}
|
|
|
|
# run the assemble test
|
|
st_rg = described_class.assemble_with_host("standard", host, family: "standard", allowed_cpus: [2, 3], memory_gib: 4)
|
|
rg = st_rg.subject
|
|
expect(rg).not_to be_nil
|
|
expect(rg.name).to eq("standard")
|
|
expect(rg.allowed_cpus_cgroup).to eq("2-3")
|
|
expect(rg.cores).to eq(1)
|
|
expect(rg.total_cpu_percent).to eq(200)
|
|
expect(rg.used_cpu_percent).to eq(0)
|
|
expect(rg.total_memory_gib).to eq(4)
|
|
expect(rg.used_memory_gib).to eq(0)
|
|
expect(rg.enabled).to be(false)
|
|
expect(rg.is_shared).to be(false)
|
|
expect(rg.id).to eq(st_rg.id)
|
|
expect(rg.ubid).to eq(st_rg.ubid)
|
|
expect(rg.ubid[..1] == "vs").to be true
|
|
expect(rg.vm_host).not_to be_nil
|
|
expect(rg.vm_host.id).to eq(host.id)
|
|
end
|
|
end
|
|
|
|
describe "#prep" do
|
|
it "starts prep on NotStarted" do
|
|
expect(sshable).to receive(:cmd).with("common/bin/daemonizer --check prep_standard").and_return("NotStarted")
|
|
expect(sshable).to receive(:cmd).with("common/bin/daemonizer 'sudo host/bin/setup-slice prep standard.slice \"2-3\"' prep_standard")
|
|
expect(vm_host_slice).to receive(:inhost_name).and_return("standard.slice")
|
|
|
|
expect { nx.prep }.to nap(1)
|
|
end
|
|
|
|
it "starts prep on Failed" do
|
|
expect(sshable).to receive(:cmd).with("common/bin/daemonizer --check prep_standard").and_return("Failed")
|
|
expect(sshable).to receive(:cmd).with("common/bin/daemonizer 'sudo host/bin/setup-slice prep standard.slice \"2-3\"' prep_standard")
|
|
expect(vm_host_slice).to receive(:inhost_name).and_return("standard.slice")
|
|
|
|
expect { nx.prep }.to nap(1)
|
|
end
|
|
|
|
it "hops to wait" do
|
|
expect(sshable).to receive(:cmd).with("common/bin/daemonizer --check prep_standard").and_return("Succeeded")
|
|
expect(sshable).to receive(:cmd).with("common/bin/daemonizer --clean prep_standard")
|
|
expect(vm_host_slice).to receive(:update).with(enabled: true)
|
|
|
|
expect { nx.prep }.to hop("wait")
|
|
end
|
|
|
|
it "do nothing on random result" do
|
|
expect(sshable).to receive(:cmd).with("common/bin/daemonizer --check prep_standard").and_return("foobar")
|
|
|
|
expect { nx.prep }.to nap(1)
|
|
end
|
|
end
|
|
|
|
describe "#wait" do
|
|
it "naps for 30 seconds" do
|
|
expect { nx.wait }.to nap(30)
|
|
end
|
|
|
|
it "hops to start_after_host_reboot when signaled" do
|
|
expect(nx).to receive(:when_start_after_host_reboot_set?).and_yield
|
|
expect(nx).to receive(:register_deadline).with(:wait, 5 * 60)
|
|
expect { nx.wait }.to hop("start_after_host_reboot")
|
|
end
|
|
|
|
it "hops to unavailable based on the slice's available status" do
|
|
expect(nx).to receive(:when_checkup_set?).and_yield
|
|
expect(nx).to receive(:available?).and_return(false)
|
|
expect { nx.wait }.to hop("unavailable")
|
|
|
|
expect(nx).to receive(:when_checkup_set?).and_yield
|
|
expect(nx).to receive(:available?).and_raise Sshable::SshError.new("ssh failed", "", "", nil, nil)
|
|
expect { nx.wait }.to hop("unavailable")
|
|
|
|
expect(nx).to receive(:when_checkup_set?).and_yield
|
|
expect(nx).to receive(:available?).and_return(true)
|
|
expect { nx.wait }.to nap(30)
|
|
end
|
|
end
|
|
|
|
describe "#destroy" do
|
|
it "deletes resources and exits" do
|
|
expect(vm_host_slice).to receive(:destroy)
|
|
expect(sshable).to receive(:cmd).with("sudo host/bin/setup-slice delete standard.slice")
|
|
expect(vm_host_slice).to receive(:inhost_name).and_return("standard.slice")
|
|
|
|
expect { nx.destroy }.to exit({"msg" => "vm_host_slice destroyed"})
|
|
end
|
|
end
|
|
|
|
describe "#start_after_host_reboot" do
|
|
it "starts slice on the host and hops to wait" do
|
|
expect(sshable).to receive(:cmd).with("sudo host/bin/setup-slice recreate-unpersisted standard.slice")
|
|
expect(vm_host_slice).to receive(:inhost_name).and_return("standard.slice")
|
|
|
|
expect { nx.start_after_host_reboot }.to hop("wait")
|
|
end
|
|
end
|
|
|
|
describe "#unavailable" do
|
|
it "hops to start_after_host_reboot when needed" do
|
|
expect(nx).to receive(:when_start_after_host_reboot_set?).and_yield
|
|
expect(nx).to receive(:incr_checkup)
|
|
expect { nx.unavailable }.to hop("start_after_host_reboot")
|
|
end
|
|
|
|
it "creates a page if vm is unavailable" do
|
|
expect(Prog::PageNexus).to receive(:assemble)
|
|
expect(nx).to receive(:available?).and_return(false)
|
|
expect { nx.unavailable }.to nap(30)
|
|
end
|
|
|
|
it "resolves the page if vm is available" do
|
|
pg = instance_double(Page)
|
|
expect(pg).to receive(:incr_resolve)
|
|
expect(nx).to receive(:available?).and_return(true)
|
|
expect(Page).to receive(:from_tag_parts).and_return(pg)
|
|
expect { nx.unavailable }.to hop("wait")
|
|
end
|
|
|
|
it "does not resolves the page if there is none" do
|
|
expect(nx).to receive(:available?).and_return(true)
|
|
expect(Page).to receive(:from_tag_parts).and_return(nil)
|
|
expect { nx.unavailable }.to hop("wait")
|
|
end
|
|
end
|
|
|
|
describe "#available?" do
|
|
let(:session) { instance_double(Net::SSH::Connection::Session) }
|
|
|
|
before do
|
|
expect(sshable).to receive(:start_fresh_session).and_yield(session)
|
|
expect(session).to receive(:exec!).with("systemctl is-active standard.slice").and_return("active\nactive\n").once
|
|
expect(session).to receive(:exec!).with("cat /sys/fs/cgroup/standard.slice/cpuset.cpus.effective").and_return("2-3\n").once
|
|
end
|
|
|
|
it "returns the available status" do
|
|
expect(session).to receive(:exec!).with("cat /sys/fs/cgroup/standard.slice/cpuset.cpus.partition").and_return("root\n").once
|
|
expect(nx.available?).to be true
|
|
end
|
|
|
|
it "fails on the incorrect partition status" do
|
|
expect(session).to receive(:exec!).with("cat /sys/fs/cgroup/standard.slice/cpuset.cpus.partition").and_return("member\n").once
|
|
expect(nx.available?).to be false
|
|
end
|
|
end
|
|
end
|