openvibers/openvibe.host Commits
certs renew runs again: a database without host_domains (dropped by 0008) has no custom domains (#141)
Changes
@@@ -255,12 +255,20 @@ function renderTenants(inv, svc, { wildcardCert, domains = [] } = {}) {/*** Verified custom domains from the Host database + certificate presence. A PostgreSQL database is* read as the postgres OS user over peer auth (Host moved to PostgreSQL; its service role's password-- * is not ovhost's to read).++ * is not ovhost's to read). A database without the table has no custom domains: OpenVibe.Host's migration++ * 0008 dropped host_domains when static hosting moved to OpenVibe.Space, and from then (2026-10-23) every++ * `ovhost certs renew` stopped at this read, before renewing anything.*/async function tenantDomains(exec, svc) {const t = tenantsConfig(svc);if (!t.database) throw new Error(`${svc.id}: nginx.tenants.database is not set in the inventory`);-- const rows = await exec.psql(t.database.database, VERIFIED_SQL);++ let rows;++ try {++ rows = await exec.psql(t.database.database, VERIFIED_SQL);++ } catch (err) {++ if (/relation "host_domains" does not exist/.test(String(err && err.message))) return [];++ throw err;++ }const out = [];for (const r of rows || []) {const h = String(r.hostname || '').toLowerCase();@@@ -63,6 +63,18 @@ runTests([assert.ok(!host.certbotCalls.some((c) => c.args.some((a) => /privkey|\.key$/.test(String(a)))), 'no command touched a key path');}),++ test('a database without host_domains (Host 0008) has no custom domains: the renewal still runs and succeeds', async () => {++ const host = issuer(withHost(scenario(), []));++ host.psqlHandler = () => { throw new Error('psql ov_host failed: ERROR: relation "host_domains" does not exist'); };++ const r = await host.cli('certs', 'renew', 'host', '--install');++ assert.strictEqual(r.code, 0, r.out);++ assert.ok(host.certbotCalls.some((c) => c.args[0] === 'renew'), 'the wildcard lineage is renewed');++ assert.ok(!host.certbotCalls.some((c) => c.args[0] === 'certonly'), 'nothing to issue');++ // Any other database failure still fails the command.++ host.psqlHandler = () => { throw new Error('psql ov_host failed: connection refused'); };++ assert.notStrictEqual((await host.cli('certs', 'renew', 'host', '--install')).code, 0);++ }),++test('issuance failure keeps the domain HTTP-only and fails the command', async () => {const host = withHost(scenario(), [{ hostname: 'docs.tenant-b.net' }]);host.certbot = (args) => (args[0] === 'certonly' ? { code: 1, stderr: 'Certbot failed to authenticate some domains: Challenge failed' } : { code: 0, stdout: '' });
@@ -255,12 +255,20 @@ function renderTenants(inv, svc, { wildcardCert, domains = [] } = {}) {
@@ -255,12 +255,20 @@ function renderTenants(inv, svc, { wildcardCert, domains = [] } = {}) {
/**
/**
* Verified custom domains from the Host database + certificate presence. A PostgreSQL database is
* Verified custom domains from the Host database + certificate presence. A PostgreSQL database is
* read as the postgres OS user over peer auth (Host moved to PostgreSQL; its service role's password
* read as the postgres OS user over peer auth (Host moved to PostgreSQL; its service role's password
- * is not ovhost's to read).
+ * is not ovhost's to read). A database without the table has no custom domains: OpenVibe.Host's migration
+ * 0008 dropped host_domains when static hosting moved to OpenVibe.Space, and from then (2026-10-23) every
+ * `ovhost certs renew` stopped at this read, before renewing anything.
*/
*/
async function tenantDomains(exec, svc) {
async function tenantDomains(exec, svc) {
const t = tenantsConfig(svc);
const t = tenantsConfig(svc);
if (!t.database) throw new Error(`${svc.id}: nginx.tenants.database is not set in the inventory`);
if (!t.database) throw new Error(`${svc.id}: nginx.tenants.database is not set in the inventory`);
- const rows = await exec.psql(t.database.database, VERIFIED_SQL);
+ let rows;
+ try {
+ rows = await exec.psql(t.database.database, VERIFIED_SQL);
+ } catch (err) {
+ if (/relation "host_domains" does not exist/.test(String(err && err.message))) return [];
+ throw err;
+ }
const out = [];
const out = [];
for (const r of rows || []) {
for (const r of rows || []) {
const h = String(r.hostname || '').toLowerCase();
const h = String(r.hostname || '').toLowerCase();
@@ -63,6 +63,18 @@ runTests([
@@ -63,6 +63,18 @@ runTests([
assert.ok(!host.certbotCalls.some((c) => c.args.some((a) => /privkey|\.key$/.test(String(a)))), 'no command touched a key path');
assert.ok(!host.certbotCalls.some((c) => c.args.some((a) => /privkey|\.key$/.test(String(a)))), 'no command touched a key path');
}),
}),
+ test('a database without host_domains (Host 0008) has no custom domains: the renewal still runs and succeeds', async () => {
+ const host = issuer(withHost(scenario(), []));
+ host.psqlHandler = () => { throw new Error('psql ov_host failed: ERROR: relation "host_domains" does not exist'); };
+ const r = await host.cli('certs', 'renew', 'host', '--install');
+ assert.strictEqual(r.code, 0, r.out);
+ assert.ok(host.certbotCalls.some((c) => c.args[0] === 'renew'), 'the wildcard lineage is renewed');
+ assert.ok(!host.certbotCalls.some((c) => c.args[0] === 'certonly'), 'nothing to issue');
+ // Any other database failure still fails the command.
+ host.psqlHandler = () => { throw new Error('psql ov_host failed: connection refused'); };
+ assert.notStrictEqual((await host.cli('certs', 'renew', 'host', '--install')).code, 0);
+ }),
+
test('issuance failure keeps the domain HTTP-only and fails the command', async () => {
test('issuance failure keeps the domain HTTP-only and fails the command', async () => {
const host = withHost(scenario(), [{ hostname: 'docs.tenant-b.net' }]);
const host = withHost(scenario(), [{ hostname: 'docs.tenant-b.net' }]);
host.certbot = (args) => (args[0] === 'certonly' ? { code: 1, stderr: 'Certbot failed to authenticate some domains: Challenge failed' } : { code: 0, stdout: '' });
host.certbot = (args) => (args[0] === 'certonly' ? { code: 1, stderr: 'Certbot failed to authenticate some domains: Challenge failed' } : { code: 0, stdout: '' });