Skip to content

Commit 4724d23

Browse files
authored
Merge pull request #3846 from canonical/refactor-image-vault-utils
Refactor image vault utils
2 parents 637e431 + a57672d commit 4724d23

18 files changed

+506
-142
lines changed

include/multipass/file_ops.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ class FileOps : public Singleton<FileOps>
8787
virtual qint64 write(QFileDevice& file, const QByteArray& data) const;
8888
virtual bool flush(QFile& file) const;
8989

90+
virtual bool copy(const QString& from, const QString& to) const;
91+
9092
// QSaveFile operations
9193
virtual bool commit(QSaveFile& file) const;
9294

@@ -119,6 +121,8 @@ class FileOps : public Singleton<FileOps>
119121
std::error_code& err) const;
120122
virtual std::unique_ptr<DirIterator> dir_iterator(const fs::path& path, std::error_code& err) const;
121123
virtual fs::path weakly_canonical(const fs::path& path) const;
124+
125+
virtual fs::path remove_extension(const fs::path& path) const;
122126
};
123127
} // namespace multipass
124128

include/multipass/vm_image_vault.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,6 @@ namespace multipass
4242
class VMImageHost;
4343
namespace vault
4444
{
45-
// Helper functions and classes for all image vault types
46-
QString filename_for(const Path& path);
47-
QString copy(const QString& file_name, const QDir& output_dir);
48-
void delete_file(const Path& path);
49-
QString compute_image_hash(const Path& image_path);
50-
void verify_image_download(const Path& image_path, const QString& image_hash);
51-
QString extract_image(const Path& image_path, const ProgressMonitor& monitor, const bool delete_file = false);
52-
std::unordered_map<std::string, VMImageHost*> configure_image_host_map(const std::vector<VMImageHost*>& image_hosts);
53-
5445
class DeleteOnException
5546
{
5647
public:
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright (C) Canonical, Ltd.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; version 3.
7+
*
8+
* This program is distributed in the hope that it will be useful,
9+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
* GNU General Public License for more details.
12+
*
13+
* You should have received a copy of the GNU General Public License
14+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
15+
*
16+
*/
17+
18+
#ifndef MULTIPASS_VM_IMAGE_VAULT_UTILS_H
19+
#define MULTIPASS_VM_IMAGE_VAULT_UTILS_H
20+
21+
#include "file_ops.h"
22+
#include "xz_image_decoder.h"
23+
24+
#include <functional>
25+
#include <string>
26+
#include <unordered_map>
27+
#include <vector>
28+
29+
#define MP_IMAGE_VAULT_UTILS multipass::ImageVaultUtils::instance()
30+
31+
namespace multipass
32+
{
33+
class VMImageHost;
34+
35+
class ImageVaultUtils : public Singleton<ImageVaultUtils>
36+
{
37+
public:
38+
ImageVaultUtils(const PrivatePass&) noexcept;
39+
40+
using Decoder = std::function<void(const QString&, const QString&)>;
41+
using DefaultDecoderT = XzImageDecoder;
42+
43+
virtual QString copy_to_dir(const QString& file, const QDir& output_dir) const;
44+
[[nodiscard]] virtual QString compute_hash(QIODevice& device) const;
45+
[[nodiscard]] virtual QString compute_file_hash(const QString& file) const;
46+
47+
virtual void verify_file_hash(const QString& file, const QString& hash) const;
48+
49+
virtual QString extract_file(const QString& file, const Decoder& decoder, bool delete_original = false) const;
50+
51+
template <class DecoderT = DefaultDecoderT>
52+
QString extract_file(const QString& file,
53+
const ProgressMonitor& monitor,
54+
bool delete_original = false,
55+
const DecoderT& = DecoderT{}) const;
56+
57+
using HostMap = std::unordered_map<std::string, VMImageHost*>;
58+
using Hosts = std::vector<VMImageHost*>;
59+
60+
[[nodiscard]] virtual HostMap configure_image_host_map(const Hosts& image_hosts) const;
61+
};
62+
63+
template <class DecoderT>
64+
QString ImageVaultUtils::extract_file(const QString& file,
65+
const ProgressMonitor& monitor,
66+
bool delete_original,
67+
const DecoderT& decoder) const
68+
{
69+
auto decoder_fn = [&monitor, &decoder](const QString& encoded_file, const QString& destination) {
70+
return decoder.decode_to(encoded_file, destination, monitor);
71+
};
72+
73+
return extract_file(file, decoder_fn, delete_original);
74+
}
75+
76+
} // namespace multipass
77+
78+
#endif // MULTIPASS_VM_IMAGE_VAULT_UTILS_H

include/multipass/xz_image_decoder.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,13 @@ namespace multipass
3232
class XzImageDecoder
3333
{
3434
public:
35-
XzImageDecoder(const Path& xz_file_path);
35+
XzImageDecoder();
3636

37-
void decode_to(const Path& decoded_file_path, const ProgressMonitor& monitor);
37+
void decode_to(const Path& xz_file_path, const Path& decoded_file_path, const ProgressMonitor& monitor) const;
3838

3939
using XzDecoderUPtr = std::unique_ptr<xz_dec, decltype(xz_dec_end)*>;
4040

4141
private:
42-
QFile xz_file;
4342
XzDecoderUPtr xz_decoder;
4443
};
4544
} // namespace multipass

src/daemon/default_vm_image_vault.cpp

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <multipass/exceptions/create_image_exception.h>
2222
#include <multipass/exceptions/image_vault_exceptions.h>
2323
#include <multipass/exceptions/unsupported_image_exception.h>
24+
#include <multipass/file_ops.h>
2425
#include <multipass/json_utils.h>
2526
#include <multipass/logging/log.h>
2627
#include <multipass/platform.h>
@@ -30,7 +31,6 @@
3031
#include <multipass/url_downloader.h>
3132
#include <multipass/utils.h>
3233
#include <multipass/vm_image.h>
33-
#include <multipass/xz_image_decoder.h>
3434

3535
#include <multipass/format.h>
3636

@@ -170,7 +170,10 @@ void remove_source_images(const mp::VMImage& source_image, const mp::VMImage& pr
170170
{
171171
// The prepare phase may have been a no-op, check and only remove source images
172172
if (source_image.image_path != prepared_image.image_path)
173-
mp::vault::delete_file(source_image.image_path);
173+
{
174+
QFile source_file{source_image.image_path};
175+
MP_FILEOPS.remove(source_file);
176+
}
174177
}
175178

176179
void delete_image_dir(const mp::Path& image_path)
@@ -295,7 +298,7 @@ mp::VMImage mp::DefaultVMImageVault::fetch_image(const FetchType& fetch_type,
295298
}
296299

297300
vm_image = prepare(source_image);
298-
vm_image.id = mp::vault::compute_image_hash(vm_image.image_path).toStdString();
301+
vm_image.id = MP_IMAGE_VAULT_UTILS.compute_file_hash(vm_image.image_path).toStdString();
299302

300303
remove_source_images(source_image, vm_image);
301304

@@ -356,7 +359,8 @@ mp::VMImage mp::DefaultVMImageVault::fetch_image(const FetchType& fetch_type,
356359
last_modified.toString(),
357360
0,
358361
checksum.has_value()};
359-
const auto image_filename = mp::vault::filename_for(image_url.path());
362+
363+
const auto image_filename = QFileInfo{image_url.path()}.fileName();
360364
// Attempt to make a sane directory name based on the filename of the image
361365

362366
const auto image_dir_name =
@@ -620,8 +624,10 @@ mp::VMImage mp::DefaultVMImageVault::download_and_prepare_source_image(
620624
}
621625
else
622626
{
627+
QFileInfo file_info{info.image_location};
628+
623629
source_image.id = id.toStdString();
624-
source_image.image_path = image_dir.filePath(mp::vault::filename_for(info.image_location));
630+
source_image.image_path = image_dir.filePath(file_info.fileName());
625631
source_image.original_release = info.release_title.toStdString();
626632
source_image.release_date = info.version.toStdString();
627633

@@ -642,12 +648,12 @@ mp::VMImage mp::DefaultVMImageVault::download_and_prepare_source_image(
642648
{
643649
mpl::log(mpl::Level::debug, category, fmt::format("Verifying hash \"{}\"", id));
644650
monitor(LaunchProgress::VERIFY, -1);
645-
mp::vault::verify_image_download(source_image.image_path, id);
651+
MP_IMAGE_VAULT_UTILS.verify_file_hash(source_image.image_path, id);
646652
}
647653

648654
if (source_image.image_path.endsWith(".xz"))
649655
{
650-
source_image.image_path = mp::vault::extract_image(source_image.image_path, monitor, true);
656+
source_image.image_path = MP_IMAGE_VAULT_UTILS.extract_file(source_image.image_path, monitor, true);
651657
}
652658

653659
auto prepared_image = prepare(source_image);
@@ -674,14 +680,14 @@ QString mp::DefaultVMImageVault::extract_image_from(const VMImage& source_image,
674680
const auto image_name = file_info.fileName().remove(".xz");
675681
const auto image_path = QDir(dest_dir).filePath(image_name);
676682

677-
return mp::vault::extract_image(image_path, monitor);
683+
return MP_IMAGE_VAULT_UTILS.extract_file(image_path, monitor);
678684
}
679685

680686
mp::VMImage mp::DefaultVMImageVault::image_instance_from(const VMImage& prepared_image, const mp::Path& dest_dir)
681687
{
682688
MP_UTILS.make_dir(dest_dir);
683689

684-
return {mp::vault::copy(prepared_image.image_path, dest_dir),
690+
return {MP_IMAGE_VAULT_UTILS.copy_to_dir(prepared_image.image_path, dest_dir),
685691
prepared_image.id,
686692
prepared_image.original_release,
687693
prepared_image.current_release,

src/platform/backends/lxd/lxd_vm_image_vault.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include <QTemporaryDir>
4848

4949
#include <chrono>
50+
#include <multipass/file_ops.h>
5051
#include <thread>
5152

5253
namespace mp = multipass;
@@ -82,15 +83,16 @@ QString post_process_downloaded_image(const QString& image_path, const mp::Progr
8283

8384
if (image_path.endsWith(".xz"))
8485
{
85-
new_image_path = mp::vault::extract_image(image_path, monitor, true);
86+
new_image_path = MP_IMAGE_VAULT_UTILS.extract_file(image_path, monitor, true);
8687
}
8788

8889
QString original_image_path{new_image_path};
8990
new_image_path = mp::backend::convert_to_qcow_if_necessary(new_image_path);
9091

9192
if (original_image_path != new_image_path)
9293
{
93-
mp::vault::delete_file(original_image_path);
94+
QFile original_file{original_image_path};
95+
MP_FILEOPS.remove(original_file);
9496
}
9597

9698
return new_image_path;
@@ -261,7 +263,7 @@ mp::VMImage mp::LXDVMImageVault::fetch_image(const FetchType& fetch_type,
261263
throw std::runtime_error(fmt::format("Custom image `{}` does not exist.", image_url.path()));
262264

263265
source_image.image_path = image_url.path();
264-
id = mp::vault::compute_image_hash(source_image.image_path);
266+
id = MP_IMAGE_VAULT_UTILS.compute_file_hash(source_image.image_path);
265267
last_modified = QDateTime::currentDateTime();
266268
}
267269

@@ -305,13 +307,13 @@ mp::VMImage mp::LXDVMImageVault::fetch_image(const FetchType& fetch_type,
305307
if (query.query_type != Query::Type::LocalFile)
306308
{
307309
// TODO: Need to make this async like in DefaultVMImageVault
308-
image_path = lxd_import_dir.filePath(mp::vault::filename_for(info.image_location));
310+
image_path = lxd_import_dir.filePath(QFileInfo{info.image_location}.fileName());
309311

310312
url_download_image(info, image_path, monitor);
311313
}
312314
else
313315
{
314-
image_path = mp::vault::copy(source_image.image_path, lxd_import_dir.path());
316+
image_path = MP_IMAGE_VAULT_UTILS.copy_to_dir(source_image.image_path, lxd_import_dir.path());
315317
}
316318

317319
image_path = post_process_downloaded_image(image_path, monitor);
@@ -522,7 +524,7 @@ void mp::LXDVMImageVault::url_download_image(const VMImageInfo& info, const QStr
522524
if (info.verify)
523525
{
524526
monitor(LaunchProgress::VERIFY, -1);
525-
mp::vault::verify_image_download(image_path, info.id);
527+
MP_IMAGE_VAULT_UTILS.verify_file_hash(image_path, info.id);
526528
}
527529
}
528530

src/platform/backends/shared/base_vm_image_vault.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <multipass/vm_image.h>
2424
#include <multipass/vm_image_host.h>
2525
#include <multipass/vm_image_vault.h>
26+
#include <multipass/vm_image_vault_utils.h>
2627

2728
#include <string>
2829
#include <utility>
@@ -34,7 +35,8 @@ class BaseVMImageVault : public VMImageVault
3435
{
3536
public:
3637
explicit BaseVMImageVault(const std::vector<VMImageHost*>& image_hosts)
37-
: image_hosts{image_hosts}, remote_image_host_map{vault::configure_image_host_map(image_hosts)} {};
38+
: image_hosts{image_hosts},
39+
remote_image_host_map{MP_IMAGE_VAULT_UTILS.configure_image_host_map(image_hosts)} {};
3840

3941
VMImageHost* image_host_for(const std::string& remote_name) const override
4042
{

src/utils/file_ops.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,11 @@ bool mp::FileOps::flush(QFile& file) const
170170
return file.flush();
171171
}
172172

173+
bool mp::FileOps::copy(const QString& from, const QString& to) const
174+
{
175+
return QFile::copy(from, to);
176+
}
177+
173178
bool mp::FileOps::commit(QSaveFile& file) const
174179
{
175180
return file.commit();
@@ -292,3 +297,8 @@ fs::path mp::FileOps::weakly_canonical(const fs::path& path) const
292297
{
293298
return fs::weakly_canonical(path);
294299
}
300+
301+
fs::path mp::FileOps::remove_extension(const fs::path& path) const
302+
{
303+
return path.parent_path() / path.stem();
304+
}

src/utils/utils.cpp

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -401,16 +401,6 @@ void mp::utils::validate_server_address(const std::string& address)
401401
throw std::runtime_error(fmt::format("invalid port number in address '{}'", address));
402402
}
403403

404-
std::string mp::utils::filename_for(const std::string& path)
405-
{
406-
return QFileInfo(QString::fromStdString(path)).fileName().toStdString();
407-
}
408-
409-
bool mp::utils::is_dir(const std::string& path)
410-
{
411-
return QFileInfo(QString::fromStdString(path)).isDir();
412-
}
413-
414404
std::string mp::utils::match_line_for(const std::string& output, const std::string& matcher)
415405
{
416406
std::istringstream ss{output};

0 commit comments

Comments
 (0)