mirror of
https://github.com/vagrant-libvirt/vagrant-libvirt.git
synced 2025-02-25 18:55:27 -06:00
Switch to xml-simple for document comparison (#1567)
The switch to compare-xml exhibits non intuitive behaviour in that comparing without use of verbose mode does not detect documents that are identical. The if statement allowed the tests to pass but reads that if the documents compare as true then emit an error message and raise an exception. However this is because it returns false even with the docs are the same content. Unfortunately there was no pre-existing test case added when Since there were concerns about equivalent-xml not being active upstream raised by a distribution maintainer, switching back should be avoided. Attempted use of nokogiri-diff indicated that whitespace changes would be treated as differences with it being unclear how to easily exclude. Moving to xml-simple, which although will treat whitespace as significant and even with NormaliseSpace set to disregard any whitespace around content elements, it requires walking the returned structure to remove the empty content attribute that is left behind to allow a direct comparison between the data structures. To ensure the XML comparison is validated add a test where libvirt returns XML that is different to what was requested, and assert that the expected error is raised, an error message emitted and that the domain define would be reverted to the previous state if possible. Relates-To: #1565 Fixes: #1556
This commit is contained in:
parent
64910dbd90
commit
439830b6d4
@ -2,14 +2,16 @@
|
||||
|
||||
require 'log4r'
|
||||
|
||||
require 'compare-xml'
|
||||
require 'rexml/document'
|
||||
|
||||
require 'vagrant-libvirt/util/xml'
|
||||
|
||||
module VagrantPlugins
|
||||
module ProviderLibvirt
|
||||
module Action
|
||||
# Just start the domain.
|
||||
class StartDomain
|
||||
|
||||
def initialize(app, _env)
|
||||
@logger = Log4r::Logger.new('vagrant_libvirt::action::start_domain')
|
||||
@app = app
|
||||
@ -427,27 +429,13 @@ module VagrantPlugins
|
||||
|
||||
begin
|
||||
# need to check whether the updated XML contains all the changes requested
|
||||
proposed = VagrantPlugins::ProviderLibvirt::Util::Xml.new(new_xml)
|
||||
applied = VagrantPlugins::ProviderLibvirt::Util::Xml.new(libvirt_domain.xml_desc(1))
|
||||
|
||||
# This normalizes the attribute order to be consistent across both XML docs to
|
||||
# eliminate differences for subsequent comparison by diffy
|
||||
applied_xml_descr = REXML::Document.new(libvirt_domain.xml_desc(1))
|
||||
applied_xml = String.new
|
||||
applied_xml_descr.write(applied_xml)
|
||||
|
||||
proposed = Nokogiri::XML(new_xml, &:noblanks)
|
||||
applied = Nokogiri::XML(applied_xml, &:noblanks)
|
||||
|
||||
if CompareXML.equivalent?(proposed, applied, { force_children: true })
|
||||
if proposed != applied
|
||||
require 'diffy'
|
||||
|
||||
# pretty print the XML as even though there can be additional changes,
|
||||
# the output with diffy appears to be clearer
|
||||
pretty_proposed = StringIO.new
|
||||
pretty_applied = StringIO.new
|
||||
proposed.write_xml_to(pretty_proposed, indent: 2)
|
||||
applied.write_xml_to(pretty_applied, indent: 2)
|
||||
|
||||
diff = Diffy::Diff.new(pretty_proposed.string, pretty_applied.string, :context => 3).to_s(:text)
|
||||
diff = Diffy::Diff.new(proposed, applied, :context => 3).to_s(:text)
|
||||
|
||||
error_msg = "Libvirt failed to fully update the domain with the specified XML. Result differs from requested:\n" +
|
||||
"--- requested\n+++ result\n#{diff}\n" +
|
||||
|
47
lib/vagrant-libvirt/util/xml.rb
Normal file
47
lib/vagrant-libvirt/util/xml.rb
Normal file
@ -0,0 +1,47 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'xmlsimple'
|
||||
|
||||
module VagrantPlugins
|
||||
module ProviderLibvirt
|
||||
module Util
|
||||
class Xml
|
||||
attr_reader :xml
|
||||
|
||||
def initialize(xmlstr)
|
||||
@xml = compact_content(XmlSimple.xml_in(xmlstr, {'NormaliseSpace' => 2}))
|
||||
end
|
||||
|
||||
def to_str
|
||||
XmlSimple.xml_out(@xml)
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
@xml == other.xml
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# content elements that are empty are preserved by xml-simple and will result
|
||||
# in the structures being considered different even if functionality the same
|
||||
# strip any empty ones to avoid.
|
||||
def compact_content(node)
|
||||
if node.is_a?(Array)
|
||||
node.map! do |element|
|
||||
compact_content(element)
|
||||
end
|
||||
elsif node.is_a?(Hash)
|
||||
if node['content'] and node['content'].empty?
|
||||
node.delete('content')
|
||||
end
|
||||
node.each do |k, v|
|
||||
node[k] = compact_content(v)
|
||||
end
|
||||
else
|
||||
return node
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -72,7 +72,30 @@ describe VagrantPlugins::ProviderLibvirt::Action::StartDomain do
|
||||
|
||||
expect(subject.call(env)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when xml not applied' do
|
||||
let(:test_file) { 'default_with_different_formatting.xml' }
|
||||
let(:updated_domain_xml) {
|
||||
new_xml = domain_xml.dup
|
||||
new_xml.gsub!(/<cpu .*<\/cpu>/m, '<cpu mode="host-passthrough"/>')
|
||||
new_xml
|
||||
}
|
||||
let(:vagrantfile_providerconfig) do
|
||||
<<-EOF
|
||||
libvirt.cpu_mode = "host-passthrough"
|
||||
EOF
|
||||
end
|
||||
|
||||
it 'should error and revert the update' do
|
||||
expect(ui).to receive(:error)
|
||||
expect(connection).to receive(:define_domain).and_return(libvirt_domain)
|
||||
expect(connection).to receive(:define_domain).with(domain_xml) # undo
|
||||
expect(libvirt_domain).to receive(:xml_desc).and_return(domain_xml, updated_domain_xml)
|
||||
expect(domain).to_not receive(:start)
|
||||
|
||||
expect { subject.call(env) }.to raise_error(VagrantPlugins::ProviderLibvirt::Errors::FogError)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when any setting changed' do
|
||||
|
@ -23,7 +23,7 @@ Gem::Specification.new do |s|
|
||||
s.add_runtime_dependency 'fog-libvirt', '>= 0.6.0'
|
||||
s.add_runtime_dependency 'fog-core', '~> 2'
|
||||
s.add_runtime_dependency 'rexml'
|
||||
s.add_runtime_dependency 'compare-xml'
|
||||
s.add_runtime_dependency 'xml-simple'
|
||||
s.add_runtime_dependency 'diffy'
|
||||
|
||||
# Make sure to allow use of the same version as Vagrant by being less specific
|
||||
|
Loading…
Reference in New Issue
Block a user