Skip to content

Commit

Permalink
Merge pull request #3400 from canonical/handle-amend-errors
Browse files Browse the repository at this point in the history
Handle failure to convert to QCOW2 v3
  • Loading branch information
Chris Townsend committed Feb 8, 2024
1 parent 8c77fa6 commit 55d5eed
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 7 deletions.
17 changes: 15 additions & 2 deletions src/platform/backends/qemu/qemu_virtual_machine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,19 @@ auto generate_metadata(const QStringList& platform_args, const QStringList& proc
metadata[mount_data_key] = mount_args_to_json(mount_args);
return metadata;
}

void convert_to_qcow2_v3_if_necessary(const mp::Path& image_path, const std::string& vm_name)
{
try
{
// convert existing VMs to v3 too (doesn't affect images that are already v3)
mp::backend::amend_to_qcow2_v3(image_path);
}
catch (const mp::backend::QemuImgException& e)
{
mpl::log(mpl::Level::error, vm_name, e.what());
}
}
} // namespace

mp::QemuVirtualMachine::QemuVirtualMachine(const VirtualMachineDescription& desc,
Expand All @@ -211,8 +224,8 @@ mp::QemuVirtualMachine::QemuVirtualMachine(const VirtualMachineDescription& desc
monitor{&monitor},
mount_args{mount_args_from_json(monitor.retrieve_metadata_for(vm_name))}
{
// convert existing VMs to v3 too (doesn't affect images that are already v3)
mp::backend::amend_to_qcow2_v3(desc.image.image_path); // TODO drop in a couple of releases (going in on v1.13)
convert_to_qcow2_v3_if_necessary(desc.image.image_path,
vm_name); // TODO drop in a couple of releases (went in on v1.13)

QObject::connect(
this, &QemuVirtualMachine::on_delete_memory_snapshot, this,
Expand Down
11 changes: 6 additions & 5 deletions src/platform/backends/shared/qemu_img_utils/qemu_img_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ auto mp::backend::checked_exec_qemu_img(std::unique_ptr<mp::QemuImgProcessSpec>
auto process_state = timeout ? process->execute(*timeout) : process->execute();
if (!process_state.completed_successfully())
{
throw std::runtime_error(fmt::format("{}: qemu-img failed ({}) with output:\n{}",
custom_error_prefix,
process_state.failure_message(),
process->read_all_standard_error()));
throw QemuImgException{fmt::format("{}: qemu-img failed ({}) with output:\n{}",
custom_error_prefix,
process_state.failure_message(),
process->read_all_standard_error())};
}

return process;
Expand Down Expand Up @@ -90,7 +90,8 @@ mp::Path mp::backend::convert_to_qcow_if_necessary(const mp::Path& image_path)
void mp::backend::amend_to_qcow2_v3(const mp::Path& image_path)
{
checked_exec_qemu_img(
std::make_unique<mp::QemuImgProcessSpec>(QStringList{"amend", "-o", "compat=1.1", image_path}, image_path));
std::make_unique<mp::QemuImgProcessSpec>(QStringList{"amend", "-o", "compat=1.1", image_path}, image_path),
"Failed to amend image to QCOW2 v3");
}

bool mp::backend::instance_image_has_snapshot(const mp::Path& image_path, QString snapshot_tag)
Expand Down
6 changes: 6 additions & 0 deletions src/platform/backends/shared/qemu_img_utils/qemu_img_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ class QemuImgProcessSpec;

namespace backend
{
class QemuImgException : public std::runtime_error
{
public:
using std::runtime_error::runtime_error;
};

Process::UPtr checked_exec_qemu_img(std::unique_ptr<QemuImgProcessSpec> spec,
const std::string& custom_error_prefix = "Internal error",
std::optional<int> timeout = std::nullopt);
Expand Down
24 changes: 24 additions & 0 deletions tests/qemu/test_qemu_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include "tests/common.h"
#include "tests/mock_environment_helpers.h"
#include "tests/mock_logger.h"
#include "tests/mock_process_factory.h"
#include "tests/mock_status_monitor.h"
#include "tests/stub_process_factory.h"
Expand Down Expand Up @@ -675,6 +676,29 @@ TEST_F(QemuBackend, ssh_hostname_timeout_throws_and_sets_unknown_state)
EXPECT_EQ(machine.state, mp::VirtualMachine::State::unknown);
}

TEST_F(QemuBackend, logsErrorOnFailureToConvertToQcow2V3UponConstruction)
{
mpt::StubVMStatusMonitor stub_monitor{};
NiceMock<mpt::MockQemuPlatform> mock_qemu_platform{};

process_factory->register_callback([this](mpt::MockProcess* process) {
if (process->program().contains("qemu-img") && process->arguments().contains("compat=1.1"))
{
mp::ProcessState exit_state{};
exit_state.exit_code = 1;
ON_CALL(*process, execute).WillByDefault(Return(exit_state));
}
else
return handle_external_process_calls(process);
});

auto logger_scope = mpt::MockLogger::inject();
logger_scope.mock_logger->screen_logs(mpl::Level::error);
logger_scope.mock_logger->expect_log(mpl::Level::error, "Failed to amend image to QCOW2 v3");

mp::QemuVirtualMachine machine{default_description, &mock_qemu_platform, stub_monitor, instance_dir.path()};
}

TEST_F(QemuBackend, lists_no_networks)
{
EXPECT_CALL(*mock_qemu_platform_factory, make_qemu_platform(_)).WillOnce([this](auto...) {
Expand Down

0 comments on commit 55d5eed

Please sign in to comment.