From 70c1cdfd8f34472761fe5ec97f0713990c1b4f5b Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Wed, 5 Sep 2018 21:23:41 +0300 Subject: Add multi-tenancy support --- load/load.cli | 6 +++++ load/load.cxx | 84 +++++++++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 71 insertions(+), 19 deletions(-) (limited to 'load') diff --git a/load/load.cli b/load/load.cli index bbe54ff..62c3204 100644 --- a/load/load.cli +++ b/load/load.cli @@ -52,6 +52,12 @@ class options repositories." }; + std::string --tenant + { + "Tenant the package and repository information should be loaded in. If not + specified, then the single-tenant mode is assumed." + }; + std::string --db-user|-u { "", diff --git a/load/load.cxx b/load/load.cxx index 01d77c2..24a3bf3 100644 --- a/load/load.cxx +++ b/load/load.cxx @@ -272,13 +272,16 @@ load_repositories (path p) // package model, including search related objects, settles down. // static bool -changed (const internal_repositories& repos, database& db) +changed (const string& tenant, + const internal_repositories& repos, + database& db) { strings names; for (auto& r: repos) { shared_ptr pr ( - db.find (r.location.canonical_name ())); + db.find (repository_id (tenant, + r.location.canonical_name ()))); if (pr == nullptr || r.location.string () != pr->location.string () || r.display_name != pr->display_name || @@ -298,8 +301,10 @@ changed (const internal_repositories& repos, database& db) // return !db.query ( - query::internal && - !query::name.in_range (names.begin (), names.end ())).empty (); + query::id.tenant == tenant && + query::internal && + !query::id.canonical_name.in_range (names.begin (), + names.end ())).empty (); } // Start 'bpkg rep-info [options] ' process. @@ -388,7 +393,7 @@ load_packages (const shared_ptr& rp, database& db) for (auto& pm: pms) { shared_ptr p ( - db.find (package_id (pm.name, pm.version))); + db.find (package_id (rp->tenant, pm.name, pm.version))); // sha256sum should always be present if the package manifest comes from // the packages.manifest file belonging to the pkg repository. @@ -515,7 +520,7 @@ load_packages (const shared_ptr& rp, database& db) if (!p->sha256sum) p->sha256sum = move (pm.sha256sum); else if (*pm.sha256sum != *p->sha256sum) - cerr << "warning: sha256sum mismatch for package " << p->id.name + cerr << "warning: sha256sum mismatch for package " << p->name << " " << p->version << endl << " info: " << p->internal_repository.load ()->location << " has " << *p->sha256sum << endl @@ -551,9 +556,12 @@ load_repositories (const shared_ptr& rp, // assert (!rp->cache_location.empty ()); + const string& tenant (rp->tenant); + // Repository is already persisted by the load_packages() function call. // - assert (db.find (rp->name) != nullptr); + assert (db.find ( + repository_id (tenant, rp->canonical_name)) != nullptr); pkg_repository_manifests rpm; @@ -680,16 +688,17 @@ load_repositories (const shared_ptr& rp, ? rp->prerequisites : rp->complements); - rs.emplace_back (db, cn); + rs.emplace_back (db, repository_id (tenant, cn)); - shared_ptr pr (db.find (cn)); + shared_ptr pr ( + db.find (repository_id (tenant, cn))); if (pr != nullptr) // The prerequisite repository is already loaded. // continue; - pr = make_shared (move (rl)); + pr = make_shared (tenant, move (rl)); // If the prerequsite repository location is a relative path, then // calculate its cache location. @@ -834,7 +843,7 @@ resolve_dependencies (package& p, database& db) if (d.package == nullptr) { cerr << "error: can't resolve dependency " << d << " of the package " - << p.id.name << " " << p.version << endl + << p.name << " " << p.version << endl << " info: repository " << p.internal_repository.load ()->location << " appears to be broken" << endl; @@ -886,7 +895,7 @@ detect_dependency_cycle (const package_id& id, ? p->internal_repository.load () : p->other_repositories[0].load ()); - cerr << id.name << " " << p->version << " (" << r->name << ")"; + cerr << p->name << " " << p->version << " (" << r->canonical_name << ")"; }; for (; i != chain.end (); ++i) @@ -1089,6 +1098,18 @@ try return 1; } + // By default the tenant is empty and assumes a single-tenant mode. Let's + // require the specified tenant to be non-empty. + // + const string& tenant (ops.tenant ()); + + if (ops.tenant_specified () && tenant.empty ()) + { + cerr << "error: empty tenant" << endl + << help_info << endl; + return 1; + } + odb::pgsql::database db ( ops.db_user (), ops.db_password (), @@ -1119,12 +1140,31 @@ try // internal_repositories irs (load_repositories (path (argv[1]))); - if (ops.force () || changed (irs, db)) + if (ops.force () || changed (tenant, irs, db)) { // Rebuild repositories persistent state from scratch. // - db.erase_query (); - db.erase_query (); + // Note that in the single-tenant mode the tenant must be empty. In the + // multi-tenant mode all tenants must be non-empty. So in the + // single-tenant mode we erase all database objects (possibly from + // multiple tenants). Otherwise, cleanup the specified and the empty + // tenants only. + // + if (tenant.empty ()) // Single-tenant mode. + { + db.erase_query (); + db.erase_query (); + } + else // Multi-tenant mode. + { + cstrings ts ({tenant.c_str (), ""}); + + db.erase_query ( + query::id.tenant.in_range (ts.begin (), ts.end ())); + + db.erase_query ( + query::id.tenant.in_range (ts.begin (), ts.end ())); + } // On the first pass over the internal repositories we load their // certificate information and packages. @@ -1141,7 +1181,8 @@ try ir.fingerprint); shared_ptr r ( - make_shared (ir.location, + make_shared (tenant, + ir.location, move (ir.display_name), move (ir.cache_location), move (cert), @@ -1157,7 +1198,8 @@ try for (const auto& ir: irs) { shared_ptr r ( - db.load (ir.location.canonical_name ())); + db.load (repository_id (tenant, + ir.location.canonical_name ()))); load_repositories (r, db, ops.shallow ()); } @@ -1170,14 +1212,18 @@ try using query = query; for (auto& p: - db.query (query::internal_repository.is_not_null ())) + db.query ( + query::id.tenant == tenant && + query::internal_repository.canonical_name.is_not_null ())) resolve_dependencies (p, db); // Make sure there is no package dependency cycles. // package_ids chain; for (const auto& p: - db.query (query::internal_repository.is_not_null ())) + db.query ( + query::id.tenant == tenant && + query::internal_repository.canonical_name.is_not_null ())) detect_dependency_cycle (p.id, chain, db); } } -- cgit v1.1