From 8e3a61e020cd9973183087bfa906a7b9379d98ba Mon Sep 17 00:00:00 2001 From: "bors[bot]" <26634292+bors[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2020 15:57:59 +0000 Subject: [PATCH] Merge #1580 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1580: [streams] don't die on missing products r=townsend2010,ricab a=Saviq If products are missing, don't fail, but log info instead. It may be a valid situation on LXD at the moment. Co-authored-by: MichaƂ Sawicz --- .../exceptions/manifest_exceptions.h | 41 +++++++++++++++++++ src/daemon/common_image_host.cpp | 6 +++ src/daemon/common_image_host.h | 1 + src/daemon/ubuntu_image_host.cpp | 9 ++++ src/simplestreams/simple_streams_manifest.cpp | 11 ++--- tests/test_simple_streams_manifest.cpp | 11 ++--- 6 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 include/multipass/exceptions/manifest_exceptions.h diff --git a/include/multipass/exceptions/manifest_exceptions.h b/include/multipass/exceptions/manifest_exceptions.h new file mode 100644 index 0000000000..695101f5f2 --- /dev/null +++ b/include/multipass/exceptions/manifest_exceptions.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2020 Canonical, Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef MULTIPASS_MANIFEST_EXCEPTIONS_H +#define MULTIPASS_MANIFEST_EXCEPTIONS_H + +#include + +namespace multipass +{ +class GenericManifestException : public std::runtime_error +{ +public: + GenericManifestException(const std::string& details) : runtime_error(details) + { + } +}; + +class EmptyManifestException : public GenericManifestException +{ +public: + EmptyManifestException(const std::string& details) : GenericManifestException(details) + { + } +}; +} // namespace multipass +#endif // MULTIPASS_MANIFEST_EXCEPTIONS_H diff --git a/src/daemon/common_image_host.cpp b/src/daemon/common_image_host.cpp index 5c461c7768..6090db427f 100644 --- a/src/daemon/common_image_host.cpp +++ b/src/daemon/common_image_host.cpp @@ -77,6 +77,12 @@ void mp::CommonVMImageHost::update_manifests() } } +void mp::CommonVMImageHost::on_manifest_empty(const std::string& details) +{ + need_extra_update = true; + mpl::log(mpl::Level::info, category, details); +} + void mp::CommonVMImageHost::on_manifest_update_failure(const std::string& details) { need_extra_update = true; diff --git a/src/daemon/common_image_host.h b/src/daemon/common_image_host.h index 2d64a259ec..ee683cc661 100644 --- a/src/daemon/common_image_host.h +++ b/src/daemon/common_image_host.h @@ -37,6 +37,7 @@ class CommonVMImageHost : public VMImageHost protected: void update_manifests(); void on_manifest_update_failure(const std::string& details); + void on_manifest_empty(const std::string& details); virtual void for_each_entry_do_impl(const Action& action) = 0; virtual VMImageInfo info_for_full_hash_impl(const std::string& full_hash) = 0; diff --git a/src/daemon/ubuntu_image_host.cpp b/src/daemon/ubuntu_image_host.cpp index cd6d9cc060..1e8b2bf656 100644 --- a/src/daemon/ubuntu_image_host.cpp +++ b/src/daemon/ubuntu_image_host.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -229,6 +230,14 @@ void mp::UbuntuVMImageHost::fetch_manifests() manifests.emplace_back( std::make_pair(remote.first, download_manifest(QString::fromStdString(remote.second), url_downloader))); } + catch (mp::EmptyManifestException& /* e */) + { + on_manifest_empty(fmt::format("Did not find any supported products in \"{}\"", remote.first)); + } + catch (mp::GenericManifestException& e) + { + on_manifest_update_failure(e.what()); + } catch (mp::DownloadException& e) { on_manifest_update_failure(e.what()); diff --git a/src/simplestreams/simple_streams_manifest.cpp b/src/simplestreams/simple_streams_manifest.cpp index 800b022ea9..f2b7b510a0 100644 --- a/src/simplestreams/simple_streams_manifest.cpp +++ b/src/simplestreams/simple_streams_manifest.cpp @@ -23,6 +23,7 @@ #include #include +#include #include namespace mp = multipass; @@ -38,10 +39,10 @@ QJsonObject parse_manifest(const QByteArray& json) QJsonParseError parse_error; const auto doc = QJsonDocument::fromJson(json, &parse_error); if (doc.isNull()) - throw std::runtime_error(parse_error.errorString().toStdString()); + throw mp::GenericManifestException(parse_error.errorString().toStdString()); if (!doc.isObject()) - throw std::runtime_error("invalid manifest object"); + throw mp::GenericManifestException("invalid manifest object"); return doc.object(); } @@ -81,12 +82,12 @@ std::unique_ptr mp::SimpleStreamsManifest::fromJson(c const auto manifest_products = manifest["products"].toObject(); if (manifest_products.isEmpty()) - throw std::runtime_error("No products found"); + throw mp::GenericManifestException("No products found"); auto arch = arch_to_manifest.value(QSysInfo::currentCpuArchitecture()); if (arch.isEmpty()) - throw std::runtime_error("Unsupported cloud image architecture"); + throw mp::GenericManifestException("Unsupported cloud image architecture"); std::vector products; for (const auto& value : manifest_products) @@ -155,7 +156,7 @@ std::unique_ptr mp::SimpleStreamsManifest::fromJson(c } if (products.empty()) - throw std::runtime_error("failed to parse any products"); + throw mp::EmptyManifestException("No supported products found."); QMap map; diff --git a/tests/test_simple_streams_manifest.cpp b/tests/test_simple_streams_manifest.cpp index 5a79a232e0..2dd168c022 100644 --- a/tests/test_simple_streams_manifest.cpp +++ b/tests/test_simple_streams_manifest.cpp @@ -19,6 +19,7 @@ #include "mock_settings.h" #include +#include #include #include @@ -61,28 +62,28 @@ TEST(SimpleStreamsManifest, can_find_info_by_alias) TEST(SimpleStreamsManifest, throws_on_invalid_json) { QByteArray json; - EXPECT_THROW(mp::SimpleStreamsManifest::fromJson(json, ""), std::runtime_error); + EXPECT_THROW(mp::SimpleStreamsManifest::fromJson(json, ""), mp::GenericManifestException); } TEST(SimpleStreamsManifest, throws_on_invalid_top_level_type) { auto json = mpt::load_test_file("invalid_top_level.json"); - EXPECT_THROW(mp::SimpleStreamsManifest::fromJson(json, ""), std::runtime_error); + EXPECT_THROW(mp::SimpleStreamsManifest::fromJson(json, ""), mp::GenericManifestException); } TEST(SimpleStreamsManifest, throws_when_missing_products) { auto json = mpt::load_test_file("missing_products_manifest.json"); - EXPECT_THROW(mp::SimpleStreamsManifest::fromJson(json, ""), std::runtime_error); + EXPECT_THROW(mp::SimpleStreamsManifest::fromJson(json, ""), mp::GenericManifestException); } TEST(SimpleStreamsManifest, throws_when_failed_to_parse_any_products) { auto json = mpt::load_test_file("missing_versions_manifest.json"); - EXPECT_THROW(mp::SimpleStreamsManifest::fromJson(json, ""), std::runtime_error); + EXPECT_THROW(mp::SimpleStreamsManifest::fromJson(json, ""), mp::EmptyManifestException); json = mpt::load_test_file("missing_versions_manifest.json"); - EXPECT_THROW(mp::SimpleStreamsManifest::fromJson(json, ""), std::runtime_error); + EXPECT_THROW(mp::SimpleStreamsManifest::fromJson(json, ""), mp::EmptyManifestException); } TEST(SimpleStreamsManifest, chooses_newest_version)