aboutsummaryrefslogtreecommitdiff
path: root/load
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2024-05-21 13:48:43 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2024-05-21 13:50:10 +0300
commitbad54b28bcc59fe5d19ecaf486f52e6359009e68 (patch)
tree4dcc9c10413d46bf9f506863b6f154ee5998e469 /load
parentc4798955364d3a5e4074e56a0038148837c75d82 (diff)
Add support for build_unloaded() notification for tenant-associated services
Diffstat (limited to 'load')
-rw-r--r--load/load.cli7
-rw-r--r--load/load.cxx155
2 files changed, 131 insertions, 31 deletions
diff --git a/load/load.cli b/load/load.cli
index 99d76f6..2061c26 100644
--- a/load/load.cli
+++ b/load/load.cli
@@ -72,6 +72,13 @@ class options
specified, then the single-tenant mode is assumed."
};
+ bool --existing-tenant
+ {
+ "Load the repository and package information into the already 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..765cf43 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.existing_tenant ())
+ {
+ cerr << "error: --existing-tenant 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<tenant> t; // Not NULL in the --existing-tenant 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 --existing-tenant mode, the specified one.
//
if (tnt.empty ()) // Single-tenant mode.
{
@@ -1770,7 +1785,49 @@ 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 --existing-tenant mode make sure that the specified tenant
+ // exists, is not archived, not marked as unloaded, and is
+ // empty. Otherwise (not in the --existing-tenant mode), remove this
+ // tenant.
+ //
+ if (ops.existing_tenant ())
+ {
+ t = db.find<tenant> (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<repository_count> (
+ query<repository_count>::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<package> (
query<package>::id.tenant.in_range (ts.begin (), ts.end ()));
@@ -1785,32 +1842,68 @@ try
query<tenant>::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 --existing-tenant mode make sure that the specified service
+ // matches the service associated with the pre-created tenant and update
+ // the service data, if specified.
//
optional<tenant_service> service;
if (ops.service_id_specified ())
+ {
service = tenant_service (ops.service_id (),
ops.service_type (),
(ops.service_data_specified ()
? ops.service_data ()
: optional<string> ()));
- db.persist (tenant (tnt,
- ops.private_ (),
- (ops.interactive_specified ()
- ? ops.interactive ()
- : optional<string> ()),
- move (service)));
+ if (ops.existing_tenant ())
+ {
+ 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.existing_tenant ())
+ db.persist (tenant (tnt,
+ ops.private_ (),
+ (ops.interactive_specified ()
+ ? ops.interactive ()
+ : optional<string> ()),
+ move (service)));
// On the first pass over the internal repositories we load their
// certificate information and packages.