Files
vagrant-libvirt/spec/unit/action/destroy_domain_spec.rb
Darragh Bailey 37597e22f9 Add action to resolve disk settings (#1502)
With multi volume boxes, need to ensure that disk settings such as the
device assigned are resolved dynamically once it has been established
which devices have already been assigned to the box volumes on either
initial creation or subsequent boots.

Otherwise users are forced to always explicitly define the device for
additional storage instead of having it be automatically assigned the
next available device.

Consequently previous changes have broken the ability for machines
with additional storage to be halted and restarted correctly.

Include an integration test that for additional storage checks that the
machine can be stopped and started again.

Fixes: #1490
2022-06-02 19:09:18 +01:00

241 lines
9.7 KiB
Ruby

# frozen_string_literal: true
require 'spec_helper'
require 'support/sharedcontext'
require 'support/libvirt_context'
require 'vagrant-libvirt/action/destroy_domain'
describe VagrantPlugins::ProviderLibvirt::Action::DestroyDomain do
subject { described_class.new(app, env) }
include_context 'unit'
include_context 'libvirt'
let(:driver) { double('driver') }
let(:libvirt_domain) { double('libvirt_domain') }
let(:libvirt_client) { double('libvirt_client') }
let(:servers) { double('servers') }
let(:domain_xml) { File.read(File.join(File.dirname(__FILE__), File.basename(__FILE__, '.rb'), domain_xml_file)) }
let(:destroy_method) { double('destroy_method') }
before do
allow(machine.provider).to receive('driver').and_return(driver)
allow(driver).to receive(:connection).and_return(connection)
allow(logger).to receive(:info)
allow(domain).to receive(:method).with(:destroy).and_return(destroy_method)
allow(destroy_method).to receive(:parameters).and_return([[:opt, :options, :flags]])
end
describe '#call' do
before do
allow(connection).to receive(:client).and_return(libvirt_client)
allow(libvirt_client).to receive(:lookup_domain_by_uuid)
.and_return(libvirt_domain)
allow(libvirt_domain).to receive(:name).and_return('vagrant-test_default')
allow(connection).to receive(:servers).and_return(servers)
allow(servers).to receive(:get).and_return(domain)
# always see this at the start of #call
expect(ui).to receive(:info).with('Removing domain...')
end
context 'when no snapshots' do
let(:root_disk) { double('libvirt_root_disk') }
before do
allow(libvirt_domain).to receive(:list_snapshots).and_return([])
allow(libvirt_domain).to receive(:has_managed_save?).and_return(nil)
allow(root_disk).to receive(:name).and_return('vagrant-test_default.img')
end
context 'when box only has one root disk' do
it 'calls fog to destroy volumes' do
expect(domain).to receive(:destroy).with(destroy_volumes: true, flags: 0)
expect(subject.call(env)).to be_nil
end
context 'when has additional disks' do
let(:vagrantfile_providerconfig) do
<<-EOF
libvirt.storage :file
EOF
end
let(:domain_xml_file) { 'additional_disks_domain.xml' }
let(:extra_disk) { double('libvirt_extra_disk') }
before do
allow(extra_disk).to receive(:name).and_return('vagrant-test_default-vdb.qcow2')
allow(domain).to receive(:volumes).and_return([root_disk, extra_disk])
expect(libvirt_domain).to receive(:xml_desc).and_return(domain_xml)
end
it 'destroys disks individually' do
expect(domain).to receive(:destroy).with(destroy_volumes: false, flags: 0)
expect(extra_disk).to receive(:destroy) # extra disk remove
expect(root_disk).to receive(:destroy) # root disk remove
expect(subject.call(env)).to be_nil
end
end
end
context 'when box has multiple disks' do
let(:domain_xml_file) { 'box_multiple_disks.xml' }
it 'calls fog to destroy volumes' do
expect(domain).to receive(:destroy).with(destroy_volumes: true, flags: 0)
expect(subject.call(env)).to be_nil
end
context 'when has additional disks' do
let(:domain_xml_file) { 'box_multiple_disks_and_additional_disks.xml' }
let(:vagrantfile_providerconfig) do
<<-EOF
libvirt.storage :file
libvirt.storage :file
EOF
end
let(:domain_disks) {[
[double('box-disk-1'), 'vagrant-test_default.img'],
[double('box-disk-2'), 'vagrant-test_default_1.img'],
[double('box-disk-3'), 'vagrant-test_default_2.img'],
[double('additional-disk-1'), 'vagrant-test_default-vdd.qcow2'],
[double('additional-disk-2'), 'vagrant-test_default-vde.qcow2'],
]}
before do
allow(libvirt_domain).to receive(:xml_desc).and_return(domain_xml)
allow(domain).to receive(:volumes).and_return(domain_disks.map { |a| a.first })
end
it 'destroys disks individually' do
domain_disks.each do |disk, name|
expect(disk).to receive(:name).and_return(name).at_least(:once)
expect(disk).to receive(:destroy)
end
expect(domain).to receive(:destroy).with(destroy_volumes: false, flags: 0)
expect(subject.call(env)).to be_nil
end
context 'when has disks added via custom virsh commands' do
let(:domain_xml_file) { 'box_multiple_disks_and_additional_and_custom_disks.xml' }
let(:domain_disks) {[
[double('box-disk-1'), 'vagrant-test_default.img'],
[double('box-disk-2'), 'vagrant-test_default_1.img'],
[double('box-disk-3'), 'vagrant-test_default_2.img'],
[double('additional-disk-1'), 'vagrant-test_default-vdd.qcow2'],
[double('additional-disk-2'), 'vagrant-test_default-vde.qcow2'],
[double('custom-disk-1'), 'vagrant-test_default-vdf.qcow2'],
]}
it 'only destroys expected disks' do
expect(ui).to receive(:warn).with(/Unexpected number of volumes detected.*/)
domain_disks.each do |disk, name|
expect(disk).to receive(:name).and_return(name).at_least(:once)
next if disk == domain_disks.last.first
expect(disk).to receive(:destroy)
end
expect(domain).to receive(:destroy).with(destroy_volumes: false, flags: 0)
expect(subject.call(env)).to be_nil
end
context 'without aliases' do
let(:domain_xml_file) { 'box_multiple_disks_and_additional_and_custom_disks_no_aliases.xml' }
it 'only destroys expected disks' do
expect(ui).to receive(:warn).with(/Machine that was originally created without device aliases.*/)
expect(ui).to receive(:warn).with(/Unexpected number of volumes detected/)
expect(ui).to receive(:warn).with(/box metadata not available to get volume list during destroy, assuming inferred list/)
domain_disks.each do |disk, name|
expect(disk).to receive(:name).and_return(name).at_least(:once)
# ignore box disks 2 and 3 and the last custom disk
next if domain_disks.last.first == disk
expect(disk).to receive(:destroy)
end
expect(domain).to receive(:destroy).with(destroy_volumes: false, flags: 0)
expect(subject.call(env)).to be_nil
end
context 'with box metadata' do
let(:box) { instance_double(::Vagrant::Box) }
before do
allow(env[:machine]).to receive(:box).and_return(box)
allow(box).to receive(:metadata).and_return(Hash[
'disks' => [
{:name => 'box-disk-1'},
{:name => 'box-disk-2'},
{:name => 'box-disk-3'},
]
])
end
it 'only destroys expected disks' do
expect(ui).to receive(:warn).with(/Machine that was originally created without device aliases.*/)
expect(ui).to receive(:warn).with(/Unexpected number of volumes detected/)
domain_disks.each do |disk, name|
expect(disk).to receive(:name).and_return(name).at_least(:once)
# ignore box disks 2 and 3 and the last custom disk
next if domain_disks.last.first == disk
expect(disk).to receive(:destroy)
end
expect(domain).to receive(:destroy).with(destroy_volumes: false, flags: 0)
expect(subject.call(env)).to be_nil
end
end
end
end
end
end
context 'when has nvram' do
let(:vagrantfile) do
<<-EOF
Vagrant.configure('2') do |config|
config.vm.define :test
config.vm.provider :libvirt do |libvirt|
libvirt.nvram = "test"
end
end
EOF
end
it 'sets destroy flags to keep nvram' do
expect(domain).to receive(:destroy).with(destroy_volumes: true, flags: VagrantPlugins::ProviderLibvirt::Util::DomainFlags::VIR_DOMAIN_UNDEFINE_KEEP_NVRAM)
expect(subject.call(env)).to be_nil
end
context 'when fog does not support destroy with flags' do
before do
expect(destroy_method).to receive(:parameters).and_return([[:opt, :options]])
end
it 'skips setting additional destroy flags' do
expect(domain).to receive(:destroy).with(destroy_volumes: true)
expect(subject.call(env)).to be_nil
end
end
end
context 'when has CDROMs attached' do
let(:vagrantfile_providerconfig) do
<<-EOF
libvirt.storage :file, :device => :cdrom
EOF
end
let(:domain_xml_file) { 'cdrom_domain.xml' }
it 'uses explicit removal of disks' do
expect(domain).to receive(:volumes).and_return([root_disk, nil])
expect(libvirt_domain).to receive(:xml_desc).and_return(domain_xml)
expect(domain).to_not receive(:destroy).with(destroy_volumes: true, flags: 0)
expect(root_disk).to receive(:destroy) # root disk remove
expect(subject.call(env)).to be_nil
end
end
end
end
end