aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--brep/handler/ci/ci-load.in22
-rw-r--r--clean/clean.cxx4
-rw-r--r--libbrep/build-extra.sql8
-rw-r--r--libbrep/build-package.hxx46
-rw-r--r--libbrep/build.cxx7
-rw-r--r--libbrep/build.hxx63
-rw-r--r--libbrep/build.xml6
-rw-r--r--libbrep/common.hxx91
-rw-r--r--libbrep/package-extra.sql57
-rw-r--r--libbrep/package.cxx70
-rw-r--r--libbrep/package.hxx40
-rw-r--r--libbrep/package.xml107
-rw-r--r--libbrep/types.hxx2
-rw-r--r--load/load.cli6
-rw-r--r--load/load.cxx84
-rw-r--r--mod/build-config.cxx9
-rw-r--r--mod/mod-build-force.cxx3
-rw-r--r--mod/mod-build-log.cxx18
-rw-r--r--mod/mod-build-result.cxx23
-rw-r--r--mod/mod-build-task.cxx69
-rw-r--r--mod/mod-builds.cxx119
-rw-r--r--mod/mod-ci.cxx2
-rw-r--r--mod/mod-package-details.cxx32
-rw-r--r--mod/mod-package-search.cxx20
-rw-r--r--mod/mod-package-version-details.cxx17
-rw-r--r--mod/mod-repository-details.cxx13
-rw-r--r--mod/mod-repository-root.cxx18
-rw-r--r--mod/mod-submit.cxx2
-rw-r--r--mod/module.hxx10
-rw-r--r--mod/page.cxx31
-rw-r--r--mod/page.hxx59
-rw-r--r--mod/utility.hxx25
-rw-r--r--tests/ci/ci-load.testscript13
-rw-r--r--tests/load/driver.cxx104
34 files changed, 810 insertions, 390 deletions
diff --git a/brep/handler/ci/ci-load.in b/brep/handler/ci/ci-load.in
index da5fb91..ad96943 100644
--- a/brep/handler/ci/ci-load.in
+++ b/brep/handler/ci/ci-load.in
@@ -128,7 +128,7 @@ spec="$spec$repository"
message_suffix=
if [ -n "$result_url" ]; then
- message_suffix=": $result_url"
+ message_suffix=": $result_url/@$reference" # Append the tenant id.
fi
# Exit with the 'CI request is queued' response if simulating.
@@ -169,28 +169,16 @@ display_name="$(sed -r \
loadtab="$data_dir/loadtab"
run echo "$repository $display_name cache:cache" >"$loadtab"
-# Load the repository into the brep package database.
+# Load the repository into the brep package database for the tenant identified
+# by the reference.
#
# Note that for now we load all the packages the repository contains without
# regard to the request manifest package values. Later, we could add filtering
# of the packages.manifest file against the request manifest values. While at
# it, we could also deduce the repository display name (see above). @@ TODO
#
-run "$loader" "${loader_options[@]}" --force --shallow "$loadtab"
-
-# @@ TMP TENANCY: before we add tenancy support we also need to manually
-# cleanup the build database. We will assume the cleanup code to optionally
-# be present in a separate bash script which path is deduced from the
-# handler's own path by adding the -clean suffix.
-#
-cleaner="$0-clean"
-if [ -f "$cleaner" ]; then
-
- # Let's pass the CI request data directory to the cleaner, so it can create
- # temporary files in this directory.
- #
- run "$cleaner" "$data_dir"
-fi
+run "$loader" "${loader_options[@]}" --force --shallow --tenant "$reference" \
+"$loadtab"
# Remove the no longer needed CI request data directory.
#
diff --git a/clean/clean.cxx b/clean/clean.cxx
index 1071123..102b165 100644
--- a/clean/clean.cxx
+++ b/clean/clean.cxx
@@ -134,7 +134,9 @@ try
using prep_bld_query = prepared_query<build>;
size_t offset (0);
- bld_query bq ("ORDER BY" + bld_query::id.package.name +
+ bld_query bq ("ORDER BY" +
+ bld_query::id.package.tenant + "," +
+ bld_query::id.package.name +
order_by_version_desc (bld_query::id.package.version, false) +
"OFFSET" + bld_query::_ref (offset) + "LIMIT 100");
diff --git a/libbrep/build-extra.sql b/libbrep/build-extra.sql
index e0aa92a..cc43239 100644
--- a/libbrep/build-extra.sql
+++ b/libbrep/build-extra.sql
@@ -13,7 +13,8 @@ DROP FOREIGN TABLE IF EXISTS build_repository;
--
--
CREATE FOREIGN TABLE build_repository (
- name TEXT NOT NULL,
+ tenant TEXT NOT NULL,
+ canonical_name TEXT NOT NULL,
location_url TEXT NOT NULL,
location_type TEXT NOT NULL,
certificate_fingerprint TEXT NULL)
@@ -23,6 +24,7 @@ SERVER package_server OPTIONS (table_name 'repository');
--
--
CREATE FOREIGN TABLE build_package (
+ tenant TEXT NOT NULL,
name CITEXT NOT NULL,
version_epoch INTEGER NOT NULL,
version_canonical_upstream TEXT NOT NULL,
@@ -30,7 +32,8 @@ CREATE FOREIGN TABLE build_package (
version_revision INTEGER NOT NULL,
version_upstream TEXT NOT NULL,
version_release TEXT NULL,
- internal_repository TEXT NULL)
+ internal_repository_tenant TEXT NULL,
+ internal_repository_canonical_name TEXT NULL)
SERVER package_server OPTIONS (table_name 'package');
-- The foreign table for the build_package object constraints member (that is
@@ -38,6 +41,7 @@ SERVER package_server OPTIONS (table_name 'package');
--
--
CREATE FOREIGN TABLE build_package_constraints (
+ tenant TEXT NOT NULL,
name CITEXT NOT NULL,
version_epoch INTEGER NOT NULL,
version_canonical_upstream TEXT NOT NULL,
diff --git a/libbrep/build-package.hxx b/libbrep/build-package.hxx
index ca65dbf..0fed500 100644
--- a/libbrep/build-package.hxx
+++ b/libbrep/build-package.hxx
@@ -29,21 +29,25 @@ namespace brep
class build_repository
{
public:
- string name; // Object id (canonical name).
+ repository_id id;
+
+ const string& canonical_name; // Tracks id.canonical_name.
repository_location location;
optional<string> certificate_fingerprint;
// Database mapping.
//
- #pragma db member(name) id
+ #pragma db member(id) id column("")
- #pragma db member(location) \
- set(this.location = std::move (?); \
- assert (this.name == this.location.canonical_name ()))
+ #pragma db member(canonical_name) transient
+
+ #pragma db member(location) \
+ set(this.location = std::move (?); \
+ assert (this.canonical_name == this.location.canonical_name ()))
private:
friend class odb::access;
- build_repository () = default;
+ build_repository (): canonical_name (id.canonical_name) {}
};
// "Foreign" value type that is mapped to a subset of the build_constraint
@@ -85,12 +89,16 @@ namespace brep
// Packages that can potentially be built (internal non-stub).
//
- #pragma db view \
- object(build_package) \
- object(build_repository inner: \
- build_package::internal_repository == build_repository::name && \
- brep::compare_version_ne (build_package::id.version, \
- brep::wildcard_version, \
+ // Note that ADL can't find the equal operator, so we use the function call
+ // notation.
+ //
+ #pragma db view \
+ object(build_package) \
+ object(build_repository inner: \
+ brep::operator== (build_package::internal_repository, \
+ build_repository::id) && \
+ brep::compare_version_ne (build_package::id.version, \
+ brep::wildcard_version, \
false))
struct buildable_package
{
@@ -102,12 +110,13 @@ namespace brep
#pragma db member(version) set(this.version.init (this.id.version, (?)))
};
- #pragma db view \
- object(build_package) \
- object(build_repository inner: \
- build_package::internal_repository == build_repository::name && \
- brep::compare_version_ne (build_package::id.version, \
- brep::wildcard_version, \
+ #pragma db view \
+ object(build_package) \
+ object(build_repository inner: \
+ brep::operator== (build_package::internal_repository, \
+ build_repository::id) && \
+ brep::compare_version_ne (build_package::id.version, \
+ brep::wildcard_version, \
false))
struct buildable_package_count
{
@@ -128,6 +137,7 @@ namespace brep
table("build_package_constraints" = "c") \
object(build_package = package inner: \
"c.exclusion AND " \
+ "c.tenant = " + package::id.tenant + "AND" + \
"c.name = " + package::id.name + "AND" + \
"c.version_epoch = " + package::id.version.epoch + "AND" + \
"c.version_canonical_upstream = " + \
diff --git a/libbrep/build.cxx b/libbrep/build.cxx
index 6ed711c..c4b32d0 100644
--- a/libbrep/build.cxx
+++ b/libbrep/build.cxx
@@ -55,13 +55,16 @@ namespace brep
// build
//
build::
- build (package_name_type pnm, version pvr,
+ build (string tnt,
+ package_name_type pnm,
+ version pvr,
string cfg,
string tnm, version tvr,
optional<string> afp, optional<string> ach,
string mnm, string msm,
butl::target_triplet trg)
- : id (package_id (move (pnm), pvr), move (cfg), tvr),
+ : id (package_id (move (tnt), move (pnm), pvr), move (cfg), tvr),
+ tenant (id.package.tenant),
package_name (id.package.name),
package_version (move (pvr)),
configuration (id.configuration),
diff --git a/libbrep/build.hxx b/libbrep/build.hxx
index c72269c..d3c2051 100644
--- a/libbrep/build.hxx
+++ b/libbrep/build.hxx
@@ -74,19 +74,25 @@ namespace brep
template <typename T>
inline auto
operator== (const T& x, const build_id& y)
- -> decltype (x.package == y.package)
+ -> decltype (x.package == y.package &&
+ x.configuration == y.configuration &&
+ x.toolchain_version.epoch == y.toolchain_version.epoch)
{
- return x.package == y.package && x.configuration == y.configuration &&
- compare_version_eq (x.toolchain_version, y.toolchain_version, true);
+ return x.package == y.package &&
+ x.configuration == y.configuration &&
+ compare_version_eq (x.toolchain_version, y.toolchain_version, true);
}
template <typename T>
inline auto
operator!= (const T& x, const build_id& y)
- -> decltype (x.package == y.package)
+ -> decltype (x.package == y.package &&
+ x.configuration == y.configuration &&
+ x.toolchain_version.epoch == y.toolchain_version.epoch)
{
- return x.package != y.package || x.configuration != y.configuration ||
- compare_version_ne (x.toolchain_version, y.toolchain_version, true);
+ return x.package != y.package ||
+ x.configuration != y.configuration ||
+ compare_version_ne (x.toolchain_version, y.toolchain_version, true);
}
// build_state
@@ -167,7 +173,9 @@ namespace brep
// Create the build object with the building state, non-existent status,
// the timestamp set to now and the force state set to unforced.
//
- build (package_name_type, version,
+ build (string tenant,
+ package_name_type,
+ version,
string configuration,
string toolchain_name, version toolchain_version,
optional<string> agent_fingerprint,
@@ -177,6 +185,7 @@ namespace brep
build_id id;
+ string& tenant; // Tracks id.package.tenant.
package_name_type& package_name; // Tracks id.package.name.
upstream_version package_version; // Original of id.package.version.
string& configuration; // Tracks id.configuration.
@@ -215,6 +224,7 @@ namespace brep
//
#pragma db member(id) id column("")
+ #pragma db member(tenant) transient
#pragma db member(package_name) transient
#pragma db member(package_version) \
set(this.package_version.init (this.id.package.version, (?)))
@@ -232,20 +242,13 @@ namespace brep
private:
friend class odb::access;
- build ()
- : package_name (id.package.name), configuration (id.configuration) {}
- };
-
- #pragma db view object(build)
- struct build_count
- {
- size_t result;
- operator size_t () const {return result;}
-
- // Database mapping.
- //
- #pragma db member(result) column("count(" + build::package_name + ")")
+ build ()
+ : tenant (id.package.tenant),
+ package_name (id.package.name),
+ configuration (id.configuration)
+ {
+ }
};
#pragma db view object(build) query(distinct)
@@ -290,21 +293,21 @@ namespace brep
// Note that ADL can't find the equal operator, so we use the function call
// notation.
//
- #pragma db view \
- object(build) \
- object(build_package inner: \
- brep::operator== (build::id.package, build_package::id) && \
- build_package::internal_repository.is_not_null ())
+ #pragma db view \
+ object(build) \
+ object(build_package inner: \
+ brep::operator== (build::id.package, build_package::id) && \
+ build_package::internal_repository.canonical_name.is_not_null ())
struct package_build
{
shared_ptr<brep::build> build;
};
- #pragma db view \
- object(build) \
- object(build_package inner: \
- brep::operator== (build::id.package, build_package::id) && \
- build_package::internal_repository.is_not_null ())
+ #pragma db view \
+ object(build) \
+ object(build_package inner: \
+ brep::operator== (build::id.package, build_package::id) && \
+ build_package::internal_repository.canonical_name.is_not_null ())
struct package_build_count
{
size_t result;
diff --git a/libbrep/build.xml b/libbrep/build.xml
index 0fd4154..0116374 100644
--- a/libbrep/build.xml
+++ b/libbrep/build.xml
@@ -1,6 +1,7 @@
<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" schema-name="build" version="1">
<model version="4">
<table name="build" kind="object">
+ <column name="package_tenant" type="TEXT" null="false"/>
<column name="package_name" type="CITEXT" null="false"/>
<column name="package_version_epoch" type="INTEGER" null="false"/>
<column name="package_version_canonical_upstream" type="TEXT" null="false"/>
@@ -26,6 +27,7 @@
<column name="machine_summary" type="TEXT" null="false"/>
<column name="target" type="TEXT" null="false"/>
<primary-key>
+ <column name="package_tenant"/>
<column name="package_name"/>
<column name="package_version_epoch"/>
<column name="package_version_canonical_upstream"/>
@@ -39,6 +41,7 @@
</primary-key>
</table>
<table name="build_results" kind="container">
+ <column name="package_tenant" type="TEXT" null="false"/>
<column name="package_name" type="CITEXT" null="false"/>
<column name="package_version_epoch" type="INTEGER" null="false"/>
<column name="package_version_canonical_upstream" type="TEXT" null="false"/>
@@ -54,6 +57,7 @@
<column name="status" type="TEXT" null="false"/>
<column name="log" type="TEXT" null="false"/>
<foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="package_tenant"/>
<column name="package_name"/>
<column name="package_version_epoch"/>
<column name="package_version_canonical_upstream"/>
@@ -65,6 +69,7 @@
<column name="toolchain_version_canonical_release"/>
<column name="toolchain_version_revision"/>
<references table="build">
+ <column name="package_tenant"/>
<column name="package_name"/>
<column name="package_version_epoch"/>
<column name="package_version_canonical_upstream"/>
@@ -78,6 +83,7 @@
</references>
</foreign-key>
<index name="build_results_object_id_i">
+ <column name="package_tenant"/>
<column name="package_name"/>
<column name="package_version_epoch"/>
<column name="package_version_canonical_upstream"/>
diff --git a/libbrep/common.hxx b/libbrep/common.hxx
index 0163ca0..b9adee8 100644
--- a/libbrep/common.hxx
+++ b/libbrep/common.hxx
@@ -203,12 +203,14 @@ namespace brep
#pragma db value
struct package_id
{
+ string tenant;
package_name name;
canonical_version version;
package_id () = default;
- package_id (package_name n, const brep::version& v)
- : name (move (n)),
+ package_id (string t, package_name n, const brep::version& v)
+ : tenant (move (t)),
+ name (move (n)),
version {
v.epoch, v.canonical_upstream, v.canonical_release, v.revision}
{
@@ -252,6 +254,19 @@ namespace brep
: (?).type ()}) \
from(brep::repository_location (std::move ((?).url), (?).type))
+ // repository_id
+ //
+ #pragma db value
+ struct repository_id
+ {
+ string tenant;
+ string canonical_name;
+
+ repository_id () = default;
+ repository_id (string t, string n)
+ : tenant (move (t)), canonical_name (move (n)) {}
+ };
+
// Version comparison operators.
//
// They allow comparing objects that have epoch, canonical_upstream,
@@ -381,35 +396,93 @@ namespace brep
+ x.revision + "DESC";
}
+ template <typename T>
+ inline auto
+ order_by_version (
+ const T& x,
+ bool first = true) -> //decltype ("ORDER BY" + x.epoch)
+ decltype (x.epoch == 0)
+ {
+ return (first ? "ORDER BY" : ", ")
+ + x.epoch + ","
+ + x.canonical_upstream + ","
+ + x.canonical_release + ","
+ + x.revision;
+ }
+
// Package id comparison operators.
//
inline bool
operator< (const package_id& x, const package_id& y)
{
+ if (int r = x.tenant.compare (y.tenant))
+ return r < 0;
+
if (int r = x.name.compare (y.name))
return r < 0;
return compare_version_lt (x.version, y.version, true);
}
- // They allow comparing objects that have name and version data members. The
- // idea is that this works for both query members of package id types (in
- // particular in join conditions) as well as for values of package_id type.
+ // They allow comparing objects that have tenant, name, and version data
+ // members. The idea is that this works for both query members of package id
+ // types (in particular in join conditions) as well as for values of
+ // package_id type.
+ //
+ template <typename T1, typename T2>
+ inline auto
+ operator== (const T1& x, const T2& y)
+ -> decltype (x.tenant == y.tenant &&
+ x.name == y.name &&
+ x.version.epoch == y.version.epoch)
+ {
+ return x.tenant == y.tenant &&
+ x.name == y.name &&
+ compare_version_eq (x.version, y.version, true);
+ }
+
+ template <typename T1, typename T2>
+ inline auto
+ operator!= (const T1& x, const T2& y)
+ -> decltype (x.tenant == y.tenant &&
+ x.name == y.name &&
+ x.version.epoch == y.version.epoch)
+ {
+ return x.tenant != y.tenant ||
+ x.name != y.name ||
+ compare_version_ne (x.version, y.version, true);
+ }
+
+ // Repository id comparison operators.
+ //
+ inline bool
+ operator< (const repository_id& x, const repository_id& y)
+ {
+ if (int r = x.tenant.compare (y.tenant))
+ return r < 0;
+
+ return x.canonical_name.compare (y.canonical_name) < 0;
+ }
+
+ // They allow comparing objects that have tenant and canonical_name data
+ // members. The idea is that this works for both query members of repository
+ // id types (in particular in join conditions) as well as for values of
+ // repository_id type.
//
template <typename T1, typename T2>
inline auto
operator== (const T1& x, const T2& y)
- -> decltype (x.name == y.name && x.version.epoch == y.version.epoch)
+ -> decltype (x.tenant == y.tenant && x.canonical_name == y.canonical_name)
{
- return x.name == y.name && compare_version_eq (x.version, y.version, true);
+ return x.tenant == y.tenant && x.canonical_name == y.canonical_name;
}
template <typename T1, typename T2>
inline auto
operator!= (const T1& x, const T2& y)
- -> decltype (x.name == y.name && x.version.epoch == y.version.epoch)
+ -> decltype (x.tenant == y.tenant && x.canonical_name == y.canonical_name)
{
- return x.name != y.name || compare_version_ne (x.version, y.version, true);
+ return x.tenant != y.tenant || x.canonical_name != y.canonical_name;
}
}
diff --git a/libbrep/package-extra.sql b/libbrep/package-extra.sql
index bd5a27b..d9930aa 100644
--- a/libbrep/package-extra.sql
+++ b/libbrep/package-extra.sql
@@ -17,10 +17,15 @@
--
-- DROP FUNCTION IF EXISTS to_tsvector(IN document weighted_text);
--
-DROP FUNCTION IF EXISTS search_packages(IN query tsquery, INOUT name CITEXT);
-DROP FUNCTION IF EXISTS search_latest_packages(IN query tsquery);
-DROP FUNCTION IF EXISTS latest_package(INOUT name CITEXT);
-DROP FUNCTION IF EXISTS latest_packages();
+DROP FUNCTION IF EXISTS search_packages(IN query tsquery,
+ IN tenant TEXT,
+ IN name CITEXT);
+
+DROP FUNCTION IF EXISTS search_latest_packages(IN query tsquery,
+ IN tenant TEXT);
+
+DROP FUNCTION IF EXISTS latest_package(IN tenant TEXT, IN name CITEXT);
+DROP FUNCTION IF EXISTS latest_packages(IN tenant TEXT);
DROP TYPE IF EXISTS weighted_text CASCADE;
CREATE TYPE weighted_text AS (a TEXT, b TEXT, c TEXT, d TEXT);
@@ -28,12 +33,14 @@ CREATE TYPE weighted_text AS (a TEXT, b TEXT, c TEXT, d TEXT);
-- Return the latest versions of internal packages as a set of package rows.
--
CREATE FUNCTION
-latest_packages()
+latest_packages(IN tenant TEXT)
RETURNS SETOF package AS $$
SELECT p1.*
FROM package p1 LEFT JOIN package p2 ON (
- p1.internal_repository IS NOT NULL AND p1.name = p2.name AND
- p2.internal_repository IS NOT NULL AND
+ p1.internal_repository_canonical_name IS NOT NULL AND
+ p1.tenant = p2.tenant AND
+ p1.name = p2.name AND
+ p2.internal_repository_canonical_name IS NOT NULL AND
(p1.version_epoch < p2.version_epoch OR
p1.version_epoch = p2.version_epoch AND
(p1.version_canonical_upstream < p2.version_canonical_upstream OR
@@ -42,23 +49,26 @@ RETURNS SETOF package AS $$
p1.version_canonical_release = p2.version_canonical_release AND
p1.version_revision < p2.version_revision))))
WHERE
- p1.internal_repository IS NOT NULL AND p2.name IS NULL;
+ p1.tenant = latest_packages.tenant AND
+ p1.internal_repository_canonical_name IS NOT NULL AND
+ p2.name IS NULL;
$$ LANGUAGE SQL STABLE;
--- Find the latest version of an internal package having the specified name.
--- Return a single row containing the package id, empty row set if the package
--- not found.
+-- Find the latest version of an internal package having the specified tenant
+-- and name. Return a single row containing the package id, empty row set if
+-- the package not found.
--
CREATE FUNCTION
-latest_package(INOUT name CITEXT,
+latest_package(INOUT tenant TEXT,
+ INOUT name CITEXT,
OUT version_epoch INTEGER,
OUT version_canonical_upstream TEXT,
OUT version_canonical_release TEXT,
OUT version_revision INTEGER)
RETURNS SETOF record AS $$
- SELECT name, version_epoch, version_canonical_upstream,
+ SELECT tenant, name, version_epoch, version_canonical_upstream,
version_canonical_release, version_revision
- FROM latest_packages()
+ FROM latest_packages(latest_package.tenant)
WHERE name = latest_package.name;
$$ LANGUAGE SQL STABLE;
@@ -69,6 +79,7 @@ $$ LANGUAGE SQL STABLE;
--
CREATE FUNCTION
search_latest_packages(IN query tsquery,
+ INOUT tenant TEXT,
OUT name CITEXT,
OUT version_epoch INTEGER,
OUT version_canonical_upstream TEXT,
@@ -76,23 +87,25 @@ search_latest_packages(IN query tsquery,
OUT version_revision INTEGER,
OUT rank real)
RETURNS SETOF record AS $$
- SELECT name, version_epoch, version_canonical_upstream,
+ SELECT tenant, name, version_epoch, version_canonical_upstream,
version_canonical_release, version_revision,
CASE
WHEN query IS NULL THEN 0
-- Weight mapping: D C B A
ELSE ts_rank_cd('{0.05, 0.2, 0.9, 1.0}', search_index, query)
END AS rank
- FROM latest_packages()
+ FROM latest_packages(search_latest_packages.tenant)
WHERE query IS NULL OR search_index @@ query;
$$ LANGUAGE SQL STABLE;
--- Search for packages matching the search query and having the specified name.
--- Return a set of rows containing the package id and search rank. If query
--- is NULL, then match all packages and return 0 rank for all rows.
+-- Search for packages matching the search query and having the specified
+-- tenant and name. Return a set of rows containing the package id and search
+-- rank. If query is NULL, then match all packages and return 0 rank for all
+-- rows.
--
CREATE FUNCTION
search_packages(IN query tsquery,
+ INOUT tenant TEXT,
INOUT name CITEXT,
OUT version_epoch INTEGER,
OUT version_canonical_upstream TEXT,
@@ -100,7 +113,7 @@ search_packages(IN query tsquery,
OUT version_revision INTEGER,
OUT rank real)
RETURNS SETOF record AS $$
- SELECT name, version_epoch, version_canonical_upstream,
+ SELECT tenant, name, version_epoch, version_canonical_upstream,
version_canonical_release, version_revision,
CASE
WHEN query IS NULL THEN 0
@@ -109,7 +122,9 @@ RETURNS SETOF record AS $$
END AS rank
FROM package
WHERE
- internal_repository IS NOT NULL AND name = search_packages.name AND
+ tenant = search_packages.tenant AND
+ name = search_packages.name AND
+ internal_repository_canonical_name IS NOT NULL AND
(query IS NULL OR search_index @@ query);
$$ LANGUAGE SQL STABLE;
diff --git a/libbrep/package.cxx b/libbrep/package.cxx
index 6d4550d..41dd4e2 100644
--- a/libbrep/package.cxx
+++ b/libbrep/package.cxx
@@ -64,32 +64,32 @@ namespace brep
optional<string> fr,
optional<string> sh,
shared_ptr<repository_type> rp)
- : id (move (nm), vr),
- version (move (vr)),
- project (move (pn)),
- priority (move (pr)),
- summary (move (sm)),
- license_alternatives (move (la)),
- tags (move (tg)),
- description (move (ds)),
- changes (move (ch)),
- url (move (ur)),
- doc_url (move (du)),
- src_url (move (su)),
- package_url (move (pu)),
- email (move (em)),
- package_email (move (pe)),
- build_email (move (be)),
- dependencies (move (dp)),
- requirements (move (rq)),
- build_constraints (
- version.compare (wildcard_version, true) != 0
- ? move (bc)
- : build_constraints_type ()),
- internal_repository (move (rp)),
- location (move (lc)),
- fragment (move (fr)),
- sha256sum (move (sh))
+ : id (rp->tenant, move (nm), vr),
+ name (id.name),
+ version (move (vr)),
+ project (move (pn)),
+ priority (move (pr)),
+ summary (move (sm)),
+ license_alternatives (move (la)),
+ tags (move (tg)),
+ description (move (ds)),
+ changes (move (ch)),
+ url (move (ur)),
+ doc_url (move (du)),
+ src_url (move (su)),
+ package_url (move (pu)),
+ email (move (em)),
+ package_email (move (pe)),
+ build_email (move (be)),
+ dependencies (move (dp)),
+ requirements (move (rq)),
+ build_constraints (version.compare (wildcard_version, true) != 0
+ ? move (bc)
+ : build_constraints_type ()),
+ internal_repository (move (rp)),
+ location (move (lc)),
+ fragment (move (fr)),
+ sha256sum (move (sh))
{
assert (internal_repository->internal);
}
@@ -98,7 +98,8 @@ namespace brep
package (package_name nm,
version_type vr,
shared_ptr<repository_type> rp)
- : id (move (nm), vr),
+ : id (rp->tenant, move (nm), vr),
+ name (id.name),
version (move (vr))
{
assert (!rp->internal);
@@ -121,7 +122,7 @@ namespace brep
// Probably drop-box would be better as also tells what are
// the available internal repositories.
//
- string k (project.string () + " " + id.name.string () + " " +
+ string k (project.string () + " " + name.string () + " " +
version.string () + " " + version.string (true));
// Add tags to keywords.
@@ -151,12 +152,15 @@ namespace brep
// repository
//
repository::
- repository (repository_location l,
+ repository (string t,
+ repository_location l,
string d,
repository_location h,
optional<certificate_type> c,
uint16_t r)
- : name (l.canonical_name ()),
+ : id (move (t), l.canonical_name ()),
+ tenant (id.tenant),
+ canonical_name (id.canonical_name),
location (move (l)),
display_name (move (d)),
priority (r),
@@ -167,8 +171,10 @@ namespace brep
}
repository::
- repository (repository_location l)
- : name (l.canonical_name ()),
+ repository (string t, repository_location l)
+ : id (move (t), l.canonical_name ()),
+ tenant (id.tenant),
+ canonical_name (id.canonical_name),
location (move (l)),
priority (0),
internal (false)
diff --git a/libbrep/package.hxx b/libbrep/package.hxx
index 3d281b0..4dbde29 100644
--- a/libbrep/package.hxx
+++ b/libbrep/package.hxx
@@ -171,6 +171,8 @@ namespace brep
#pragma db value(build_constraint) definition
+ // certificate
+ //
#pragma db value
class certificate
{
@@ -191,7 +193,8 @@ namespace brep
// Create internal repository.
//
- repository (repository_location,
+ repository (string tenant,
+ repository_location,
string display_name,
repository_location cache_location,
optional<certificate_type>,
@@ -200,9 +203,12 @@ namespace brep
// Create external repository.
//
explicit
- repository (repository_location);
+ repository (string tenant, repository_location);
+
+ repository_id id;
- string name; // Object id (canonical name).
+ const string& tenant; // Tracks id.tenant.
+ const string& canonical_name; // Tracks id.canonical_name.
repository_location location; // Note: foreign-mapped in build.
string display_name;
@@ -243,21 +249,24 @@ namespace brep
// Database mapping.
//
- #pragma db member(name) id
+ #pragma db member(id) id column("")
- #pragma db member(location) \
- set(this.location = std::move (?); \
- assert (this.name == this.location.canonical_name ()))
+ #pragma db member(tenant) transient
+ #pragma db member(canonical_name) transient
- #pragma db member(complements) id_column("repository") \
- value_column("complement") value_not_null
+ #pragma db member(location) \
+ set(this.location = std::move (?); \
+ assert (this.canonical_name == this.location.canonical_name ()))
- #pragma db member(prerequisites) id_column("repository") \
- value_column("prerequisite") value_not_null
+ #pragma db member(complements) id_column("repository_") \
+ value_column("complement_") value_not_null
+
+ #pragma db member(prerequisites) id_column("repository_") \
+ value_column("prerequisite_") value_not_null
private:
friend class odb::access;
- repository () = default;
+ repository (): tenant (id.tenant), canonical_name (id.canonical_name) {}
};
// The 'to' expression calls the PostgreSQL to_tsvector(weighted_text)
@@ -336,6 +345,8 @@ namespace brep
// Manifest data.
//
package_id id;
+
+ const package_name& name; // Tracks id.name.
upstream_version version;
// Matches the package name if the project name is not specified in
@@ -384,6 +395,7 @@ namespace brep
// Database mapping.
//
#pragma db member(id) id column("")
+ #pragma db member(name) transient
#pragma db member(version) set(this.version.init (this.id.version, (?)))
// license
@@ -453,7 +465,7 @@ namespace brep
// other_repositories
//
#pragma db member(other_repositories) \
- id_column("") value_column("repository") value_not_null
+ id_column("") value_column("repository_") value_not_null
// search_index
//
@@ -464,7 +476,7 @@ namespace brep
private:
friend class odb::access;
- package () = default;
+ package (): name (id.name) {}
// Save keywords, summary, description, and changes to weighted_text
// a, b, c, d members, respectively. So a word found in keywords will
diff --git a/libbrep/package.xml b/libbrep/package.xml
index 55baae9..7b74349 100644
--- a/libbrep/package.xml
+++ b/libbrep/package.xml
@@ -1,7 +1,8 @@
<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" schema-name="package" version="1">
<model version="7">
<table name="repository" kind="object">
- <column name="name" type="TEXT" null="false"/>
+ <column name="tenant" type="TEXT" null="false"/>
+ <column name="canonical_name" type="TEXT" null="false"/>
<column name="location_url" type="TEXT" null="false"/>
<column name="location_type" type="TEXT" null="false"/>
<column name="display_name" type="TEXT" null="false"/>
@@ -22,56 +23,72 @@
<column name="repositories_timestamp" type="BIGINT" null="false"/>
<column name="internal" type="BOOLEAN" null="false"/>
<primary-key>
- <column name="name"/>
+ <column name="tenant"/>
+ <column name="canonical_name"/>
</primary-key>
</table>
<table name="repository_complements" kind="container">
- <column name="repository" type="TEXT" null="false"/>
+ <column name="repository_tenant" type="TEXT" null="false"/>
+ <column name="repository_canonical_name" type="TEXT" null="false"/>
<column name="index" type="BIGINT" null="false"/>
- <column name="complement" type="TEXT" null="false"/>
+ <column name="complement_tenant" type="TEXT" null="false"/>
+ <column name="complement_canonical_name" type="TEXT" null="false"/>
<foreign-key name="repository_fk" on-delete="CASCADE">
- <column name="repository"/>
+ <column name="repository_tenant"/>
+ <column name="repository_canonical_name"/>
<references table="repository">
- <column name="name"/>
+ <column name="tenant"/>
+ <column name="canonical_name"/>
</references>
</foreign-key>
<index name="repository_complements_repository_i">
- <column name="repository"/>
+ <column name="repository_tenant"/>
+ <column name="repository_canonical_name"/>
</index>
<index name="repository_complements_index_i">
<column name="index"/>
</index>
<foreign-key name="complement_fk" deferrable="DEFERRED">
- <column name="complement"/>
+ <column name="complement_tenant"/>
+ <column name="complement_canonical_name"/>
<references table="repository">
- <column name="name"/>
+ <column name="tenant"/>
+ <column name="canonical_name"/>
</references>
</foreign-key>
</table>
<table name="repository_prerequisites" kind="container">
- <column name="repository" type="TEXT" null="false"/>
+ <column name="repository_tenant" type="TEXT" null="false"/>
+ <column name="repository_canonical_name" type="TEXT" null="false"/>
<column name="index" type="BIGINT" null="false"/>
- <column name="prerequisite" type="TEXT" null="false"/>
+ <column name="prerequisite_tenant" type="TEXT" null="false"/>
+ <column name="prerequisite_canonical_name" type="TEXT" null="false"/>
<foreign-key name="repository_fk" on-delete="CASCADE">
- <column name="repository"/>
+ <column name="repository_tenant"/>
+ <column name="repository_canonical_name"/>
<references table="repository">
- <column name="name"/>
+ <column name="tenant"/>
+ <column name="canonical_name"/>
</references>
</foreign-key>
<index name="repository_prerequisites_repository_i">
- <column name="repository"/>
+ <column name="repository_tenant"/>
+ <column name="repository_canonical_name"/>
</index>
<index name="repository_prerequisites_index_i">
<column name="index"/>
</index>
<foreign-key name="prerequisite_fk" deferrable="DEFERRED">
- <column name="prerequisite"/>
+ <column name="prerequisite_tenant"/>
+ <column name="prerequisite_canonical_name"/>
<references table="repository">
- <column name="name"/>
+ <column name="tenant"/>
+ <column name="canonical_name"/>
</references>
</foreign-key>
</table>
<table name="package" kind="object">
+ <column name="tenant" type="TEXT" null="false"/>
<column name="name" type="CITEXT" null="false"/>
<column name="version_epoch" type="INTEGER" null="false"/>
<column name="version_canonical_upstream" type="TEXT" null="false"/>
@@ -99,12 +116,14 @@
<column name="package_email_comment" type="TEXT" null="true"/>
<column name="build_email" type="TEXT" null="true"/>
<column name="build_email_comment" type="TEXT" null="true"/>
- <column name="internal_repository" type="TEXT" null="true"/>
+ <column name="internal_repository_tenant" type="TEXT" null="true"/>
+ <column name="internal_repository_canonical_name" type="TEXT" null="true"/>
<column name="location" type="TEXT" null="true"/>
<column name="fragment" type="TEXT" null="true"/>
<column name="sha256sum" type="TEXT" null="true"/>
<column name="search_index" type="tsvector" null="true"/>
<primary-key>
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
@@ -112,9 +131,11 @@
<column name="version_revision"/>
</primary-key>
<foreign-key name="internal_repository_fk" deferrable="DEFERRED">
- <column name="internal_repository"/>
+ <column name="internal_repository_tenant"/>
+ <column name="internal_repository_canonical_name"/>
<references table="repository">
- <column name="name"/>
+ <column name="tenant"/>
+ <column name="canonical_name"/>
</references>
</foreign-key>
<index name="package_search_index_i" method="GIN">
@@ -122,6 +143,7 @@
</index>
</table>
<table name="package_license_alternatives" kind="container">
+ <column name="tenant" type="TEXT" null="false"/>
<column name="name" type="CITEXT" null="false"/>
<column name="version_epoch" type="INTEGER" null="false"/>
<column name="version_canonical_upstream" type="TEXT" null="false"/>
@@ -130,12 +152,14 @@
<column name="index" type="BIGINT" null="false"/>
<column name="comment" type="TEXT" null="false"/>
<foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
<column name="version_canonical_release"/>
<column name="version_revision"/>
<references table="package">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
@@ -144,6 +168,7 @@
</references>
</foreign-key>
<index name="package_license_alternatives_object_id_i">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
@@ -155,6 +180,7 @@
</index>
</table>
<table name="package_licenses" kind="container">
+ <column name="tenant" type="TEXT" null="false"/>
<column name="name" type="CITEXT" null="false"/>
<column name="version_epoch" type="INTEGER" null="false"/>
<column name="version_canonical_upstream" type="TEXT" null="false"/>
@@ -164,12 +190,14 @@
<column name="index" type="BIGINT" null="false"/>
<column name="license" type="TEXT" null="false"/>
<foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
<column name="version_canonical_release"/>
<column name="version_revision"/>
<references table="package">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
@@ -178,6 +206,7 @@
</references>
</foreign-key>
<index name="package_licenses_object_id_i">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
@@ -186,6 +215,7 @@
</index>
</table>
<table name="package_tags" kind="container">
+ <column name="tenant" type="TEXT" null="false"/>
<column name="name" type="CITEXT" null="false"/>
<column name="version_epoch" type="INTEGER" null="false"/>
<column name="version_canonical_upstream" type="TEXT" null="false"/>
@@ -194,12 +224,14 @@
<column name="index" type="BIGINT" null="false"/>
<column name="tag" type="TEXT" null="false"/>
<foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
<column name="version_canonical_release"/>
<column name="version_revision"/>
<references table="package">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
@@ -208,6 +240,7 @@
</references>
</foreign-key>
<index name="package_tags_object_id_i">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
@@ -219,6 +252,7 @@
</index>
</table>
<table name="package_dependencies" kind="container">
+ <column name="tenant" type="TEXT" null="false"/>
<column name="name" type="CITEXT" null="false"/>
<column name="version_epoch" type="INTEGER" null="false"/>
<column name="version_canonical_upstream" type="TEXT" null="false"/>
@@ -229,12 +263,14 @@
<column name="buildtime" type="BOOLEAN" null="false"/>
<column name="comment" type="TEXT" null="false"/>
<foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
<column name="version_canonical_release"/>
<column name="version_revision"/>
<references table="package">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
@@ -243,6 +279,7 @@
</references>
</foreign-key>
<index name="package_dependencies_object_id_i">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
@@ -254,6 +291,7 @@
</index>
</table>
<table name="package_dependency_alternatives" kind="container">
+ <column name="tenant" type="TEXT" null="false"/>
<column name="name" type="CITEXT" null="false"/>
<column name="version_epoch" type="INTEGER" null="false"/>
<column name="version_canonical_upstream" type="TEXT" null="false"/>
@@ -276,18 +314,21 @@
<column name="dep_max_version_release" type="TEXT" null="true"/>
<column name="dep_min_open" type="BOOLEAN" null="true"/>
<column name="dep_max_open" type="BOOLEAN" null="true"/>
+ <column name="dep_package_tenant" type="TEXT" null="true"/>
<column name="dep_package_name" type="CITEXT" null="true"/>
<column name="dep_package_version_epoch" type="INTEGER" null="true"/>
<column name="dep_package_version_canonical_upstream" type="TEXT" null="true"/>
<column name="dep_package_version_canonical_release" type="TEXT" null="true" options="COLLATE &quot;C&quot;"/>
<column name="dep_package_version_revision" type="INTEGER" null="true"/>
<foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
<column name="version_canonical_release"/>
<column name="version_revision"/>
<references table="package">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
@@ -296,6 +337,7 @@
</references>
</foreign-key>
<index name="package_dependency_alternatives_object_id_i">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
@@ -303,12 +345,14 @@
<column name="version_revision"/>
</index>
<foreign-key name="dep_package_fk" deferrable="DEFERRED">
+ <column name="dep_package_tenant"/>
<column name="dep_package_name"/>
<column name="dep_package_version_epoch"/>
<column name="dep_package_version_canonical_upstream"/>
<column name="dep_package_version_canonical_release"/>
<column name="dep_package_version_revision"/>
<references table="package">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
@@ -318,6 +362,7 @@
</foreign-key>
</table>
<table name="package_requirements" kind="container">
+ <column name="tenant" type="TEXT" null="false"/>
<column name="name" type="CITEXT" null="false"/>
<column name="version_epoch" type="INTEGER" null="false"/>
<column name="version_canonical_upstream" type="TEXT" null="false"/>
@@ -328,12 +373,14 @@
<column name="buildtime" type="BOOLEAN" null="false"/>
<column name="comment" type="TEXT" null="false"/>
<foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
<column name="version_canonical_release"/>
<column name="version_revision"/>
<references table="package">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
@@ -342,6 +389,7 @@
</references>
</foreign-key>
<index name="package_requirements_object_id_i">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
@@ -353,6 +401,7 @@
</index>
</table>
<table name="package_requirement_alternatives" kind="container">
+ <column name="tenant" type="TEXT" null="false"/>
<column name="name" type="CITEXT" null="false"/>
<column name="version_epoch" type="INTEGER" null="false"/>
<column name="version_canonical_upstream" type="TEXT" null="false"/>
@@ -362,12 +411,14 @@
<column name="index" type="BIGINT" null="false"/>
<column name="id" type="TEXT" null="false"/>
<foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
<column name="version_canonical_release"/>
<column name="version_revision"/>
<references table="package">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
@@ -376,6 +427,7 @@
</references>
</foreign-key>
<index name="package_requirement_alternatives_object_id_i">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
@@ -384,6 +436,7 @@
</index>
</table>
<table name="package_build_constraints" kind="container">
+ <column name="tenant" type="TEXT" null="false"/>
<column name="name" type="CITEXT" null="false"/>
<column name="version_epoch" type="INTEGER" null="false"/>
<column name="version_canonical_upstream" type="TEXT" null="false"/>
@@ -395,12 +448,14 @@
<column name="target" type="TEXT" null="true"/>
<column name="comment" type="TEXT" null="false"/>
<foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
<column name="version_canonical_release"/>
<column name="version_revision"/>
<references table="package">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
@@ -409,6 +464,7 @@
</references>
</foreign-key>
<index name="package_build_constraints_object_id_i">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
@@ -420,20 +476,24 @@
</index>
</table>
<table name="package_other_repositories" kind="container">
+ <column name="tenant" type="TEXT" null="false"/>
<column name="name" type="CITEXT" null="false"/>
<column name="version_epoch" type="INTEGER" null="false"/>
<column name="version_canonical_upstream" type="TEXT" null="false"/>
<column name="version_canonical_release" type="TEXT" null="false" options="COLLATE &quot;C&quot;"/>
<column name="version_revision" type="INTEGER" null="false"/>
<column name="index" type="BIGINT" null="false"/>
- <column name="repository" type="TEXT" null="false"/>
+ <column name="repository_tenant" type="TEXT" null="false"/>
+ <column name="repository_canonical_name" type="TEXT" null="false"/>
<foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
<column name="version_canonical_release"/>
<column name="version_revision"/>
<references table="package">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
@@ -442,6 +502,7 @@
</references>
</foreign-key>
<index name="package_other_repositories_object_id_i">
+ <column name="tenant"/>
<column name="name"/>
<column name="version_epoch"/>
<column name="version_canonical_upstream"/>
@@ -452,9 +513,11 @@
<column name="index"/>
</index>
<foreign-key name="repository_fk" deferrable="DEFERRED">
- <column name="repository"/>
+ <column name="repository_tenant"/>
+ <column name="repository_canonical_name"/>
<references table="repository">
- <column name="name"/>
+ <column name="tenant"/>
+ <column name="canonical_name"/>
</references>
</foreign-key>
</table>
diff --git a/libbrep/types.hxx b/libbrep/types.hxx
index 13d3d09..7c7b6ec 100644
--- a/libbrep/types.hxx
+++ b/libbrep/types.hxx
@@ -87,6 +87,8 @@ namespace brep
using paths = std::vector<path>;
using dir_paths = std::vector<dir_path>;
+ using butl::path_cast;
+
// <libbutl/timestamp.mxx>
//
using butl::system_clock;
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
{
"<user>",
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<repository> pr (
- db.find<repository> (r.location.canonical_name ()));
+ db.find<repository> (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<repository> (
- 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] <repository_location>' process.
@@ -388,7 +393,7 @@ load_packages (const shared_ptr<repository>& rp, database& db)
for (auto& pm: pms)
{
shared_ptr<package> p (
- db.find<package> (package_id (pm.name, pm.version)));
+ db.find<package> (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<repository>& 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<repository>& rp,
//
assert (!rp->cache_location.empty ());
+ const string& tenant (rp->tenant);
+
// Repository is already persisted by the load_packages() function call.
//
- assert (db.find<repository> (rp->name) != nullptr);
+ assert (db.find<repository> (
+ repository_id (tenant, rp->canonical_name)) != nullptr);
pkg_repository_manifests rpm;
@@ -680,16 +688,17 @@ load_repositories (const shared_ptr<repository>& rp,
? rp->prerequisites
: rp->complements);
- rs.emplace_back (db, cn);
+ rs.emplace_back (db, repository_id (tenant, cn));
- shared_ptr<repository> pr (db.find<repository> (cn));
+ shared_ptr<repository> pr (
+ db.find<repository> (repository_id (tenant, cn)));
if (pr != nullptr)
// The prerequisite repository is already loaded.
//
continue;
- pr = make_shared<repository> (move (rl));
+ pr = make_shared<repository> (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<package> ();
- db.erase_query<repository> ();
+ // 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<package> ();
+ db.erase_query<repository> ();
+ }
+ else // Multi-tenant mode.
+ {
+ cstrings ts ({tenant.c_str (), ""});
+
+ db.erase_query<package> (
+ query<package>::id.tenant.in_range (ts.begin (), ts.end ()));
+
+ db.erase_query<repository> (
+ query<repository>::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<repository> r (
- make_shared<repository> (ir.location,
+ make_shared<repository> (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<repository> r (
- db.load<repository> (ir.location.canonical_name ()));
+ db.load<repository> (repository_id (tenant,
+ ir.location.canonical_name ())));
load_repositories (r, db, ops.shallow ());
}
@@ -1170,14 +1212,18 @@ try
using query = query<package>;
for (auto& p:
- db.query<package> (query::internal_repository.is_not_null ()))
+ db.query<package> (
+ 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<package> (query::internal_repository.is_not_null ()))
+ db.query<package> (
+ query::id.tenant == tenant &&
+ query::internal_repository.canonical_name.is_not_null ()))
detect_dependency_cycle (p.id, chain, db);
}
}
diff --git a/mod/build-config.cxx b/mod/build-config.cxx
index 4913555..e838a59 100644
--- a/mod/build-config.cxx
+++ b/mod/build-config.cxx
@@ -14,6 +14,8 @@
#include <web/mime-url-encoding.hxx>
+#include <mod/utility.hxx>
+
namespace brep
{
using namespace std;
@@ -117,7 +119,7 @@ namespace brep
// needs to be url-encoded, and only in the query part of the URL. We embed
// the package version into the URL path part and so don't encode it.
//
- string url (host + root.representation () +
+ string url (host + tenant_dir (root, b.tenant).representation () +
mime_url_encode (b.package_name.string (), false) + '/' +
b.package_version.string () + "/log/" +
mime_url_encode (b.configuration, false) + '/' +
@@ -140,7 +142,7 @@ namespace brep
// we embed the package version into the URL query part, where it is not
// encoded by design.
//
- return host + root.string () +
+ return host + tenant_dir (root, b.tenant).string () +
"?build-force&pn=" + mime_url_encode (b.package_name.string ()) +
"&pv=" + b.package_version.string () +
"&cf=" + mime_url_encode (b.configuration) +
@@ -148,7 +150,8 @@ namespace brep
}
bool
- match (const string& config_pattern, const optional<string>& target_pattern,
+ match (const string& config_pattern,
+ const optional<string>& target_pattern,
const build_config& c)
{
return path_match (config_pattern, c.name) &&
diff --git a/mod/mod-build-force.cxx b/mod/mod-build-force.cxx
index b6514ce..ddc1301 100644
--- a/mod/mod-build-force.cxx
+++ b/mod/mod-build-force.cxx
@@ -122,7 +122,7 @@ handle (request& rq, response& rs)
if (c.empty ())
throw invalid_argument ("no configuration name");
- id = build_id (package_id (move (p), package_version),
+ id = build_id (package_id (move (tenant), move (p), package_version),
move (c),
toolchain_version);
}
@@ -168,6 +168,7 @@ handle (request& rq, response& rs)
build_db_->update (b);
l1 ([&]{trace << "force rebuild for "
+ << b->tenant << ' '
<< b->package_name << '/' << b->package_version << ' '
<< b->configuration << ' '
<< b->toolchain_name << '-' << b->toolchain_version
diff --git a/mod/mod-build-log.cxx b/mod/mod-build-log.cxx
index 57135b6..70e2c7e 100644
--- a/mod/mod-build-log.cxx
+++ b/mod/mod-build-log.cxx
@@ -78,13 +78,22 @@ handle (request& rq, response& rs)
path lpath (rq.path ().leaf (options_->root ()));
+ // If the tenant is not empty then it is contained in the leftmost path
+ // component (see repository_root for details). Strip it if that's the case.
+ //
+ if (!tenant.empty ())
+ {
+ assert (!lpath.empty ());
+ lpath = path (++lpath.begin (), lpath.end ());
+ }
+
+ assert (!lpath.empty ());
+
try
{
auto i (lpath.begin ());
- assert (i != lpath.end ());
package_name name;
-
try
{
name = package_name (*i++);
@@ -127,7 +136,7 @@ handle (request& rq, response& rs)
version toolchain_version (parse_version (*i++, "toolchain version"));
- id = build_id (package_id (move (name), package_version),
+ id = build_id (package_id (tenant, move (name), package_version),
move (config),
toolchain_version);
@@ -164,6 +173,7 @@ handle (request& rq, response& rs)
auto config_expired = [&trace, &lpath, this] (const string& d)
{
l2 ([&]{trace << "package build configuration for " << lpath
+ << (!tenant.empty () ? "(" + tenant + ")" : "")
<< " expired: " << d;});
throw invalid_request (404, "package build configuration expired: " + d);
@@ -204,6 +214,8 @@ handle (request& rq, response& rs)
auto print_header = [&os, &b] ()
{
+ // @@ Should we print the tenant? How to call it if that's the case?
+ //
os << "package: " << b->package_name << endl
<< "version: " << b->package_version << endl
<< "toolchain: " << b->toolchain_name << '-' << b->toolchain_version
diff --git a/mod/mod-build-result.cxx b/mod/mod-build-result.cxx
index 65e8425..1ba1bec 100644
--- a/mod/mod-build-result.cxx
+++ b/mod/mod-build-result.cxx
@@ -106,8 +106,8 @@ handle (request& rq, response&)
}
// Parse the task response session to obtain the build configuration name and
- // the timestamp, and to make sure the session matches the result manifest's
- // package name and version.
+ // the timestamp, and to make sure the session matches tenant and the result
+ // manifest's package name, and version.
//
build_id id;
timestamp session_timestamp;
@@ -116,9 +116,18 @@ handle (request& rq, response&)
{
const string& s (rqm.session);
- size_t p (s.find ('/')); // End of package name.
+ size_t p (s.find ('/')); // End of tenant.
- if (p == 0)
+ if (p == string::npos)
+ throw invalid_argument ("no package name");
+
+ if (tenant.compare (0, tenant.size (), s, 0, p) != 0)
+ throw invalid_argument ("tenant mismatch");
+
+ size_t b (p + 1); // Start of package name.
+ p = s.find ('/', b); // End of package name.
+
+ if (p == b)
throw invalid_argument ("empty package name");
if (p == string::npos)
@@ -127,11 +136,11 @@ handle (request& rq, response&)
package_name& name (rqm.result.name);
{
const string& n (name.string ());
- if (n.compare (0, n.size (), s, 0, p) != 0)
+ if (n.compare (0, n.size (), s, b, p - b) != 0)
throw invalid_argument ("package name mismatch");
}
- size_t b (p + 1); // Start of version.
+ b = p + 1; // Start of version.
p = s.find ('/', b); // End of version.
if (p == string::npos)
@@ -172,7 +181,7 @@ handle (request& rq, response&)
version toolchain_version (parse_version ("toolchain version"));
- id = build_id (package_id (move (name), package_version),
+ id = build_id (package_id (move (tenant), move (name), package_version),
move (config),
toolchain_version);
diff --git a/mod/mod-build-task.cxx b/mod/mod-build-task.cxx
index 4e56d02..77652ce 100644
--- a/mod/mod-build-task.cxx
+++ b/mod/mod-build-task.cxx
@@ -188,13 +188,15 @@ handle (request& rq, response& rs)
chrono::duration_cast<std::chrono::nanoseconds> (
b->timestamp.time_since_epoch ()).count ());
- string session (b->package_name.string () + '/' +
- b->package_version.string () +
- '/' + b->configuration +
- '/' + b->toolchain_version.string () +
- '/' + to_string (ts));
-
- string result_url (options_->host () + options_->root ().string () +
+ string session (b->tenant + '/' +
+ b->package_name.string () + '/' +
+ b->package_version.string () + '/' +
+ b->configuration + '/' +
+ b->toolchain_version.string () + '/' +
+ to_string (ts));
+
+ string result_url (options_->host () +
+ tenant_dir (options_->root (), b->tenant).string () +
"?build-result");
lazy_shared_ptr<build_repository> r (p->internal_repository);
@@ -319,6 +321,10 @@ handle (request& rq, response& rs)
// harmful in that: updates are infrequent and missed packages will be
// picked up on the next request.
//
+ // Also note that we disregard the request tenant and operate on the whole
+ // set of the packages and builds. In future we may add support for
+ // building packages for a specific tenant.
+ //
using pkg_query = query<buildable_package>;
using prep_pkg_query = prepared_query<buildable_package>;
@@ -330,18 +336,17 @@ handle (request& rq, response& rs)
if (!rp.empty ())
pq = pq &&
- pkg_query::build_repository::name.in_range (rp.begin (), rp.end ());
+ pkg_query::build_repository::id.canonical_name.in_range (rp.begin (),
+ rp.end ());
// Specify the portion.
//
size_t offset (0);
pq += "ORDER BY" +
- pkg_query::build_package::id.name + "," +
- pkg_query::build_package::id.version.epoch + "," +
- pkg_query::build_package::id.version.canonical_upstream + "," +
- pkg_query::build_package::id.version.canonical_release + "," +
- pkg_query::build_package::id.version.revision +
+ pkg_query::build_package::id.tenant + "," +
+ pkg_query::build_package::id.name +
+ order_by_version (pkg_query::build_package::id.version, false) +
"OFFSET" + pkg_query::_ref (offset) + "LIMIT 50";
connection_ptr conn (build_db_->connection ());
@@ -368,20 +373,23 @@ handle (request& rq, response& rs)
const auto& qv (bld_query::id.package.version);
bld_query bq (
- bld_query::id.package.name == bld_query::_ref (id.name) &&
+ bld_query::id.package.tenant == bld_query::_ref (id.tenant) &&
+
+ bld_query::id.package.name == bld_query::_ref (id.name) &&
- qv.epoch == bld_query::_ref (id.version.epoch) &&
+ qv.epoch == bld_query::_ref (id.version.epoch) &&
qv.canonical_upstream ==
- bld_query::_ref (id.version.canonical_upstream) &&
- qv.canonical_release == bld_query::_ref (id.version.canonical_release) &&
- qv.revision == bld_query::_ref (id.version.revision) &&
+ bld_query::_ref (id.version.canonical_upstream) &&
+ qv.canonical_release ==
+ bld_query::_ref (id.version.canonical_release) &&
+ qv.revision == bld_query::_ref (id.version.revision) &&
bld_query::id.configuration.in_range (cfg_names.begin (),
- cfg_names.end ()) &&
+ cfg_names.end ()) &&
compare_version_eq (bld_query::id.toolchain_version,
toolchain_version,
- true) &&
+ true) &&
(bld_query::state == "built" ||
((bld_query::force == "forcing" &&
@@ -467,13 +475,14 @@ handle (request& rq, response& rs)
shared_ptr<build> b (build_db_->find<build> (bid));
optional<string> cl (challenge ());
- // If build configuration doesn't exist then create the new one and
- // persist. Otherwise put it into the building state, refresh the
- // timestamp and update.
+ // If build configuration doesn't exist then create the new one
+ // and persist. Otherwise put it into the building state, refresh
+ // the timestamp and update.
//
if (b == nullptr)
{
- b = make_shared<build> (move (bid.package.name),
+ b = make_shared<build> (move (bid.package.tenant),
+ move (bid.package.name),
move (bp.version),
move (bid.configuration),
move (tqm.toolchain_name),
@@ -493,9 +502,9 @@ handle (request& rq, response& rs)
//
// Note that in both cases we keep the status intact to be able
// to compare it with the final one in the result request
- // handling in order to decide if to send the notification email.
- // The same is true for the forced flag (in the sense that we
- // don't set the force state to unforced).
+ // handling in order to decide if to send the notification
+ // email. The same is true for the forced flag (in the sense
+ // that we don't set the force state to unforced).
//
// Load the section to assert the above statement.
//
@@ -558,8 +567,7 @@ handle (request& rq, response& rs)
// 2: overall status
// 3: timestamp (less is preferred)
//
- auto cmp = [] (const shared_ptr<build>& x,
- const shared_ptr<build>& y) -> bool
+ auto cmp = [] (const shared_ptr<build>& x, const shared_ptr<build>& y)
{
if (x->force != y->force)
return x->force > y->force; // Forced goes first.
@@ -611,7 +619,8 @@ handle (request& rq, response& rs)
shared_ptr<build_package> p (
build_db_->find<build_package> (b->id.package));
- if (p != nullptr && p->internal_repository != nullptr &&
+ if (p != nullptr &&
+ p->internal_repository != nullptr &&
!exclude (*p, *cm.config))
{
assert (b->status);
diff --git a/mod/mod-builds.cxx b/mod/mod-builds.cxx
index 19d187c..d2e3a25 100644
--- a/mod/mod-builds.cxx
+++ b/mod/mod-builds.cxx
@@ -92,7 +92,9 @@ transform (const string& s)
template <typename T>
static inline query<T>
-build_query (const brep::cstrings& configs, const brep::params::builds& params)
+build_query (const brep::cstrings& configs,
+ const brep::params::builds& params,
+ const brep::optional<string>& tenant)
{
using namespace brep;
using query = query<T>;
@@ -102,6 +104,11 @@ build_query (const brep::cstrings& configs, const brep::params::builds& params)
? qb::id.configuration.in_range (configs.begin (), configs.end ())
: query (true));
+ const auto& pid (qb::id.package);
+
+ if (tenant)
+ q = q && pid.tenant == *tenant;
+
// Note that there is no error reported if the filter parameters parsing
// fails. Instead, it is considered that no package builds match such a
// query.
@@ -111,13 +118,13 @@ build_query (const brep::cstrings& configs, const brep::params::builds& params)
// Package name.
//
if (!params.name ().empty ())
- q = q && qb::id.package.name.like (
- package_name (transform (params.name ()), package_name::raw_string));
+ q = q && pid.name.like (package_name (transform (params.name ()),
+ package_name::raw_string));
// Package version.
//
if (!params.version ().empty () && params.version () != "*")
- q = q && compare_version_eq (qb::id.package.version,
+ q = q && compare_version_eq (pid.version,
version (params.version ()), // May throw.
true);
@@ -200,12 +207,12 @@ build_query (const brep::cstrings& configs, const brep::params::builds& params)
template <typename T, typename P = typename query<T>::build_package>
static inline query<T>
-package_query (const brep::params::builds& params)
+package_query (const brep::params::builds& params, const string& tenant)
{
using namespace brep;
using query = query<T>;
- query q (true);
+ query q (P::id.tenant == tenant);
// Note that there is no error reported if the filter parameters parsing
// fails. Instead, it is considered that no packages match such a query.
@@ -240,22 +247,24 @@ package_id_eq (const ID& x, const brep::package_id& y)
using query = query<T>;
const auto& qv (x.version);
- return x.name == query::_ref (y.name) &&
- qv.epoch == query::_ref (y.version.epoch) &&
+ return
+ x.tenant == query::_ref (y.tenant) &&
+ x.name == query::_ref (y.name) &&
+ qv.epoch == query::_ref (y.version.epoch) &&
qv.canonical_upstream == query::_ref (y.version.canonical_upstream) &&
- qv.canonical_release == query::_ref (y.version.canonical_release) &&
+ qv.canonical_release == query::_ref (y.version.canonical_release) &&
qv.revision == query::_ref (y.version.revision);
}
static const vector<pair<string, string>> build_results ({
- {"unbuilt", "<unbuilt>"},
- {"*", "*"},
- {"pending", "pending"},
+ {"unbuilt", "<unbuilt>"},
+ {"*", "*"},
+ {"pending", "pending"},
{"building", "building"},
- {"success", "success"},
- {"warning", "warning"},
- {"error", "error"},
- {"abort", "abort"},
+ {"success", "success"},
+ {"warning", "warning"},
+ {"error", "error"},
+ {"abort", "abort"},
{"abnormal", "abnormal"}});
bool brep::builds::
@@ -309,7 +318,7 @@ handle (request& rq, response& rs)
<< SCRIPT << " " << ~SCRIPT
<< ~HEAD
<< BODY
- << DIV_HEADER (root, options_->logo (), options_->menu ())
+ << DIV_HEADER (options_->logo (), options_->menu (), root, tenant)
<< DIV(ID="content");
// Return the list of distinct toolchain name/version pairs. The build db
@@ -323,6 +332,7 @@ handle (request& rq, response& rs)
toolchains r;
for (auto& t: build_db_->query<toolchain> (
+ (query::id.package.tenant == tenant) +
"ORDER BY" + query::toolchain_name +
order_by_version_desc (query::id.toolchain_version, false)))
r.emplace_back (move (t.name), move (t.version));
@@ -356,11 +366,11 @@ handle (request& rq, response& rs)
}
// The 'action' attribute is optional in HTML5. While the standard
- // doesn't specify browser behavior explicitly for the case the attribute
- // is omitted, the only reasonable behavior is to default it to the
- // current document URL. Note that we specify the function name using the
- // "hidden" <input/> element since the action url must not contain the
- // query part.
+ // doesn't specify browser behavior explicitly for the case the
+ // attribute is omitted, the only reasonable behavior is to default it
+ // to the current document URL. Note that we specify the function name
+ // using the "hidden" <input/> element since the action url must not
+ // contain the query part.
//
s << FORM
<< TABLE(ID="filter", CLASS="proplist")
@@ -418,7 +428,7 @@ handle (request& rq, response& rs)
transaction t (build_db_->begin ());
count = build_db_->query_value<package_build_count> (
- build_query<package_build_count> (*build_conf_names_, params));
+ build_query<package_build_count> (*build_conf_names_, params, tenant));
// Print the filter form.
//
@@ -433,8 +443,8 @@ handle (request& rq, response& rs)
//
s << DIV;
for (auto& pb: build_db_->query<package_build> (
- build_query<package_build> (*build_conf_names_, params) +
- "ORDER BY" + query<build>::timestamp + "DESC" +
+ build_query<package_build> (*build_conf_names_, params, tenant) +
+ "ORDER BY" + query<package_build>::build::timestamp + "DESC" +
"OFFSET" + to_string (page * page_configs) +
"LIMIT" + to_string (page_configs)))
{
@@ -451,8 +461,8 @@ handle (request& rq, response& rs)
s << TABLE(CLASS="proplist build")
<< TBODY
- << TR_NAME (b.package_name, string (), root)
- << TR_VERSION (b.package_name, b.package_version, root)
+ << TR_NAME (b.package_name, string (), root, tenant)
+ << TR_VERSION (b.package_name, b.package_version, root, tenant)
<< TR_VALUE ("toolchain",
b.toolchain_name + '-' +
b.toolchain_version.string ())
@@ -470,8 +480,8 @@ handle (request& rq, response& rs)
}
else // Print unbuilt package configurations.
{
- // Parameters to use for package build configurations queries. Note that we
- // cleanup the machine and the result filter arguments, as they are
+ // Parameters to use for package build configurations queries. Note that
+ // we cleanup the machine and the result filter arguments, as they are
// irrelevant for unbuilt configurations.
//
params::builds bld_params (params);
@@ -506,8 +516,8 @@ handle (request& rq, response& rs)
}
};
- // Note that config_toolchains contains shallow references to the toolchain
- // names and versions.
+ // Note that config_toolchains contains shallow references to the
+ // toolchain names and versions.
//
set<config_toolchain> config_toolchains;
{
@@ -576,12 +586,15 @@ handle (request& rq, response& rs)
// due to the build configuration target change. We should deduct such
// builds count from the number of existing package builds.
//
- size_t nmax (config_toolchains.size () *
- build_db_->query_value<buildable_package_count> (
- package_query<buildable_package_count> (params)));
+ size_t nmax (
+ config_toolchains.size () *
+ build_db_->query_value<buildable_package_count> (
+ package_query<buildable_package_count> (params, tenant)));
size_t ncur = build_db_->query_value<package_build_count> (
- build_query<package_build_count> (*build_conf_names_, bld_params));
+ build_query<package_build_count> (*build_conf_names_,
+ bld_params,
+ tenant));
// From now we will be using specific package name and version for each
// build database query.
@@ -602,11 +615,18 @@ handle (request& rq, response& rs)
package_id id;
string config;
+ const auto& bid (bld_query::build::id);
+
bld_query bq (
- package_id_eq<package_build_count> (
- bld_query::build::id.package, id) &&
- bld_query::build::id.configuration == bld_query::_ref (config) &&
- build_query<package_build_count> (cstrings (), bld_params));
+ package_id_eq<package_build_count> (bid.package, id) &&
+ bid.configuration == bld_query::_ref (config) &&
+
+ // Note that the query already constrains the tenant via the build
+ // package id.
+ //
+ build_query<package_build_count> (cstrings () /* configs */,
+ bld_params,
+ nullopt /* tenant */));
prep_bld_query bld_prep_query (
build_db_->prepare_query<package_build_count> (
@@ -620,7 +640,8 @@ handle (request& rq, response& rs)
// form parameters.
//
using query = query<build_constrained_package>;
- query q (package_query<build_constrained_package, query> (params));
+ query q (package_query<build_constrained_package, query> (params,
+ tenant));
for (const auto& p: build_db_->query<build_constrained_package> (q))
{
@@ -675,13 +696,15 @@ handle (request& rq, response& rs)
using pkg_query = query<buildable_package>;
using prep_pkg_query = prepared_query<buildable_package>;
- pkg_query pq (package_query<buildable_package> (params));
+ pkg_query pq (package_query<buildable_package> (params, tenant));
// Specify the portion. Note that we will still be querying packages in
// chunks, not to hold locks for too long.
//
size_t offset (0);
+ // @@ TENANT: use tenant for sorting when add support for global view.
+ //
pq += "ORDER BY" +
pkg_query::build_package::id.name +
order_by_version_desc (pkg_query::build_package::id.version, false) +
@@ -706,7 +729,13 @@ handle (request& rq, response& rs)
bld_query bq (
package_id_eq<package_build> (bld_query::build::id.package, id) &&
- build_query<package_build> (*build_conf_names_, bld_params));
+
+ // Note that the query already constrains the tenant via the build
+ // package id.
+ //
+ build_query<package_build> (*build_conf_names_,
+ bld_params,
+ nullopt /* tenant */));
prep_bld_query bld_prep_query (
conn->prepare_query<package_build> ("mod-builds-build-query", bq));
@@ -805,8 +834,8 @@ handle (request& rq, response& rs)
s << TABLE(CLASS="proplist build")
<< TBODY
- << TR_NAME (id.name, string (), root)
- << TR_VERSION (id.name, p.version, root)
+ << TR_NAME (id.name, string (), root, tenant)
+ << TR_VERSION (id.name, p.version, root, tenant)
<< TR_VALUE ("toolchain",
string (ct.toolchain_name) + '-' +
ct.toolchain_version.string ())
@@ -829,7 +858,7 @@ handle (request& rq, response& rs)
s << ~DIV;
}
- string u (root.string () + "?builds");
+ string u (tenant_dir (root, tenant).string () + "?builds");
if (!params.name ().empty ())
{
diff --git a/mod/mod-ci.cxx b/mod/mod-ci.cxx
index 79472d0..e16a1a1 100644
--- a/mod/mod-ci.cxx
+++ b/mod/mod-ci.cxx
@@ -182,7 +182,7 @@ handle (request& rq, response& rs)
<< CSS_LINKS (path ("ci.css"), root)
<< ~HEAD
<< BODY
- << DIV_HEADER (root, options_->logo (), options_->menu ())
+ << DIV_HEADER (options_->logo (), options_->menu (), root, tenant)
<< DIV(ID="content") << *form_ << ~DIV
<< ~BODY
<< ~HTML;
diff --git a/mod/mod-package-details.cxx b/mod/mod-package-details.cxx
index 3db9e1f..6ec0b0f 100644
--- a/mod/mod-package-details.cxx
+++ b/mod/mod-package-details.cxx
@@ -50,7 +50,9 @@ init (scanner& s)
template <typename T>
static inline query<T>
-search_params (const brep::package_name& n, const brep::string& q)
+search_params (const brep::string& q,
+ const brep::string& t,
+ const brep::package_name& n)
{
using query = query<T>;
@@ -59,6 +61,8 @@ search_params (const brep::package_name& n, const brep::string& q)
? query ("NULL")
: "plainto_tsquery (" + query::_val (q) + ")") +
"," +
+ query::_val (t) +
+ "," +
query::_val (n) +
")";
}
@@ -100,12 +104,13 @@ handle (request& rq, response& rs)
try
{
+ using query = query<latest_package>;
+
package_name n (*rq.path ().rbegin ());
latest_package lp;
if (!package_db_->query_one<latest_package> (
- query<latest_package> ("(" + query<latest_package>::_val (n) + ")"),
- lp))
+ "(" + query::_val (tenant) + "," + query::_val (n) + ")", lp))
throw invalid_request (404, "Package '" + n.string () + "' not found");
pkg = package_db_->load<package> (lp.id);
@@ -115,7 +120,7 @@ handle (request& rq, response& rs)
throw invalid_request (400, "invalid package name format");
}
- const package_name& name (pkg->id.name);
+ const package_name& name (pkg->name);
const string ename (mime_url_encode (name.string (), false));
auto url = [&ename] (bool f = false,
@@ -156,7 +161,7 @@ handle (request& rq, response& rs)
<< SCRIPT << " " << ~SCRIPT
<< ~HEAD
<< BODY
- << DIV_HEADER (root, options_->logo (), options_->menu ())
+ << DIV_HEADER (options_->logo (), options_->menu (), root, tenant)
<< DIV(ID="content");
if (full)
@@ -187,7 +192,7 @@ handle (request& rq, response& rs)
s << TABLE(CLASS="proplist", ID="package")
<< TBODY
<< TR_LICENSE (licenses)
- << TR_PROJECT (pkg->project, root);
+ << TR_PROJECT (pkg->project, root, tenant);
if (pkg->url)
s << TR_URL (*pkg->url);
@@ -201,14 +206,14 @@ handle (request& rq, response& rs)
if (pkg->email)
s << TR_EMAIL (*pkg->email);
- s << TR_TAGS (pkg->tags, root)
+ s << TR_TAGS (pkg->tags, root, tenant)
<< ~TBODY
<< ~TABLE;
}
auto pkg_count (
package_db_->query_value<package_count> (
- search_params<package_count> (name, squery)));
+ search_params<package_count> (squery, tenant, name)));
s << FORM_SEARCH (squery)
<< DIV_COUNTER (pkg_count, "Version", "Versions");
@@ -218,7 +223,7 @@ handle (request& rq, response& rs)
s << DIV;
for (const auto& pr:
package_db_->query<package_search_rank> (
- search_params<package_search_rank> (name, squery) +
+ search_params<package_search_rank> (squery, tenant, name) +
"ORDER BY rank DESC, version_epoch DESC, "
"version_canonical_upstream DESC, version_canonical_release DESC, "
"version_revision DESC" +
@@ -229,7 +234,7 @@ handle (request& rq, response& rs)
s << TABLE(CLASS="proplist version")
<< TBODY
- << TR_VERSION (name, p->version, root)
+ << TR_VERSION (name, p->version, root, tenant)
// @@ Shouldn't we skip low priority row ? Don't think so, why?
//
@@ -256,8 +261,11 @@ handle (request& rq, response& rs)
//
// Hm, I am not so sure about this. Consider: stable/testing/unstable.
//
- s << TR_REPOSITORY (p->internal_repository.object_id (), root)
- << TR_DEPENDS (p->dependencies, root)
+ s << TR_REPOSITORY (
+ p->internal_repository.object_id ().canonical_name,
+ root,
+ tenant)
+ << TR_DEPENDS (p->dependencies, root, tenant)
<< TR_REQUIRES (p->requirements)
<< ~TBODY
<< ~TABLE;
diff --git a/mod/mod-package-search.cxx b/mod/mod-package-search.cxx
index a86e5a7..347abf1 100644
--- a/mod/mod-package-search.cxx
+++ b/mod/mod-package-search.cxx
@@ -67,13 +67,15 @@ init (scanner& s)
template <typename T>
static inline query<T>
-search_param (const brep::string& q)
+search_param (const brep::string& q, const brep::string& t)
{
using query = query<T>;
return "(" +
(q.empty ()
? query ("NULL")
: "plainto_tsquery (" + query::_val (q) + ")") +
+ "," +
+ query::_val (t) +
")";
}
@@ -133,7 +135,7 @@ handle (request& rq, response& rs)
<< SCRIPT << " " << ~SCRIPT
<< ~HEAD
<< BODY
- << DIV_HEADER (root, options_->logo (), options_->menu ())
+ << DIV_HEADER (options_->logo (), options_->menu (), root, tenant)
<< DIV(ID="content");
session sn;
@@ -141,17 +143,19 @@ handle (request& rq, response& rs)
auto pkg_count (
package_db_->query_value<latest_package_count> (
- search_param<latest_package_count> (squery)));
+ search_param<latest_package_count> (squery, tenant)));
s << FORM_SEARCH (squery)
<< DIV_COUNTER (pkg_count, "Package", "Packages");
// Enclose the subsequent tables to be able to use nth-child CSS selector.
//
+ // @@ TENANT: use tenant for sorting when add support for global view.
+ //
s << DIV;
for (const auto& pr:
package_db_->query<latest_package_search_rank> (
- search_param<latest_package_search_rank> (squery) +
+ search_param<latest_package_search_rank> (squery, tenant) +
"ORDER BY rank DESC, name" +
"OFFSET" + to_string (page * res_page) +
"LIMIT" + to_string (res_page)))
@@ -160,11 +164,11 @@ handle (request& rq, response& rs)
s << TABLE(CLASS="proplist package")
<< TBODY
- << TR_NAME (p->id.name, squery_param, root)
+ << TR_NAME (p->name, squery_param, root, tenant)
<< TR_SUMMARY (p->summary)
<< TR_LICENSE (p->license_alternatives)
- << TR_TAGS (p->project, p->tags, root)
- << TR_DEPENDS (p->dependencies, root)
+ << TR_TAGS (p->project, p->tags, root, tenant)
+ << TR_DEPENDS (p->dependencies, root, tenant)
<< TR_REQUIRES (p->requirements)
<< ~TBODY
<< ~TABLE;
@@ -174,7 +178,7 @@ handle (request& rq, response& rs)
t.commit ();
s << DIV_PAGER (page, pkg_count, res_page, options_->search_pages (),
- root.string () + squery_param)
+ tenant_dir (root, tenant).string () + squery_param)
<< ~DIV
<< ~BODY
<< ~HTML;
diff --git a/mod/mod-package-version-details.cxx b/mod/mod-package-version-details.cxx
index d2d96d2..ee7457a 100644
--- a/mod/mod-package-version-details.cxx
+++ b/mod/mod-package-version-details.cxx
@@ -131,7 +131,7 @@ handle (request& rq, response& rs)
try
{
- pkg = package_db_->load<package> (package_id (pn, ver));
+ pkg = package_db_->load<package> (package_id (tenant, pn, ver));
// If the requested package turned up to be an "external" one just
// respond that no "internal" package is present.
@@ -147,7 +147,7 @@ handle (request& rq, response& rs)
throw invalid_request (
404, "Package '" + pn.string () + ' ' + sver + "' not found");
- const string& name (pkg->id.name.string ());
+ const string& name (pkg->name.string ());
const string title (name + " " + sver);
xml::serializer s (rs.content (), title);
@@ -158,7 +158,7 @@ handle (request& rq, response& rs)
<< CSS_LINKS (path ("package-version-details.css"), root)
<< ~HEAD
<< BODY
- << DIV_HEADER (root, options_->logo (), options_->menu ())
+ << DIV_HEADER (options_->logo (), options_->menu (), root, tenant)
<< DIV(ID="content");
if (full)
@@ -166,7 +166,8 @@ handle (request& rq, response& rs)
s << DIV(ID="heading")
<< H1
- << A(HREF=root / path (mime_url_encode (name, false)))
+ << A(HREF=tenant_dir (root, tenant) /
+ path (mime_url_encode (name, false)))
<< name
<< ~A
<< "/"
@@ -195,7 +196,7 @@ handle (request& rq, response& rs)
<< TR_PRIORITY (pkg->priority)
<< TR_LICENSES (pkg->license_alternatives)
- << TR_REPOSITORY (rl.canonical_name (), root)
+ << TR_REPOSITORY (rl.canonical_name (), root, tenant)
<< TR_LOCATION (rl);
if (rl.type () == repository_type::pkg)
@@ -216,7 +217,7 @@ handle (request& rq, response& rs)
<< TABLE(CLASS="proplist", ID="package")
<< TBODY
- << TR_PROJECT (pkg->project, root);
+ << TR_PROJECT (pkg->project, root, tenant);
const auto& u (pkg->url);
@@ -246,7 +247,7 @@ handle (request& rq, response& rs)
if (be && ((pe && be != pe) || (!pe && be != em)))
s << TR_EMAIL (*be, "build-email");
- s << TR_TAGS (pkg->tags, root)
+ s << TR_TAGS (pkg->tags, root, tenant)
<< ~TBODY
<< ~TABLE;
@@ -306,7 +307,7 @@ handle (request& rq, response& rs)
}
else if (p->internal ())
{
- dir_path u (root / dir_path (ename));
+ dir_path u (tenant_dir (root, tenant) / dir_path (ename));
s << A(HREF=u) << dname << ~A;
if (dcon)
diff --git a/mod/mod-repository-details.cxx b/mod/mod-repository-details.cxx
index 36d5508..6d3b8c1 100644
--- a/mod/mod-repository-details.cxx
+++ b/mod/mod-repository-details.cxx
@@ -82,7 +82,7 @@ handle (request& rq, response& rs)
<< CSS_LINKS (path ("repository-details.css"), root)
<< ~HEAD
<< BODY
- << DIV_HEADER (root, options_->logo (), options_->menu ())
+ << DIV_HEADER (options_->logo (), options_->menu (), root, tenant)
<< DIV(ID="content");
transaction t (package_db_->begin ());
@@ -91,13 +91,14 @@ handle (request& rq, response& rs)
for (const auto& r:
package_db_->query<repository> (
- query::internal + "ORDER BY" + query::priority))
+ (query::internal && query::id.tenant == tenant) +
+ "ORDER BY" + query::priority))
{
//@@ Feels like a lot of trouble (e.g., id_attribute()) for very
// dubious value. A link to the package search page just for
// this repository would probably be more useful.
//
- string id (html_id (r.name));
+ string id (html_id (r.canonical_name));
s << H1(ID=id)
<< A(HREF="#" + web::mime_url_encode (id, false))
<< r.display_name
@@ -144,9 +145,9 @@ handle (request& rq, response& rs)
//
s << P << "REPOSITORY CERTIFICATE" << ~P
<< P
- << "CN=" << cert.name.c_str () + np + 1 << *BR
- << "O=" << cert.organization << *BR
- << email (cert.email)
+ << "CN=" << cert.name.c_str () + np + 1 << *BR
+ << "O=" << cert.organization << *BR
+ << email (cert.email)
<< ~P
<< P(CLASS="certfp") << cert.fingerprint << ~P
<< PRE(CLASS="certpem") << cert.pem << ~PRE;
diff --git a/mod/mod-repository-root.cxx b/mod/mod-repository-root.cxx
index 3b0ab1f..b8777fd 100644
--- a/mod/mod-repository-root.cxx
+++ b/mod/mod-repository-root.cxx
@@ -275,13 +275,27 @@ namespace brep
if (!rpath.sub (root))
return false;
- const path& lpath (rpath.leaf (root));
+ path lpath (rpath.leaf (root));
+
+ if (!lpath.empty ())
+ {
+ path::iterator i (lpath.begin ());
+ const string& s (*i);
+
+ if (s[0] == '@' && s.size () > 1)
+ {
+ tenant = string (s, 1);
+ lpath = path (++i, lpath.end ());
+ }
+ }
// Delegate the request handling to the selected sub-handler. Intercept
// exception handling to add sub-handler attribution.
//
- auto handle = [&rq, &rs, this] (const char* nm, bool fn = false) -> bool
+ auto handle = [&rq, &rs, this] (const char* nm, bool fn = false)
{
+ handler_->tenant = move (tenant);
+
try
{
// Delegate the handling straight away if the sub-handler is not a
diff --git a/mod/mod-submit.cxx b/mod/mod-submit.cxx
index 470bd45..1b93756 100644
--- a/mod/mod-submit.cxx
+++ b/mod/mod-submit.cxx
@@ -201,7 +201,7 @@ handle (request& rq, response& rs)
<< CSS_LINKS (path ("submit.css"), root)
<< ~HEAD
<< BODY
- << DIV_HEADER (root, options_->logo (), options_->menu ())
+ << DIV_HEADER (options_->logo (), options_->menu (), root, tenant)
<< DIV(ID="content") << *form_ << ~DIV
<< ~BODY
<< ~HTML;
diff --git a/mod/module.hxx b/mod/module.hxx
index 127cdab..25dce43 100644
--- a/mod/module.hxx
+++ b/mod/module.hxx
@@ -10,6 +10,7 @@
#include <libbrep/types.hxx>
#include <libbrep/utility.hxx>
+#include <mod/utility.hxx>
#include <mod/options.hxx>
#include <mod/diagnostics.hxx>
@@ -70,6 +71,13 @@ namespace brep
//
class handler: public web::handler
{
+ public:
+ // If not empty, denotes the repository tenant the request is for.
+ // Extracted by the handler implementation from the request (URL path,
+ // parameters, etc).
+ //
+ string tenant;
+
// Diagnostics.
//
protected:
@@ -89,7 +97,7 @@ namespace brep
// Set to true when the handler is successfully initialized.
//
- bool initialized_ {false};
+ bool initialized_ = false;
// Implementation details.
//
diff --git a/mod/page.cxx b/mod/page.cxx
index 46b5e71..46f4879 100644
--- a/mod/page.cxx
+++ b/mod/page.cxx
@@ -18,6 +18,7 @@
#include <libbrep/package.hxx>
#include <libbrep/package-odb.hxx>
+#include <mod/utility.hxx>
#include <mod/build-config.hxx> // build_log_url()
using namespace std;
@@ -61,11 +62,14 @@ namespace brep
s << DIV(ID="header-menu")
<< DIV(ID="header-menu-body");
+ dir_path root (tenant_dir (root_, tenant_));
+
for (const auto& m: menu_)
{
- const string& l (m.link[0] == '/' || m.link.find (':') != string::npos
- ? m.link
- : root_.string () + m.link);
+ const string& l (
+ m.link[0] == '/' || m.link.find (':') != string::npos
+ ? m.link
+ : root.string () + m.link);
s << A(HREF=l) << m.label << ~A;
}
@@ -191,7 +195,8 @@ namespace brep
// Propagate search criteria to the package details page.
//
- << root_ / path (mime_url_encode (name_.string (), false))
+ << tenant_dir (root_, tenant_) /
+ path (mime_url_encode (name_.string (), false))
<< query_param_
<< ~HREF
@@ -221,8 +226,9 @@ namespace brep
}
else
{
- assert (root_ != nullptr);
- s << A(HREF=*root_ /
+ assert (root_ != nullptr && tenant_ != nullptr);
+
+ s << A(HREF=tenant_dir (*root_, *tenant_) /
dir_path (mime_url_encode (package_->string (), false)) /
path (version_))
<< version_;
@@ -249,7 +255,8 @@ namespace brep
<< SPAN(CLASS="value")
<< A
<< HREF
- << root_ << "?q=" << mime_url_encode (project_.string ())
+ << tenant_dir (root_, tenant_) << "?q="
+ << mime_url_encode (project_.string ())
<< ~HREF
<< project_
<< ~A
@@ -347,7 +354,10 @@ namespace brep
auto print = [&s, this] (const string& t)
{
- s << A << HREF << root_ << "?q=" << mime_url_encode (t) << ~HREF
+ s << A
+ << HREF
+ << tenant_dir (root_, tenant_) << "?q=" << mime_url_encode (t)
+ << ~HREF
<< t
<< ~A;
};
@@ -439,7 +449,7 @@ namespace brep
if (r->interface_url)
s << A(HREF=*r->interface_url + en) << n << ~A;
else if (p->internal ())
- s << A(HREF=root_ / path (en)) << n << ~A;
+ s << A(HREF=tenant_dir (root_, tenant_) / path (en)) << n << ~A;
else
// Display the dependency as plain text if no repository URL
// available.
@@ -586,7 +596,8 @@ namespace brep
<< SPAN(CLASS="value")
<< A
<< HREF
- << root_ << "?about#" << mime_url_encode (html_id (name_), false)
+ << tenant_dir (root_, tenant_) << "?about#"
+ << mime_url_encode (html_id (name_), false)
<< ~HREF
<< name_
<< ~A
diff --git a/mod/page.hxx b/mod/page.hxx
index 759984e..d3e10db 100644
--- a/mod/page.hxx
+++ b/mod/page.hxx
@@ -44,18 +44,20 @@ namespace brep
class DIV_HEADER
{
public:
- DIV_HEADER (const dir_path& root,
- const web::xhtml::fragment& logo,
- const vector<page_menu>& menu):
- root_ (root), logo_ (logo), menu_ (menu) {}
+ DIV_HEADER (const web::xhtml::fragment& logo,
+ const vector<page_menu>& menu,
+ const dir_path& root,
+ const string& tenant):
+ logo_ (logo), menu_ (menu), root_ (root), tenant_ (tenant) {}
void
operator() (xml::serializer&) const;
private:
- const dir_path& root_;
const web::xhtml::fragment& logo_;
const vector<page_menu>& menu_;
+ const dir_path& root_;
+ const string& tenant_;
};
// Generates package search form element.
@@ -163,8 +165,11 @@ namespace brep
class TR_NAME
{
public:
- TR_NAME (const package_name& n, const string& q, const dir_path& r)
- : name_ (n), query_param_ (q), root_ (r) {}
+ TR_NAME (const package_name& n,
+ const string& q,
+ const dir_path& r,
+ const string& t)
+ : name_ (n), query_param_ (q), root_ (r), tenant_ (t) {}
void
operator() (xml::serializer&) const;
@@ -173,6 +178,7 @@ namespace brep
const package_name& name_;
const string& query_param_;
const dir_path& root_;
+ const string& tenant_;
};
// Generates package version element.
@@ -182,11 +188,15 @@ namespace brep
public:
// Display the version as a link to the package version details page.
//
- TR_VERSION (const package_name& p, const version& v, const dir_path& r)
+ TR_VERSION (const package_name& p,
+ const version& v,
+ const dir_path& r,
+ const string& t)
: package_ (&p),
version_ (v.string ()),
stub_ (v.compare (wildcard_version, true) == 0),
- root_ (&r)
+ root_ (&r),
+ tenant_ (&t)
{
}
@@ -196,7 +206,8 @@ namespace brep
: package_ (nullptr),
version_ (v.string ()),
stub_ (v.compare (wildcard_version, true) == 0),
- root_ (nullptr)
+ root_ (nullptr),
+ tenant_ (nullptr)
{
}
@@ -208,6 +219,7 @@ namespace brep
string version_;
bool stub_;
const dir_path* root_;
+ const string* tenant_;
};
// Generates package project name element.
@@ -218,8 +230,8 @@ namespace brep
class TR_PROJECT
{
public:
- TR_PROJECT (const package_name& p, const dir_path& r)
- : project_ (p), root_ (r) {}
+ TR_PROJECT (const package_name& p, const dir_path& r, const string& t)
+ : project_ (p), root_ (r), tenant_ (t) {}
void
operator() (xml::serializer&) const;
@@ -227,6 +239,7 @@ namespace brep
private:
const package_name& project_;
const dir_path& root_;
+ const string& tenant_;
};
// Generates package summary element.
@@ -279,14 +292,17 @@ namespace brep
public:
// Display the tag link list.
//
- TR_TAGS (const strings& ts, const dir_path& r)
- : project_ (nullptr), tags_ (ts), root_ (r) {}
+ TR_TAGS (const strings& ts, const dir_path& r, const string& t)
+ : project_ (nullptr), tags_ (ts), root_ (r), tenant_ (t) {}
// As above but prepend the list with a tag link produced from the project
// name, if it differs from other tags.
//
- TR_TAGS (const package_name& pr, const strings& ts, const dir_path& r)
- : project_ (&pr), tags_ (ts), root_ (r) {}
+ TR_TAGS (const package_name& pr,
+ const strings& ts,
+ const dir_path& r,
+ const string& t)
+ : project_ (&pr), tags_ (ts), root_ (r), tenant_ (t) {}
void
operator() (xml::serializer&) const;
@@ -295,6 +311,7 @@ namespace brep
const package_name* project_;
const strings& tags_;
const dir_path& root_;
+ const string& tenant_;
};
// Generates package dependencies element.
@@ -302,8 +319,8 @@ namespace brep
class TR_DEPENDS
{
public:
- TR_DEPENDS (const dependencies& d, const dir_path& r)
- : dependencies_ (d), root_ (r) {}
+ TR_DEPENDS (const dependencies& d, const dir_path& r, const string& t)
+ : dependencies_ (d), root_ (r), tenant_ (t) {}
void
operator() (xml::serializer&) const;
@@ -311,6 +328,7 @@ namespace brep
private:
const dependencies& dependencies_;
const dir_path& root_;
+ const string& tenant_;
};
// Generates package requirements element.
@@ -377,8 +395,8 @@ namespace brep
class TR_REPOSITORY
{
public:
- TR_REPOSITORY (const string& n, const dir_path& r)
- : name_ (n), root_ (r) {}
+ TR_REPOSITORY (const string& n, const dir_path& r, const string& t)
+ : name_ (n), root_ (r), tenant_ (t) {}
void
operator() (xml::serializer&) const;
@@ -386,6 +404,7 @@ namespace brep
private:
const string& name_;
const dir_path& root_;
+ const string& tenant_;
};
// Generates repository location element.
diff --git a/mod/utility.hxx b/mod/utility.hxx
new file mode 100644
index 0000000..2ce1e07
--- /dev/null
+++ b/mod/utility.hxx
@@ -0,0 +1,25 @@
+// file : mod/utility.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef MOD_UTILITY_HXX
+#define MOD_UTILITY_HXX
+
+#include <libbrep/types.hxx>
+#include <libbrep/utility.hxx>
+
+namespace brep
+{
+ // Append the @<tenant> leaf component to the directory if the tenant is
+ // not empty. Otherwise, return the directory unchanged.
+ //
+ inline dir_path
+ tenant_dir (const dir_path& dir, const string& tenant)
+ {
+ return !tenant.empty ()
+ ? path_cast<dir_path> (dir / ('@' + tenant))
+ : dir;
+ }
+}
+
+#endif // MOD_UTILITY_HXX
diff --git a/tests/ci/ci-load.testscript b/tests/ci/ci-load.testscript
index de23b79..c574a32 100644
--- a/tests/ci/ci-load.testscript
+++ b/tests/ci/ci-load.testscript
@@ -55,12 +55,14 @@
cat <<"EOI" >=$loader;
#!/usr/bin/env bash
- if [ "\$#" != 5 -o \
+ if [ "\$#" != 7 -o \
"\$1" != "--db-host=localhost" -o \
"\$2" != "--db-port=8432" -o \
"\$3" != "--force" -o \
"\$4" != "--shallow" -o \
- "\$5" != "$data_dir/loadtab" -o \
+ "\$5" != "--tenant" -o \
+ -z "\$6" -o \
+ "\$7" != "$data_dir/loadtab" -o \
! -f "$data_dir/cache/repositories.manifest" -o \
! -f "$data_dir/cache/packages.manifest" ]; then
exit 1
@@ -74,7 +76,7 @@
$clone_root_data;
- $* $loader --db-host=localhost --db-port=8432 $data_dir >>"EOO"
+ $* $loader --db-host=localhost --db-port=8432 $data_dir >>"EOO";
: 1
status: 200
message: CI request is queued
@@ -85,10 +87,11 @@
$* --result-url "http://example.com/" \
$loader --db-host=localhost --db-port=8432 \
- $data_dir >>"EOO"
+ $data_dir >>~"%EOO%"
: 1
status: 200
- message: CI request is queued: http://example.com
+ %message: CI request is queued: http://example.com/@.+%
+ %.
reference: $request_id
EOO
}
diff --git a/tests/load/driver.cxx b/tests/load/driver.cxx
index b387b9b..a36653c 100644
--- a/tests/load/driver.cxx
+++ b/tests/load/driver.cxx
@@ -35,7 +35,7 @@ check_location (shared_ptr<package>& p)
{
if (p->internal ())
return p->location && *p->location ==
- path (p->id.name.string () + "-" + p->version.string () + ".tar.gz");
+ path (p->name.string () + "-" + p->version.string () + ".tar.gz");
else
return !p->location;
}
@@ -63,12 +63,14 @@ namespace bpkg
static void
test_pkg_repos (const cstrings& loader_args,
const dir_path& loadtab_dir,
- odb::pgsql::database&);
+ odb::pgsql::database&,
+ const string& tenant);
static void
test_git_repos (const cstrings& loader_args,
const dir_path& loadtab_dir,
- odb::pgsql::database&);
+ odb::pgsql::database&,
+ const string& tenant);
int
main (int argc, char* argv[])
@@ -98,8 +100,9 @@ main (int argc, char* argv[])
return 1;
}
- // Parse the database options.
+ // Parse the tenant and database options.
//
+ string tenant;
string user;
string password;
string name ("brep_package");
@@ -109,7 +112,9 @@ main (int argc, char* argv[])
for (++i; i < argc - 1; ++i)
{
string n (argv[i]);
- if (n == "--db-user" || n == "-u")
+ if (n == "--tenant")
+ tenant = argv[++i];
+ else if (n == "--db-user" || n == "-u")
user = argv[++i];
else if (n == "--db-password")
password = argv[++i];
@@ -157,12 +162,12 @@ main (int argc, char* argv[])
{
case repository_type::pkg:
{
- test_pkg_repos (loader_args, loadtab_dir, db);
+ test_pkg_repos (loader_args, loadtab_dir, db, tenant);
break;
}
case repository_type::git:
{
- test_git_repos (loader_args, loadtab_dir, db);
+ test_git_repos (loader_args, loadtab_dir, db, tenant);
break;
}
default:
@@ -192,7 +197,8 @@ dep (const char* n, optional<dependency_constraint> c)
static void
test_git_repos (const cstrings& loader_args,
const dir_path& loadtab_dir,
- odb::pgsql::database& db)
+ odb::pgsql::database& db,
+ const string& tenant)
{
path loadtab (loadtab_dir / "git-loadtab");
@@ -212,13 +218,17 @@ test_git_repos (const cstrings& loader_args,
session s;
transaction t (db.begin ());
- assert (db.query<repository> ().size () == 1);
- assert (db.query<package> ().size () == 1);
+ assert (db.query<repository> (
+ query<repository>::id.tenant == tenant).size () == 1);
+
+ assert (db.query<package> (
+ query<package>::id.tenant == tenant).size () == 1);
// Verify 'foo' repository.
//
shared_ptr<repository> r (
- db.load<repository> ("git:example.com/foo#master"));
+ db.load<repository> (repository_id (tenant,
+ "git:example.com/foo#master")));
assert (r->location.string () == "https://git.example.com/foo.git#master");
assert (r->summary && *r->summary == "foo project repository");
@@ -229,7 +239,7 @@ test_git_repos (const cstrings& loader_args,
//
shared_ptr<package> p (
db.load<package> (
- package_id (package_name ("libfoo"), version ("1.0"))));
+ package_id (tenant, package_name ("libfoo"), version ("1.0"))));
assert (p->fragment &&
*p->fragment == "0f50af28d1cfb0c22f5b88e2bf674ab732e058d9");
@@ -249,7 +259,8 @@ test_git_repos (const cstrings& loader_args,
static void
test_pkg_repos (const cstrings& loader_args,
const dir_path& loadtab_dir,
- odb::pgsql::database& db)
+ odb::pgsql::database& db,
+ const string& tenant)
{
path p (loadtab_dir / dir_path ("1/stable") / packages);
timestamp srt (file_mtime (p));
@@ -273,23 +284,30 @@ test_pkg_repos (const cstrings& loader_args,
session s;
transaction t (db.begin ());
- assert (db.query<repository> ().size () == 7);
- assert (db.query<package> ().size () == 18);
+ assert (db.query<repository> (
+ query<repository>::id.tenant == tenant).size () == 7);
+
+ assert (db.query<package> (
+ query<package>::id.tenant == tenant).size () == 18);
shared_ptr<repository> sr (
- db.load<repository> ("pkg:dev.cppget.org/stable"));
+ db.load<repository> (repository_id (tenant,
+ "pkg:dev.cppget.org/stable")));
shared_ptr<repository> mr (
- db.load<repository> ("pkg:dev.cppget.org/math"));
+ db.load<repository> (repository_id (tenant,
+ "pkg:dev.cppget.org/math")));
shared_ptr<repository> cr (
- db.load<repository> ("pkg:dev.cppget.org/misc"));
+ db.load<repository> (repository_id (tenant, "pkg:dev.cppget.org/misc")));
shared_ptr<repository> tr (
- db.load<repository> ("pkg:dev.cppget.org/testing"));
+ db.load<repository> (repository_id (tenant,
+ "pkg:dev.cppget.org/testing")));
shared_ptr<repository> gr (
- db.load<repository> ("pkg:dev.cppget.org/staging"));
+ db.load<repository> (repository_id (tenant,
+ "pkg:dev.cppget.org/staging")));
// Verify 'stable' repository.
//
@@ -326,7 +344,7 @@ test_pkg_repos (const cstrings& loader_args,
//
shared_ptr<package> fpvxy (
db.load<package> (
- package_id (package_name ("libfoo"), version ("+0-X.Y"))));
+ package_id (tenant, package_name ("libfoo"), version ("+0-X.Y"))));
assert (fpvxy->project == package_name ("libfoo"));
assert (fpvxy->summary == "The Foo Library");
@@ -359,7 +377,7 @@ test_pkg_repos (const cstrings& loader_args,
//
shared_ptr<package> fpv1 (
db.load<package> (
- package_id (package_name ("libfoo"), version ("1.0"))));
+ package_id (tenant, package_name ("libfoo"), version ("1.0"))));
assert (fpv1->summary == "The Foo Library");
assert (fpv1->tags.empty ());
@@ -393,7 +411,7 @@ test_pkg_repos (const cstrings& loader_args,
//
shared_ptr<package> fpv2 (
db.load<package> (
- package_id (package_name ("libfoo"), version ("1.2.2"))));
+ package_id (tenant, package_name ("libfoo"), version ("1.2.2"))));
assert (fpv2->summary == "The Foo library");
assert (fpv2->tags == strings ({"c++", "foo"}));
@@ -435,7 +453,9 @@ test_pkg_repos (const cstrings& loader_args,
//
shared_ptr<package> fpv2a (
db.load<package> (
- package_id (package_name ("libfoo"), version ("1.2.2-alpha.1"))));
+ package_id (tenant,
+ package_name ("libfoo"),
+ version ("1.2.2-alpha.1"))));
assert (fpv2a->summary == "The Foo library");
assert (fpv2a->tags == strings ({"c++", "foo"}));
@@ -495,7 +515,7 @@ test_pkg_repos (const cstrings& loader_args,
//
shared_ptr<package> fpv3 (
db.load<package> (
- package_id (package_name ("libfoo"), version ("1.2.3+4"))));
+ package_id (tenant, package_name ("libfoo"), version ("1.2.3+4"))));
assert (fpv3->summary == "The Foo library");
assert (fpv3->tags == strings ({"c++", "foo"}));
@@ -531,7 +551,7 @@ test_pkg_repos (const cstrings& loader_args,
//
shared_ptr<package> fpv4 (
db.load<package> (
- package_id (package_name ("libfoo"), version ("1.2.4"))));
+ package_id (tenant, package_name ("libfoo"), version ("1.2.4"))));
assert (fpv4->summary == "The Foo Library");
assert (fpv4->tags == strings ({"c++", "foo"}));
@@ -597,7 +617,9 @@ test_pkg_repos (const cstrings& loader_args,
//
shared_ptr<package> xpv (
db.load<package> (
- package_id (package_name ("libstudxml"), version ("1.0.0+1"))));
+ package_id (tenant,
+ package_name ("libstudxml"),
+ version ("1.0.0+1"))));
assert (xpv->summary == "Modern C++ XML API");
assert (xpv->tags == strings ({"c++", "xml", "parser", "serializer",
@@ -646,7 +668,7 @@ test_pkg_repos (const cstrings& loader_args,
//
shared_ptr<package> fpv5 (
db.load<package> (
- package_id (package_name ("libfoo"), version ("1.2.4+1"))));
+ package_id (tenant, package_name ("libfoo"), version ("1.2.4+1"))));
assert (fpv5->summary == "The Foo Math Library");
assert (fpv5->tags == strings ({"c++", "foo", "math"}));
@@ -761,7 +783,7 @@ test_pkg_repos (const cstrings& loader_args,
//
shared_ptr<package> epv (
db.load<package> (
- package_id (package_name ("libexp"), version ("+2-1.2+1"))));
+ package_id (tenant, package_name ("libexp"), version ("+2-1.2+1"))));
assert (epv->project == "mathLab");
assert (epv->summary == "The exponent");
@@ -812,7 +834,9 @@ test_pkg_repos (const cstrings& loader_args,
// libpq-0
//
shared_ptr<package> qpv (
- db.load<package> (package_id (package_name ("libpq"), version ("0"))));
+ db.load<package> (package_id (tenant,
+ package_name ("libpq"),
+ version ("0"))));
assert (qpv->summary == "PostgreSQL C API client library");
@@ -849,7 +873,7 @@ test_pkg_repos (const cstrings& loader_args,
//
shared_ptr<package> bpv (
db.load<package> (
- package_id (package_name ("libbar"), version ("2.4.0+3"))));
+ package_id (tenant, package_name ("libbar"), version ("2.4.0+3"))));
assert (check_external (*bpv));
assert (bpv->other_repositories.size () == 1);
@@ -862,7 +886,7 @@ test_pkg_repos (const cstrings& loader_args,
//
shared_ptr<package> fpv0 (
db.load<package> (
- package_id (package_name ("libfoo"), version ("0.1"))));
+ package_id (tenant, package_name ("libfoo"), version ("0.1"))));
assert (check_external (*fpv0));
assert (fpv0->other_repositories.size () == 1);
@@ -873,7 +897,7 @@ test_pkg_repos (const cstrings& loader_args,
//
shared_ptr<package> fpv6 (
db.load<package> (
- package_id (package_name ("libfoo"), version ("1.2.4+2"))));
+ package_id (tenant, package_name ("libfoo"), version ("1.2.4+2"))));
assert (check_external (*fpv6));
assert (fpv6->other_repositories.size () == 1);
@@ -913,7 +937,7 @@ test_pkg_repos (const cstrings& loader_args,
//
shared_ptr<package> mpv0 (
db.load<package> (
- package_id (package_name ("libmisc"), version ("2.4.0"))));
+ package_id (tenant, package_name ("libmisc"), version ("2.4.0"))));
assert (check_external (*mpv0));
assert (mpv0->other_repositories.size () == 1);
@@ -924,7 +948,7 @@ test_pkg_repos (const cstrings& loader_args,
//
shared_ptr<package> mpv1 (
db.load<package> (
- package_id (package_name ("libmisc"), version ("2.3.0+1"))));
+ package_id (tenant, package_name ("libmisc"), version ("2.3.0+1"))));
assert (check_external (*mpv1));
assert (mpv1->other_repositories.size () == 1);
@@ -963,7 +987,7 @@ test_pkg_repos (const cstrings& loader_args,
//
shared_ptr<package> tpv (
db.load<package> (
- package_id (package_name ("libexpat"), version ("5.1"))));
+ package_id (tenant, package_name ("libexpat"), version ("5.1"))));
assert (check_external (*tpv));
assert (tpv->other_repositories.size () == 1);
@@ -976,7 +1000,7 @@ test_pkg_repos (const cstrings& loader_args,
//
shared_ptr<package> gpv (
db.load<package> (
- package_id (package_name ("libgenx"), version ("1.0"))));
+ package_id (tenant, package_name ("libgenx"), version ("1.0"))));
assert (check_external (*gpv));
assert (gpv->other_repositories.size () == 1);
@@ -989,7 +1013,7 @@ test_pkg_repos (const cstrings& loader_args,
//
shared_ptr<package> mpv2 (
db.load<package> (
- package_id (package_name ("libmisc"), version ("1.0"))));
+ package_id (tenant, package_name ("libmisc"), version ("1.0"))));
assert (check_external (*mpv2));
assert (mpv2->other_repositories.size () == 1);
@@ -1019,7 +1043,7 @@ test_pkg_repos (const cstrings& loader_args,
shared_ptr<package> bpv (
db.load<package> (
- package_id (package_name ("libbar"), version ("2.4.0+3"))));
+ package_id (tenant, package_name ("libbar"), version ("2.4.0+3"))));
assert (bpv->summary == "test");
@@ -1040,7 +1064,7 @@ test_pkg_repos (const cstrings& loader_args,
shared_ptr<package> bpv (
db.find<package> (
- package_id (package_name ("libbar"), version ("2.4.0+3"))));
+ package_id (tenant, package_name ("libbar"), version ("2.4.0+3"))));
// External package summary is not saved.
//