From 57f6378aea619ceb07b935012772bd4698e6a2be Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 17 May 2024 17:27:44 +0300 Subject: Add support for build_unloaded() notification for tenant-associated services --- load/load.cli | 7 +++ load/load.cxx | 154 ++++++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 130 insertions(+), 31 deletions(-) (limited to 'load') diff --git a/load/load.cli b/load/load.cli index 99d76f6..0defa8a 100644 --- a/load/load.cli +++ b/load/load.cli @@ -72,6 +72,13 @@ class options specified, then the single-tenant mode is assumed." }; + bool --tenant-load + { + "Load the repository and package information into the pre-created empty + tenant rather than into the newly created one. Requires the \cb{--tenant} + option to be specified." + }; + bool --private { "Display the tenant packages in the web interface only in the tenant view diff --git a/load/load.cxx b/load/load.cxx index 474b443..e338cb6 100644 --- a/load/load.cxx +++ b/load/load.cxx @@ -1643,11 +1643,23 @@ try // const string& tnt (ops.tenant ()); - if (ops.tenant_specified () && tnt.empty ()) + if (ops.tenant_specified ()) { - cerr << "error: empty tenant" << endl - << help_info << endl; - throw failed (); + if (tnt.empty ()) + { + cerr << "error: empty tenant" << endl + << help_info << endl; + throw failed (); + } + } + else + { + if (ops.tenant_load ()) + { + cerr << "error: --tenant-load requires --tenant" << endl + << help_info << endl; + throw failed (); + } } // Verify the --service-* options. @@ -1656,14 +1668,15 @@ try { if (!ops.tenant_specified ()) { - cerr << "error: --service-id requires --tenant" << endl; + cerr << "error: --service-id requires --tenant" << endl + << help_info << endl; throw failed (); } if (ops.service_type ().empty ()) { - cerr << "error: --service-id requires --service-type" - << endl; + cerr << "error: --service-id requires --service-type" << endl + << help_info << endl; throw failed (); } } @@ -1671,15 +1684,15 @@ try { if (ops.service_type_specified ()) { - cerr << "error: --service-type requires --service-id" - << endl; + cerr << "error: --service-type requires --service-id" << endl + << help_info << endl; throw failed (); } if (ops.service_data_specified ()) { - cerr << "error: --service-data requires --service-id" - << endl; + cerr << "error: --service-data requires --service-id" << endl + << help_info << endl; throw failed (); } } @@ -1753,13 +1766,15 @@ try if (ops.force () || changed (tnt, irs, db)) { + shared_ptr t; // Not NULL in the --tenant-load mode. + // Rebuild repositories persistent state from scratch. // // 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. + // multi-tenant mode all tenants, excluding the pre-created ones, must be + // non-empty. So in the single-tenant mode we erase all database objects + // (possibly from multiple tenants). Otherwise, cleanup the empty tenant + // and, unless in the --tenant-load mode, the specified one. // if (tnt.empty ()) // Single-tenant mode. { @@ -1770,7 +1785,48 @@ try } else // Multi-tenant mode. { - cstrings ts ({tnt.c_str (), ""}); + // NOTE: don't forget to update ci_start::create() if changing anything + // here. + // + cstrings ts ({""}); + + // In the --tenant-load mode make sure that the specified tenant exists, + // is not archived, not marked as unloaded, and is empty. Otherwise (not + // in the --tenant-load mode), remove this tenant. + // + if (ops.tenant_load ()) + { + t = db.find (tnt); + + if (t == nullptr) + { + cerr << "error: unable to find tenant " << tnt << endl; + throw failed (); + } + + if (t->archived) + { + cerr << "error: tenant " << tnt << " is archived" << endl; + throw failed (); + } + + if (t->loaded_timestamp) + { + cerr << "error: tenant " << tnt << " is marked as unloaded" << endl; + throw failed (); + } + + size_t n (db.query_value ( + query::id.tenant == tnt)); + + if (n != 0) + { + cerr << "error: tenant " << tnt << " is not empty" << endl; + throw failed (); + } + } + else + ts.push_back (tnt.c_str ()); db.erase_query ( query::id.tenant.in_range (ts.begin (), ts.end ())); @@ -1785,32 +1841,68 @@ try query::id.in_range (ts.begin (), ts.end ())); } - // Persist the tenant. + // Craft the tenant service object from the --service-* options. // - // Note that if the tenant service is specified and some tenant with the - // same service id and type is already persisted, then we will end up with - // the `object already persistent` error and terminate with the exit code - // 1 (fatal error). We could potentially dedicate a special exit code for - // such a case, so that the caller may recognize it and behave accordingly - // (CI request handler can treat it as a client error rather than an - // internal error, etc). However, let's first see if it ever becomes a - // problem. + // In the --tenant-load mode make sure that the specified service matches + // the service associated with the pre-created tenant and update the + // service data, if specified. // optional service; if (ops.service_id_specified ()) + { service = tenant_service (ops.service_id (), ops.service_type (), (ops.service_data_specified () ? ops.service_data () : optional ())); - db.persist (tenant (tnt, - ops.private_ (), - (ops.interactive_specified () - ? ops.interactive () - : optional ()), - move (service))); + if (ops.tenant_load ()) + { + assert (t != nullptr); + + if (!t->service) + { + cerr << "error: no service associated with tenant " << tnt << endl; + throw failed (); + } + + if (t->service->id != service->id || t->service->type != service->type) + { + cerr << "error: associated service mismatch for tenant " << tnt << endl << + " info: specified service: " << service->id << ' ' + << service->type << endl << + " info: associated service: " << t->service->id << ' ' + << t->service->type << endl; + throw failed (); + } + + if (service->data) + { + t->service->data = move (service->data); + db.update (t); + } + } + } + + // Persist the tenant. + // + // Note that if the tenant service is specified and some tenant with the + // same service id and type is already persisted, then we will end up with + // the `object already persistent` error and terminate with the exit code + // 1 (fatal error). We could potentially dedicate a special exit code for + // such a case, so that the caller may recognize it and behave accordingly + // (CI request handler can treat it as a client error rather than an + // internal error, etc). However, let's first see if it ever becomes a + // problem. + // + if (!ops.tenant_load ()) + db.persist (tenant (tnt, + ops.private_ (), + (ops.interactive_specified () + ? ops.interactive () + : optional ()), + move (service))); // On the first pass over the internal repositories we load their // certificate information and packages. -- cgit v1.1