From 0185884ca8fb4f36eef91eced541c19d5f6717bc Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 16 Oct 2018 09:56:56 +0100 Subject: [PATCH] Catalogs: allow migration to alternate IDs --- simgear/package/Catalog.cxx | 88 +++++++++--- simgear/package/Catalog.hxx | 2 + simgear/package/CatalogTest.cxx | 126 +++++++++++++++++ simgear/package/Root.cxx | 13 +- simgear/package/Root.hxx | 2 + simgear/package/catalogTest1/catalog-alt.xml | 141 +++++++++++++++++++ simgear/package/catalogTest1/catalog-v2.xml | 6 + 7 files changed, 358 insertions(+), 20 deletions(-) create mode 100644 simgear/package/catalogTest1/catalog-alt.xml diff --git a/simgear/package/Catalog.cxx b/simgear/package/Catalog.cxx index de74d19d..5092b888 100644 --- a/simgear/package/Catalog.cxx +++ b/simgear/package/Catalog.cxx @@ -81,17 +81,27 @@ bool checkVersion(const std::string& aVersion, SGPropertyNode_ptr props) } return false; } + +SGPropertyNode_ptr alternateForVersion(const std::string& aVersion, SGPropertyNode_ptr props) +{ + for (auto v : props->getChildren("alternate-version")) { + for (auto versionSpec : v->getChildren("version")) { + if (checkVersionString(aVersion, versionSpec->getStringValue())) { + return v; + } + } + } + + return {}; +} std::string redirectUrlForVersion(const std::string& aVersion, SGPropertyNode_ptr props) { - for (SGPropertyNode* v : props->getChildren("alternate-version")) { - std::string s(v->getStringValue("version")); - if (checkVersionString(aVersion, s)) { - return v->getStringValue("url");; - } - } + auto node = alternateForVersion(aVersion, props); + if (node) + return node->getStringValue("url");; - return std::string(); + return {}; } ////////////////////////////////////////////////////////////////////////////// @@ -139,19 +149,15 @@ protected: std::string ver(m_owner->root()->applicationVersion()); if (!checkVersion(ver, props)) { - SG_LOG(SG_GENERAL, SG_WARN, "downloaded catalog " << m_owner->url() << ", but version required " << ver); - // check for a version redirect entry - std::string url = redirectUrlForVersion(ver, props); - if (!url.empty()) { - SG_LOG(SG_GENERAL, SG_WARN, "redirecting from " << m_owner->url() << - " to \n\t" << url); - - // update the URL and kick off a new request - m_owner->setUrl(url); - Downloader* dl = new Downloader(m_owner, url); - m_owner->root()->makeHTTPRequest(dl); + auto alt = alternateForVersion(ver, props); + if (alt) { + SG_LOG(SG_GENERAL, SG_WARN, "have alternate version of package at:" + << alt->getStringValue("url")); + m_owner->processAlternate(alt); } else { + SG_LOG(SG_GENERAL, SG_WARN, "downloaded catalog " << m_owner->url() + << ", but app version " << ver << " is not comaptible"); m_owner->refreshComplete(Delegate::FAIL_VERSION); } @@ -228,7 +234,7 @@ CatalogRef Catalog::createFromPath(Root* aRoot, const SGPath& aPath) bool versionCheckOk = checkVersion(aRoot->applicationVersion(), props); if (!versionCheckOk) { - SG_LOG(SG_GENERAL, SG_INFO, "catalog at:" << aPath << " failed version check: need version: " + SG_LOG(SG_GENERAL, SG_INFO, "catalog at:" << aPath << " failed version check: app version: " << aRoot->applicationVersion()); // keep the catalog but mark it as needing an update } else { @@ -598,6 +604,50 @@ bool Catalog::isEnabled() const } } +void Catalog::processAlternate(SGPropertyNode_ptr alt) +{ + std::string altId; + const auto idPtr = alt->getStringValue("id"); + if (idPtr) { + altId = std::string(idPtr); + } + + std::string altUrl; + if (alt->getStringValue("url")) { + altUrl = std::string(alt->getStringValue("url")); + } + + CatalogRef existing; + if (!altId.empty()) { + existing = root()->getCatalogById(altId); + } else { + existing = root()->getCatalogByUrl(altUrl); + } + + if (existing && (existing != this)) { + // we already have the alternate, so just go quiet here + changeStatus(Delegate::FAIL_VERSION); + return; + } + + // we have an alternate ID, and it's differnt from our ID, so let's + // define a new catalog + if (!altId.empty()) { + SG_LOG(SG_GENERAL, SG_INFO, "Adding new catalog:" << altId << " as version alternate for " << id()); + // new catalog being added + createFromUrl(root(), altUrl); + + // and we can go idle now + changeStatus(Delegate::FAIL_VERSION); + return; + } + + SG_LOG(SG_GENERAL, SG_INFO, "Migrating catalog " << id() << " to new URL:" << altUrl); + setUrl(altUrl); + Downloader* dl = new Downloader(this, altUrl); + root()->makeHTTPRequest(dl); +} + } // of namespace pkg } // of namespace simgear diff --git a/simgear/package/Catalog.hxx b/simgear/package/Catalog.hxx index c0a6edd8..f300690a 100644 --- a/simgear/package/Catalog.hxx +++ b/simgear/package/Catalog.hxx @@ -179,6 +179,8 @@ private: void changeStatus(Delegate::StatusCode newStatus); + void processAlternate(SGPropertyNode_ptr alt); + Root* m_root; SGPropertyNode_ptr m_props; SGPath m_installRoot; diff --git a/simgear/package/CatalogTest.cxx b/simgear/package/CatalogTest.cxx index d0d1d93a..8b9f3435 100644 --- a/simgear/package/CatalogTest.cxx +++ b/simgear/package/CatalogTest.cxx @@ -741,6 +741,130 @@ void testVersionMigrate(HTTP::Client* cl) } } +void testVersionMigrateToId(HTTP::Client* cl) +{ + global_catalogVersion = 2; // version which has migration info + SGPath rootPath(simgear::Dir::current().path()); + rootPath.append("cat_migrate_version_id"); + simgear::Dir pd(rootPath); + pd.removeChildren(); + + { + pkg::RootRef root(new pkg::Root(rootPath, "8.1.2")); + root->setHTTPClient(cl); + + pkg::CatalogRef c = pkg::Catalog::createFromUrl(root.ptr(), "http://localhost:2000/catalogTest1/catalog.xml"); + waitForUpdateComplete(cl, root); + SG_VERIFY(c->isEnabled()); + + // install a package + pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG"); + SG_CHECK_EQUAL(p1->id(), "b737-NG"); + pkg::InstallRef ins = p1->install(); + SG_VERIFY(ins->isQueued()); + waitForUpdateComplete(cl, root); + SG_VERIFY(p1->isInstalled()); + } + + // change version to an alternate one + { + pkg::RootRef root(new pkg::Root(rootPath, "7.5")); + root->setHTTPClient(cl); + + // this should cause the alternate package to be loaded + root->refresh(true); + waitForUpdateComplete(cl, root); + + pkg::CatalogRef cat = root->getCatalogById("org.flightgear.test.catalog1"); + SG_VERIFY(!cat->isEnabled()); + SG_CHECK_EQUAL(cat->status(), pkg::Delegate::FAIL_VERSION); + SG_CHECK_EQUAL(cat->id(), "org.flightgear.test.catalog1"); + SG_CHECK_EQUAL(cat->url(), "http://localhost:2000/catalogTest1/catalog.xml"); + + auto enabledCats = root->catalogs(); + auto it = std::find(enabledCats.begin(), enabledCats.end(), cat); + SG_VERIFY(it == enabledCats.end()); + + // ensure existing package is still installed + pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG"); + SG_VERIFY(p1 != pkg::PackageRef()); + SG_CHECK_EQUAL(p1->id(), "b737-NG"); + + // but not listed + auto packs = root->allPackages(); + auto k = std::find(packs.begin(), packs.end(), p1); + SG_VERIFY(k == packs.end()); + + // check the new catalog + auto altCat = root->getCatalogById("org.flightgear.test.catalog-alt"); + SG_VERIFY(altCat->isEnabled()); + SG_CHECK_EQUAL(altCat->status(), pkg::Delegate::STATUS_REFRESHED); + SG_CHECK_EQUAL(altCat->id(), "org.flightgear.test.catalog-alt"); + SG_CHECK_EQUAL(altCat->url(), "http://localhost:2000/catalogTest1/catalog-alt.xml"); + + it = std::find(enabledCats.begin(), enabledCats.end(), altCat); + SG_VERIFY(it != enabledCats.end()); + + // install a parallel package from the new catalog + pkg::PackageRef p2 = root->getPackageById("org.flightgear.test.catalog-alt.b737-NG"); + SG_CHECK_EQUAL(p2->id(), "b737-NG"); + pkg::InstallRef ins = p2->install(); + SG_VERIFY(ins->isQueued()); + waitForUpdateComplete(cl, root); + SG_VERIFY(p2->isInstalled()); + + // do a non-scoped lookup, we should get the new one + pkg::PackageRef p3 = root->getPackageById("b737-NG"); + SG_CHECK_EQUAL(p2, p3); + } + + // test that re-init-ing doesn't mirgate again + { + pkg::RootRef root(new pkg::Root(rootPath, "7.5")); + root->setHTTPClient(cl); + + root->refresh(true); + waitForUpdateComplete(cl, root); + + pkg::CatalogRef cat = root->getCatalogById("org.flightgear.test.catalog1"); + SG_VERIFY(!cat->isEnabled()); + SG_CHECK_EQUAL(cat->status(), pkg::Delegate::FAIL_VERSION); + + auto altCat = root->getCatalogById("org.flightgear.test.catalog-alt"); + SG_VERIFY(altCat->isEnabled()); + + auto packs = root->allPackages(); + SG_CHECK_EQUAL(packs.size(), 4); + } + + // and now switch back to the older version + { + pkg::RootRef root(new pkg::Root(rootPath, "8.1.0")); + root->setHTTPClient(cl); + root->refresh(true); + waitForUpdateComplete(cl, root); + + pkg::CatalogRef cat = root->getCatalogById("org.flightgear.test.catalog1"); + SG_VERIFY(cat->isEnabled()); + SG_CHECK_EQUAL(cat->status(), pkg::Delegate::STATUS_REFRESHED); + + auto altCat = root->getCatalogById("org.flightgear.test.catalog-alt"); + SG_VERIFY(!altCat->isEnabled()); + + // verify the original aircraft is still installed and available + pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG"); + SG_VERIFY(p1 != pkg::PackageRef()); + SG_CHECK_EQUAL(p1->id(), "b737-NG"); + SG_VERIFY(p1->isInstalled()); + + // verify the alt package is still installed, + pkg::PackageRef p2 = root->getPackageById("org.flightgear.test.catalog-alt.b737-NG"); + SG_VERIFY(p2 != pkg::PackageRef()); + SG_CHECK_EQUAL(p2->id(), "b737-NG"); + SG_VERIFY(p2->isInstalled()); + } +} + void testOfflineMode(HTTP::Client* cl) { global_catalogVersion = 0; @@ -990,6 +1114,8 @@ int main(int argc, char* argv[]) removeInvalidCatalog(&cl); + testVersionMigrateToId(&cl); + SG_LOG(SG_GENERAL, SG_INFO, "Successfully passed all tests!"); return EXIT_SUCCESS; } diff --git a/simgear/package/Root.cxx b/simgear/package/Root.cxx index b207af2c..ac19af07 100644 --- a/simgear/package/Root.cxx +++ b/simgear/package/Root.cxx @@ -414,7 +414,7 @@ Root::Root(const SGPath& aPath, const std::string& aVersion) : if (cat && cat->isEnabled()) { d->catalogs.insert({cat->id(), cat}); } else if (cat) { - SG_LOG(SG_GENERAL, SG_WARN, "Package-Root init: catalog is disabled: " << cat->id()); + SG_LOG(SG_GENERAL, SG_DEBUG, "Package-Root init: catalog is disabled: " << cat->id()); } } // of child directories iteration } @@ -450,6 +450,17 @@ CatalogRef Root::getCatalogById(const std::string& aId) const return it->second; } +CatalogRef Root::getCatalogByUrl(const std::string& aUrl) const +{ + auto it = std::find_if(d->catalogs.begin(), d->catalogs.end(), + [aUrl](const CatalogDict::value_type& v) + { return (v.second->url() == aUrl); }); + if (it == d->catalogs.end()) + return {}; + + return it->second; +} + PackageRef Root::getPackageById(const std::string& aName) const { size_t lastDot = aName.rfind('.'); diff --git a/simgear/package/Root.hxx b/simgear/package/Root.hxx index 70461915..2c598166 100644 --- a/simgear/package/Root.hxx +++ b/simgear/package/Root.hxx @@ -132,6 +132,8 @@ public: CatalogRef getCatalogById(const std::string& aId) const; + CatalogRef getCatalogByUrl(const std::string& aUrl) const; + void scheduleToUpdate(InstallRef aInstall); /** diff --git a/simgear/package/catalogTest1/catalog-alt.xml b/simgear/package/catalogTest1/catalog-alt.xml new file mode 100644 index 00000000..54d2edc2 --- /dev/null +++ b/simgear/package/catalogTest1/catalog-alt.xml @@ -0,0 +1,141 @@ + + + + org.flightgear.test.catalog-alt + Alternate test catalog + http://localhost:2000/catalogTest1/catalog-alt.xml + 4 + + 7.* + + + + alpha + Alpha package + 9 + 593 + + a469c4b837f0521db48616cfe65ac1ea + http://localhost:2000/catalogTest1/alpha.zip + + alpha + + + + c172p + Cessna 172-P + c172p + A plane made by Cessna + 42 + 860 + + cessna + ga + piston + ifr + + + 3 + 4 + 5 + 4 + + + + + exterior + thumb-exterior.png + http://foo.bar.com/thumb-exterior.png + + + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + + + + thumb-something.png + http://foo.bar.com/thumb-something.png + + + + c172p-2d-panel + C172 with 2d panel only + + + + c172p-floats + C172 with floats + + + exterior + thumb-exterior-floats.png + http://foo.bar.com/thumb-exterior-floats.png + + + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + + + + + c172p-skis + C172 with skis + + + exterior + thumb-exterior-skis.png + http://foo.bar.com/thumb-exterior-skis.png + + + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + + + + ec0e2ffdf98d6a5c05c77445e5447ff5 + http://localhost:2000/catalogTest1/c172p.zip + + + + + b737-NG + Boeing 737 NG + b737NG + A popular twin-engined narrow body jet + 112 + 860 + + boeing + jet + ifr + + + 5 + 5 + 4 + 4 + + + a94ca5704f305b90767f40617d194ed6 + http://localhost:2000/catalogTest1/b737.tar.gz + + + + + dc3 + DC-3 + 9 + 593 + + a469c4b837f0521db48616cfe65ac1ea + http://localhost:2000/catalogTest1/alpha.zip + + dc3 + + diff --git a/simgear/package/catalogTest1/catalog-v2.xml b/simgear/package/catalogTest1/catalog-v2.xml index 75a19c57..fa416115 100644 --- a/simgear/package/catalogTest1/catalog-v2.xml +++ b/simgear/package/catalogTest1/catalog-v2.xml @@ -15,6 +15,12 @@ http://localhost:2000/catalogTest1/catalog-v10.xml + + 7.* + org.flightgear.test.catalog-alt + http://localhost:2000/catalogTest1/catalog-alt.xml + + alpha Alpha package