diff --git a/.github/scripts/check_release_drift.js b/.github/scripts/check_release_drift.js new file mode 100644 index 0000000..fb4e29e --- /dev/null +++ b/.github/scripts/check_release_drift.js @@ -0,0 +1,109 @@ +#!/usr/bin/env node +/** + * Fails the build when the site's fallback release drifts from the release the + * package repository actually mirrors. + * + * The /packages page used to hardcode its versions, and they went stale: the + * page still advertised 0.114-0 (and 0.113-0 for the repository examples) after + * v0.116-0 had been published and mirrored. The page now reads + * out/packages/release-info.json at runtime, but it still needs a compiled-in + * fallback for the first paint and for the case where that fetch fails - so the + * fallback can drift in exactly the same way, just less visibly. + * + * This check closes that loop: whenever the deployment mirrors a release, the + * fallback baked into the bundle must name the same one. + * + * Skipped when release-info.json is absent, which is the normal case for a + * site-only build (BUILD_PACKAGES=false, or any fork without the packaging + * secrets). There is nothing to compare against then. + */ + +const fs = require("node:fs"); +const path = require("node:path"); + +const releaseInfoPath = path.join(process.cwd(), "out", "packages", "release-info.json"); +const fallbackSourcePath = path.join(process.cwd(), "app", "lib", "releaseInfo.ts"); + +if (!fs.existsSync(releaseInfoPath)) { + console.log(`No ${path.relative(process.cwd(), releaseInfoPath)}; skipping release drift check.`); + process.exit(0); +} + +const source = fs.readFileSync(fallbackSourcePath, "utf8"); + +/** Reads a string field out of the FALLBACK_RELEASE object literal. */ +function fallbackField(field) { + const match = new RegExp(`${field}:\\s*"([^"]+)"`).exec(source); + return match ? match[1] : null; +} + +const mirrored = JSON.parse(fs.readFileSync(releaseInfoPath, "utf8")); +const mirroredTag = mirrored.tag_name; +if (typeof mirroredTag !== "string" || mirroredTag.length === 0) { + console.error("release-info.json has no tag_name; cannot verify the site's fallback release."); + process.exit(1); +} + +const fallbackTag = fallbackField("tagName"); +if (fallbackTag !== mirroredTag) { + console.error( + [ + "Release drift: the site's fallback release does not match the mirrored release.", + "", + ` mirrored (out/packages/release-info.json): ${mirroredTag}`, + ` fallback (app/lib/releaseInfo.ts): ${fallbackTag ?? ""}`, + "", + "Update FALLBACK_RELEASE in app/lib/releaseInfo.ts to the mirrored release.", + "It is what visitors see before the release feed loads, and permanently if", + "that fetch fails.", + ].join("\n"), + ); + process.exit(1); +} + +// The version strings are derived from real asset filenames, so a mismatch here +// means the fallback would render install commands for packages the release +// does not contain. +const assetNames = Array.isArray(mirrored.assets) + ? mirrored.assets.map((asset) => asset && asset.name).filter((name) => typeof name === "string") + : []; + +const derived = [ + { + field: "aptVersion", + pattern: /^ubuntu[\d.]+-postgresql-\d+-documentdb_([^_]+)_/, + }, + { + field: "rpmVersion", + pattern: /^rhel\d+-postgresql\d+-documentdb-(.+)\.(?:x86_64|aarch64)\.rpm$/, + }, + { + field: "metaVersion", + pattern: /^ubuntu[\d.]+-documentdb_([^_]+)_all\.deb$/, + }, +]; + +let failed = false; +for (const { field, pattern } of derived) { + const actual = assetNames.map((name) => pattern.exec(name)).find((m) => m && m[1]); + if (!actual) { + // The release simply does not ship that package shape; the fallback keeps + // whatever it had, which is not drift. + continue; + } + const expected = actual[1]; + const declared = fallbackField(field); + if (declared !== expected) { + console.error( + `Release drift: FALLBACK_RELEASE.${field} is "${declared}" but ${mirroredTag} ships "${expected}".`, + ); + failed = true; + } +} + +if (failed) { + console.error("\nUpdate FALLBACK_RELEASE in app/lib/releaseInfo.ts."); + process.exit(1); +} + +console.log(`Release fallback matches the mirrored release (${mirroredTag}).`); diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index 27431c1..4032dd1 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -266,6 +266,9 @@ jobs: # downloading ~500 MB of PostgreSQL and PostGIS, which keeps the check to # a few seconds while still catching the entire "package missing from the # pool / unsatisfiable dependency" class. + - name: Verify the site's fallback release matches the mirrored release + if: steps.features.outputs.packages == 'true' + run: node .github/scripts/check_release_drift.js - name: Smoke test the generated repository (dependency resolution) if: steps.features.outputs.packages == 'true' run: | diff --git a/PACKAGE-INSTALL.md b/PACKAGE-INSTALL.md index 7d0b248..d30d279 100644 --- a/PACKAGE-INSTALL.md +++ b/PACKAGE-INSTALL.md @@ -102,6 +102,14 @@ mongosh 'mongodb://admin:@127.0.0.1:10260/mydb?tls=true&tlsAllowInvali --eval 'db.runCommand({ping: 1})' ``` +If the password contains `@`, `:`, `/` or other reserved characters it must be percent-encoded +in the URI (`@` becomes `%40`). To avoid encoding entirely, pass the credentials as flags: + +```bash +mongosh localhost:10260 -u admin -p --authenticationMechanism SCRAM-SHA-256 \ + --tls --tlsAllowInvalidCertificates --eval 'db.runCommand({ping: 1})' +``` + A first database and collection are created on first write: ```javascript @@ -163,11 +171,22 @@ instead; re-run `documentdb-setup` to restart it. **Remove or reset:** ```bash +# Stop the stack first — package removal deletes files but does not stop a +# running gateway. On systemd hosts: +sudo systemctl stop documentdb-local@18.target +# Without systemd the wizard started the gateway directly; kill that process. + sudo documentdb-setup --restore # detach the managed integration sudo documentdb-local-reset --pg-version 18 --confirm-destroy # DESTROYS the data directory -sudo apt remove documentdb # or: sudo dnf remove documentdb + +# Name the package you installed AND the extension: autoremove does not reap +# postgresql-18-documentdb, and `remove` would leave its config behind. +sudo apt purge --autoremove documentdb-18 postgresql-18-documentdb +sudo dnf remove documentdb-18 postgresql18-documentdb && sudo dnf autoremove ``` +If you installed the `documentdb` meta package rather than `documentdb-18`, name that instead. + ### What the packages are | Package | Role | diff --git a/app/lib/packageInstall.ts b/app/lib/packageInstall.ts index 9ed15e5..c294973 100644 --- a/app/lib/packageInstall.ts +++ b/app/lib/packageInstall.ts @@ -39,12 +39,50 @@ const rpmMajorVersions: Record = { rhel9: "9", }; +// Distributions where the repository serves the full v0.116-0 package set +// (`documentdb` meta, `documentdb-N`, `documentdb-common`, `documentdb-gateway`, +// `documentdb-postgresql-tools`) rather than the extension package alone. +// v0.116-0 ships Tier-1 only, so everywhere else still resolves the older +// extension-only release and must keep the `postgresql-N-documentdb` command. +export const aptFullStackDistros: readonly AptDistro[] = ["ubuntu24"]; +export const rpmFullStackDistros: readonly RpmDistro[] = ["rhel9"]; + +// The stand-alone packages exist only for the majors the full stack was built +// for. PostgreSQL 16 resolves the older extension-only build even on a +// full-stack distribution, so it must not be offered the stand-alone command. +const fullStackPgVersions = ["17", "18"]; + +export function aptServesFullStack( + aptTarget: AptDistro, + aptPgVersion: AptPgVersion, +): boolean { + return ( + aptFullStackDistros.includes(aptTarget) && + fullStackPgVersions.includes(aptPgVersion) + ); +} + +export function rpmServesFullStack( + rpmTarget: RpmDistro, + rpmPgVersion: RpmPgVersion, +): boolean { + return ( + rpmFullStackDistros.includes(rpmTarget) && + fullStackPgVersions.includes(rpmPgVersion) + ); +} + export function buildAptInstallCommand( aptTarget: AptDistro, aptArch: AptArch, aptPgVersion: AptPgVersion, ): string { const pgdgSuite = aptPgdgSuites[aptTarget]; + // `documentdb-N` pulls the whole stack (extension + gateway + tools + + // documentdb-common) and owns the systemd lifecycle for that major. + const installTarget = aptServesFullStack(aptTarget, aptPgVersion) + ? `documentdb-${aptPgVersion}` + : `postgresql-${aptPgVersion}-documentdb`; return `sudo apt update && \\ sudo apt install -y curl ca-certificates gnupg && \\ @@ -53,7 +91,7 @@ echo "deb [signed-by=/usr/share/keyrings/postgresql.gpg] https://apt.postgresql. curl -fsSL https://documentdb.io/documentdb-archive-keyring.gpg | sudo gpg --dearmor --yes -o /usr/share/keyrings/documentdb-archive-keyring.gpg && \\ echo "deb [arch=${aptArch} signed-by=/usr/share/keyrings/documentdb-archive-keyring.gpg] https://documentdb.io/deb stable ${aptTarget}" | sudo tee /etc/apt/sources.list.d/documentdb.list >/dev/null && \\ sudo apt update && \\ -sudo apt install -y postgresql-${aptPgVersion}-documentdb`; +sudo apt install -y ${installTarget}`; } export function buildRpmInstallCommand( @@ -62,6 +100,9 @@ export function buildRpmInstallCommand( rpmPgVersion: RpmPgVersion, ): string { const rhelMajorVersion = rpmMajorVersions[rpmTarget]; + const installTarget = rpmServesFullStack(rpmTarget, rpmPgVersion) + ? `documentdb-${rpmPgVersion}` + : `postgresql${rpmPgVersion}-documentdb`; return `sudo dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-${rhelMajorVersion}.noarch.rpm && \\ sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-${rhelMajorVersion}-${rpmArch}/pgdg-redhat-repo-latest.noarch.rpm && \\ @@ -78,5 +119,12 @@ printf '%s\\n' \\ 'enabled=1' \\ 'gpgcheck=1' \\ 'gpgkey=https://documentdb.io/documentdb-archive-keyring.gpg' | sudo tee /etc/yum.repos.d/documentdb.repo >/dev/null && \\ -sudo dnf install -y postgresql${rpmPgVersion}-documentdb`; +sudo dnf install -y ${installTarget}`; +} + +// Shown after a full-stack install: the packages ship a wizard that creates the +// PostgreSQL instance, installs the extensions and starts the gateway, so the +// install command alone does not leave a reachable endpoint. +export function buildSetupCommand(): string { + return `sudo documentdb-setup --admin-user admin`; } diff --git a/app/lib/releaseInfo.ts b/app/lib/releaseInfo.ts new file mode 100644 index 0000000..0171b0b --- /dev/null +++ b/app/lib/releaseInfo.ts @@ -0,0 +1,142 @@ +"use client"; + +import { useEffect, useState } from "react"; + +// The site publishes out/packages/release-info.json on every deployment, built +// from the GitHub release the package repository actually mirrors. It is the +// only authoritative statement of "what version is on documentdb.io", so the +// UI derives its version strings from it rather than repeating them. +// +// Before this module the versions were hardcoded in the page, and they drifted: +// the page still advertised 0.114-0 (and 0.113-0 for the repository examples) +// after v0.116-0 had been published and mirrored. + +export type ReleaseInfo = { + /** Git tag of the mirrored release, e.g. "v0.116-0". */ + tagName: string; + /** Extension package version on DEB, e.g. "0.116-0". */ + aptVersion: string; + /** Extension package version on RPM, e.g. "0.116.0-1.el9". */ + rpmVersion: string; + /** Version of every non-extension package, e.g. "0.116.0". */ + metaVersion: string; + releaseUrl: string; + assetNames: readonly string[]; +}; + +// Used until the fetch resolves, and permanently if it fails. A stale-but-valid +// page is much better than a blank one, so this is a real release rather than a +// placeholder. Keep it in step with the newest release; the drift check in CI +// fails the build when it falls behind release-info.json. +export const FALLBACK_RELEASE: ReleaseInfo = { + tagName: "v0.116-0", + aptVersion: "0.116-0", + rpmVersion: "0.116.0-1.el9", + metaVersion: "0.116.0", + releaseUrl: "https://github.com/documentdb/documentdb/releases/tag/v0.116-0", + assetNames: [], +}; + +type RawReleaseInfo = { + tag_name?: unknown; + html_url?: unknown; + assets?: unknown; +}; + +function assetNamesOf(raw: RawReleaseInfo): string[] { + if (!Array.isArray(raw.assets)) { + return []; + } + return raw.assets + .map((asset) => + asset && typeof asset === "object" && typeof (asset as { name?: unknown }).name === "string" + ? (asset as { name: string }).name + : null, + ) + .filter((name): name is string => name !== null); +} + +function firstMatch(names: readonly string[], pattern: RegExp): string | null { + for (const name of names) { + const match = pattern.exec(name); + if (match?.[1]) { + return match[1]; + } + } + return null; +} + +/** + * Derives the display versions from a release-info.json payload. + * + * Each field falls back independently: a release that stops shipping one + * package shape must not blank out the versions that are still present. + */ +export function parseReleaseInfo(payload: unknown): ReleaseInfo { + if (!payload || typeof payload !== "object") { + return FALLBACK_RELEASE; + } + const raw = payload as RawReleaseInfo; + const names = assetNamesOf(raw); + + const tagName = typeof raw.tag_name === "string" ? raw.tag_name : FALLBACK_RELEASE.tagName; + const releaseUrl = + typeof raw.html_url === "string" + ? raw.html_url + : `https://github.com/documentdb/documentdb/releases/tag/${tagName}`; + + // The extension keeps the control-file form (0.116-0) on DEB, while RPM + // splits it into Version/Release and renders 0.116.0-1.el9. Everything else + // uses the flat dotted form. Read all three off real filenames so the page + // cannot claim a shape the release does not contain. + const aptVersion = + firstMatch(names, /^ubuntu[\d.]+-postgresql-\d+-documentdb_([^_]+)_/) ?? + firstMatch(names, /^deb\d+-postgresql-\d+-documentdb_([^_]+)_/) ?? + FALLBACK_RELEASE.aptVersion; + + const rpmVersion = + firstMatch(names, /^rhel\d+-postgresql\d+-documentdb-(.+)\.(?:x86_64|aarch64)\.rpm$/) ?? + FALLBACK_RELEASE.rpmVersion; + + const metaVersion = + firstMatch(names, /^ubuntu[\d.]+-documentdb_([^_]+)_all\.deb$/) ?? + firstMatch(names, /^documentdb-(\d+\.\d+\.\d+)-\d+\.noarch\.rpm$/) ?? + FALLBACK_RELEASE.metaVersion; + + return { tagName, aptVersion, rpmVersion, metaVersion, releaseUrl, assetNames: names }; +} + +/** + * Reads the mirrored release description published alongside the packages. + * + * Returns the fallback synchronously so the first paint is always correct-ish, + * then swaps in the live values. The site is a static export, so this has to + * happen in the browser; NEXT_PUBLIC_BASE_PATH is the one base-path value Next + * keeps in the client bundle. + */ +export function useReleaseInfo(): ReleaseInfo { + const [release, setRelease] = useState(FALLBACK_RELEASE); + + useEffect(() => { + let cancelled = false; + const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? ""; + + fetch(`${basePath}/packages/release-info.json`) + .then((response) => (response.ok ? response.json() : Promise.reject(response.status))) + .then((payload) => { + if (!cancelled) { + setRelease(parseReleaseInfo(payload)); + } + }) + .catch(() => { + // Keep the fallback: an unreachable or malformed feed must not empty + // the install commands the page exists to show. + }); + + return () => { + cancelled = true; + }; + }, []); + + return release; +} diff --git a/app/packages/page.tsx b/app/packages/page.tsx index cdfb0c7..ee63af6 100644 --- a/app/packages/page.tsx +++ b/app/packages/page.tsx @@ -6,8 +6,11 @@ import CommandSnippet from "../components/CommandSnippet"; import { aptTargetPgVersions, aptTargetLabels, + aptServesFullStack, buildAptInstallCommand, buildRpmInstallCommand, + buildSetupCommand, + rpmServesFullStack, type AptArch, type AptDistro, type AptPgVersion, @@ -16,6 +19,7 @@ import { type RpmPgVersion, rpmTargetLabels, } from "../lib/packageInstall"; +import { useReleaseInfo } from "../lib/releaseInfo"; type InstallMethod = "docker" | "packages"; type PackageFamily = "apt" | "rpm"; @@ -50,28 +54,48 @@ const nextGuides = [ ] as const; const allReleasesUrl = "https://github.com/documentdb/documentdb/releases"; -// Latest GitHub release, used for the direct .deb/.rpm download examples. -const latestReleaseAptVersion = "0.114-0"; -const latestReleaseRpmVersion = "0.114.0-1.el9"; -// Version currently served by the APT/RPM package repositories, which can lag -// GitHub Releases. Used for the version-pinning examples. -const repoAptVersionExample = "0.113-0"; -const repoRpmVersionExample = "0.113.0-1.el9"; -const currentReleaseExamples = [ - `ubuntu22.04-postgresql-18-documentdb_${latestReleaseAptVersion}_amd64.deb`, - `deb13-postgresql-18-documentdb_${latestReleaseAptVersion}_amd64.deb`, - `rhel9-postgresql18-documentdb-${latestReleaseRpmVersion}.x86_64.rpm`, + +// The v0.116-0 packaging redesign replaced the single extension package with +// this set. Listed here so the page explains what an install actually brings +// in, instead of naming one package and silently pulling four more. +const packageRoles = [ + { + name: "documentdb / documentdb-N", + role: "Meta and per-major stand-alone package. Pins PostgreSQL and owns the systemd lifecycle.", + }, + { + name: "postgresql-N-documentdb", + role: "The PostgreSQL extension itself (files only).", + }, + { + name: "documentdb-gateway", + role: "Wire-protocol runtime that serves the MongoDB-compatible endpoint.", + }, + { + name: "documentdb-postgresql-tools", + role: "Administrator helpers: documentdb-tune, documentdb-createcluster, documentdb-register-gateway, documentdb-gateway-admin.", + }, + { + name: "documentdb-common", + role: "Shared payload: documentdb-setup, the systemd units, helper scripts and sample data.", + }, ] as const; export default function PackagesPage() { + const release = useReleaseInfo(); const [method, setMethod] = useState("docker"); const [packageFamily, setPackageFamily] = useState("apt"); - const [aptTarget, setAptTarget] = useState("ubuntu22"); + // Default to the paved road (Ubuntu 24.04 + PostgreSQL 18). It is the target + // the release is built and end-to-end tested against, and the only one whose + // repository component serves the full package set - defaulting to an + // extension-only target showed first-time visitors the older, smaller + // experience. + const [aptTarget, setAptTarget] = useState("ubuntu24"); const [rpmTarget, setRpmTarget] = useState("rhel9"); const [aptArch, setAptArch] = useState("amd64"); const [rpmArch, setRpmArch] = useState("x86_64"); - const [aptPgVersion, setAptPgVersion] = useState("16"); - const [rpmPgVersion, setRpmPgVersion] = useState("16"); + const [aptPgVersion, setAptPgVersion] = useState("18"); + const [rpmPgVersion, setRpmPgVersion] = useState("18"); const availableAptPgVersions = aptTargetPgVersions[aptTarget]; useEffect(() => { @@ -80,10 +104,29 @@ export default function PackagesPage() { } }, [aptPgVersion, availableAptPgVersions]); + const latestReleaseAptVersion = release.aptVersion; + const latestReleaseRpmVersion = release.rpmVersion; + // The repository serves the mirrored release, so the pinning examples use the + // same versions rather than a separately maintained pair that fell behind. + const repoAptVersionExample = release.aptVersion; + const repoRpmVersionExample = release.rpmVersion; + const currentReleaseExamples = [ + `ubuntu24.04-documentdb_${release.metaVersion}_all.deb`, + `ubuntu24.04-postgresql-18-documentdb_${latestReleaseAptVersion}_amd64.deb`, + `rhel9-postgresql18-documentdb-${latestReleaseRpmVersion}.x86_64.rpm`, + ] as const; + const aptCommand = buildAptInstallCommand(aptTarget, aptArch, aptPgVersion); const rpmCommand = buildRpmInstallCommand(rpmTarget, rpmArch, rpmPgVersion); - const selectedPackageNames = + // Tier-1 targets resolve the full v0.116-0 stack, so the selected package is + // the per-major stand-alone rather than the bare extension. + const isFullStack = packageFamily === "apt" + ? aptServesFullStack(aptTarget, aptPgVersion) + : rpmServesFullStack(rpmTarget, rpmPgVersion); + const selectedPackageNames = isFullStack + ? `documentdb-${packageFamily === "apt" ? aptPgVersion : rpmPgVersion}` + : packageFamily === "apt" ? `postgresql-${aptPgVersion}-documentdb` : `postgresql${rpmPgVersion}-documentdb`; const selectedTargetText = @@ -98,9 +141,11 @@ export default function PackagesPage() { Download DocumentDB

- Choose Docker for the fastest local setup, or Linux packages for PostgreSQL - extension installs. The generated package commands configure the PostgreSQL - dependency repositories and install the DocumentDB extension package. + Choose Docker for the fastest local setup, or Linux packages for a persistent + install. On Ubuntu 24.04 and RHEL-compatible 9 the packages install the full + DocumentDB stack — the PostgreSQL extension, the wire-protocol gateway, the + administrator tools and systemd units. Other distributions currently receive the + extension package alone.

@@ -289,9 +334,36 @@ export default function PackagesPage() { rum for PostgreSQL 16/17.

- It installs the PostgreSQL extension package. The published package repository - does not currently include a gateway package, setup helper, or systemd service. + {isFullStack + ? "It installs the full DocumentDB stack for this target: the extension, the gateway runtime, the administrator tools and the systemd units." + : "It installs the PostgreSQL extension package. This target is not yet covered by the v0.116-0 package layout, so the repository serves the extension alone — no gateway package, setup helper, or systemd service."}

+ {isFullStack ? ( + <> +

+ Then run the setup wizard. It creates the PostgreSQL instance, installs the + extensions, bootstraps the admin user and starts the gateway — the install + above on its own does not leave a reachable endpoint. It prompts for the + admin password; pass{" "} + --admin-password-stdin --yes for an + unattended install. +

+ +

+ The gateway then listens on port{" "} + 10260. It binds all interfaces by + default, so firewall the port and supply a real certificate before exposing + it to a network. See the{" "} + + package installation guide + {" "} + for verification, day-2 and upgrade steps. +

+ + ) : null} {packageFamily === "apt" ? (

Running in a clean Debian/Ubuntu container as root? @@ -303,14 +375,36 @@ export default function PackagesPage() { ) : null}

- Need the MongoDB-compatible gateway? -

-

- Use the Docker image for the fastest gateway-backed local setup. If you want a - package-backed host install that still works with mongosh, - the Linux package guide includes the exact non-root gateway follow-up commands - and host build prerequisites. + {isFullStack + ? "What gets installed" + : "Need the MongoDB-compatible gateway?"}

+ {isFullStack ? ( + <> +

+ DocumentDB ships as five packages. Installing{" "} + {selectedPackageNames} pulls in + everything below. +

+
+ {packageRoles.map((entry) => ( +
+
+ {entry.name} +
+
{entry.role}
+
+ ))} +
+ + ) : ( +

+ Use the Docker image for the fastest gateway-backed local setup. If you want a + package-backed host install that still works with mongosh, + the Linux package guide includes the exact non-root gateway follow-up commands + and host build prerequisites. +

+ )}
{packageFamily === "apt" ? (

@@ -349,17 +443,35 @@ export default function PackagesPage() { APT - Ubuntu 22.04/24.04, Debian 11/12/13 + Ubuntu 24.04 (full stack) + amd64, arm64 + 17, 18 + + documentdb-<pg> + + + + APT + Ubuntu 22.04, Debian 11/12/13 (extension only) amd64, arm64 16, 17, 18 (Debian 11: 16, 17) postgresql-<pg>-documentdb + + RPM + RHEL-compatible 9 (full stack) + x86_64, aarch64 + 17, 18 + + documentdb-<pg> + + RPM - RHEL-compatible 8/9 (tested on Rocky Linux) + RHEL-compatible 8 (extension only, tested on Rocky Linux) x86_64, aarch64 16, 17, 18 @@ -386,7 +498,10 @@ export default function PackagesPage() {

- Use the commands below to discover available versions before pinning. Replace{" "} + Use the commands below to discover available versions before pinning. The + examples name the extension package; substitute{" "} + {selectedPackageNames} to pin the package + your selected target actually installs. Replace{" "} <VERSION> with the version string shown by the list command (e.g.{" "} {repoAptVersionExample} for APT,{" "} @@ -398,12 +513,12 @@ export default function PackagesPage() {

APT — list then pin

- apt-cache madison postgresql-16-documentdb + apt-cache madison postgresql-18-documentdb
- sudo apt install postgresql-16-documentdb=<VERSION> + sudo apt install postgresql-18-documentdb=<VERSION>
@@ -411,12 +526,12 @@ export default function PackagesPage() {

RPM — list then pin

- dnf --showduplicates list postgresql16-documentdb + dnf --showduplicates list postgresql18-documentdb
- sudo dnf install postgresql16-documentdb-<VERSION> + sudo dnf install postgresql18-documentdb-<VERSION>
@@ -473,13 +588,13 @@ export default function PackagesPage() {
sudo apt update && apt search documentdb && apt-cache policy - postgresql-16-documentdb + postgresql-18-documentdb
sudo dnf clean all && dnf search documentdb && rpm -qi - postgresql16-documentdb + postgresql18-documentdb
@@ -492,10 +607,12 @@ export default function PackagesPage() { 3. Connect and try it

- Docker starts a gateway-backed local endpoint on port 10260. Linux packages install - the PostgreSQL extension; the Linux package guide adds the extra source-gateway - steps needed when you want a host install that still exposes a MongoDB-compatible - endpoint. + Docker starts a gateway-backed local endpoint on port 10260. On Ubuntu 24.04 and + RHEL-compatible 9 the packages give you the same thing: install, then run{" "} + sudo documentdb-setup --admin-user admin, + which creates the database and starts the gateway. On the extension-only + distributions the Linux package guide covers the additional source-gateway steps + needed to expose a MongoDB-compatible endpoint.

diff --git a/app/services/articleService.ts b/app/services/articleService.ts index 29f6257..9d7db2a 100644 --- a/app/services/articleService.ts +++ b/app/services/articleService.ts @@ -108,65 +108,175 @@ If something does not work as expected: const linuxPackagesGuideContent = `# Linux Packages Quick Start -Install the DocumentDB PostgreSQL extension package on Debian, Ubuntu, or RHEL-compatible hosts. +Install DocumentDB on Debian, Ubuntu, or RHEL-compatible hosts from the published package repository. + +## What you get + +Since v0.116-0 DocumentDB ships as a set of packages rather than a lone extension: + +| Package | Role | +| --- | --- | +| \`documentdb\` (meta) + \`documentdb-N\` | Full stand-alone install. Pins PostgreSQL major N and owns the systemd lifecycle. | +| \`postgresql-N-documentdb\` | The PostgreSQL extension itself (files only). | +| \`documentdb-gateway\` | Wire-protocol runtime serving the MongoDB-compatible endpoint. | +| \`documentdb-postgresql-tools\` | Admin helpers: \`documentdb-tune\`, \`documentdb-createcluster\`, \`documentdb-register-gateway\`, \`documentdb-gateway-admin\`. | +| \`documentdb-common\` | Shared payload: \`documentdb-setup\`, the systemd units, helper scripts, sample data. | + +The full set is published for **Ubuntu 24.04** and **RHEL-compatible 9** on PostgreSQL 17 and 18. Ubuntu 22.04, Debian 11/12/13 and RHEL-compatible 8 still resolve the **extension package only** — for those, see [Extension-only hosts](#extension-only-hosts) below. ## Choose the right package command Use the [Package Finder](/packages) to generate the exact install command for your distro, architecture, and PostgreSQL version. -> The generated command installs the PostgreSQL extension package and its PostgreSQL-side dependencies. The published package repository and GitHub Releases do not currently include a gateway package, setup helper, or systemd service. +> The package commands assume a regular Linux host where you use \`sudo\`. In a clean container running as \`root\`, omit \`sudo\`. > -> The repository-backed install commands currently cover Ubuntu 22.04/24.04, Debian 11/12/13, and RHEL-compatible 8/9 systems. Debian 11 currently resolves PostgreSQL 16 and 17 in the repository-backed flow. -> -> The package commands assume a regular Linux host where you use \`sudo\`. If you are testing in a clean container that already runs as \`root\`, omit \`sudo\` from the package-install commands. -> -> On Debian and Ubuntu in a clean container, also run \`export DEBIAN_FRONTEND=noninteractive\` in the shell before the APT commands. Without it, \`tzdata\` (and a few other packages) prompt for input during \`apt install\` and the install hangs with no visible error. +> On Debian and Ubuntu in a clean container, also run \`export DEBIAN_FRONTEND=noninteractive\` first. Without it, \`tzdata\` prompts for input during \`apt install\` and the install hangs with no visible error. ## Install the packages -### APT example +### APT example (Ubuntu 24.04, PostgreSQL 18) \`\`\`bash -${buildAptInstallCommand('ubuntu24', 'amd64', '16')} +${buildAptInstallCommand('ubuntu24', 'amd64', '18')} \`\`\` -### RPM example +### RPM example (RHEL-compatible 9, PostgreSQL 18) \`\`\`bash -${buildRpmInstallCommand('rhel9', 'x86_64', '16')} +${buildRpmInstallCommand('rhel9', 'x86_64', '18')} \`\`\` -## What the package installs +## Set up and connect -The packages install the DocumentDB PostgreSQL extension files for the selected PostgreSQL major version. They do not by themselves start a MongoDB-compatible gateway endpoint on port \`10260\`. +Installing the packages puts files on disk; it does not create a database or start the endpoint. The setup wizard does that: -Use the Docker quick start when you need the fastest local gateway-backed DocumentDB endpoint: +\`\`\`bash +sudo documentdb-setup --admin-user admin +\`\`\` + +It creates the PostgreSQL instance, installs the extensions, bootstraps the admin user, starts the gateway, and enables \`documentdb-local@.target\` so the stack survives reboot. It **prompts for the admin password**; for servers and CI pass \`--admin-password-file \` or \`--admin-password-stdin\` together with \`--yes\`. + +\`mongosh\` is not shipped by these packages — install it from the [official instructions](https://www.mongodb.com/docs/mongodb-shell/install/), then: \`\`\`bash -docker run -dt --name documentdb \\ - -p 10260:10260 \\ - ghcr.io/documentdb/documentdb/documentdb-local:latest \\ - --username \\ - --password +mongosh 'mongodb://admin:@127.0.0.1:10260/mydb?tls=true&tlsAllowInvalidCertificates=true' \\ + --eval 'db.runCommand({ping: 1})' \`\`\` -If you are operating a host PostgreSQL installation, configure PostgreSQL and run the gateway using the source repository's build/run scripts. +If the password contains \`@\`, \`:\`, \`/\` or other reserved characters it must be percent-encoded in the URI (\`@\` becomes \`%40\`), otherwise the URI misparses. To avoid encoding entirely, pass the credentials as flags instead: -## Verify the package install +\`\`\`bash +mongosh localhost:10260 -u admin -p --authenticationMechanism SCRAM-SHA-256 \\ + --tls --tlsAllowInvalidCertificates --eval 'db.runCommand({ping: 1})' +\`\`\` + +A database and collection are created on first write: + +\`\`\`javascript +db.orders.insertOne({ item: "widget", qty: 5 }) +db.orders.find() +\`\`\` + +## Before exposing it to a network + +The gateway binds **all interfaces** (\`0.0.0.0:10260\` and \`[::]:10260\`) by default, even though the connect string above says \`127.0.0.1\`. The PostgreSQL instance behind it stays on loopback. -Use package-manager metadata to confirm the extension package is installed: +Before using this anywhere but a private machine: + +- Restrict the listener with \`DOCUMENTDB_LISTEN_ADDR=127.0.0.1:10260\` in \`/etc/documentdb/local//gateway.env\` and restart the service, or firewall port \`10260\`. Note that re-running \`documentdb-setup\` rewrites that file, so a firewall rule is the more durable control. +- Replace the auto-generated self-signed certificate. \`tlsAllowInvalidCertificates=true\` disables certificate validation — point \`DOCUMENTDB_TLS_CERT_FILE\` / \`DOCUMENTDB_TLS_KEY_FILE\` at a real certificate and drop that option. +- Use a strong admin password and create per-application users rather than sharing \`admin\`. + +## Verify and operate + +\`\`\`bash +sudo documentdb-setup --status # gateway listener, service states, resolved paths +documentdb-gateway --version # DocumentDB version +dpkg -l | grep documentdb # or: rpm -qa | grep documentdb +\`\`\` + +> Do not use \`db.version()\` or \`buildInfo\` in \`mongosh\` to check the DocumentDB version — those report the emulated MongoDB wire version, not DocumentDB's. + +| Thing | Where | +| --- | --- | +| Gateway port | \`10260\` | +| PostgreSQL port | \`9700 + \` (9718 for PG 18), loopback only | +| Gateway log | \`/var/lib/documentdb-gateway/gateway.log\` | +| PostgreSQL log | \`/var/lib/documentdb-local//data/pglog.log\` | +| Setup state / gateway env | \`/etc/documentdb/local//setup.conf\`, \`.../gateway.env\` | + +Day 2 (units are templated per PostgreSQL major): + +\`\`\`bash +sudo systemctl status documentdb-local@18.target +sudo systemctl restart documentdb-local@18.target +sudo systemctl stop documentdb-local@18.target +\`\`\` + +On hosts without systemd the wizard starts the gateway directly; re-run \`documentdb-setup\` to restart it. + +Remove or reset: + +\`\`\`bash +# Stop the stack first — package removal deletes files but does not stop a +# running gateway. On systemd hosts: +sudo systemctl stop documentdb-local@18.target +# Without systemd the wizard started the gateway directly; kill that process. + +sudo documentdb-setup --restore # detach the managed integration +sudo documentdb-local-reset --pg-version 18 --confirm-destroy # DESTROYS the data directory + +# Name the package you installed AND the extension: autoremove does not reap +# postgresql-18-documentdb, and \`remove\` would leave config behind. +sudo apt purge --autoremove documentdb-18 postgresql-18-documentdb +sudo dnf remove documentdb-18 postgresql18-documentdb && sudo dnf autoremove +\`\`\` + +(If you installed the \`documentdb\` meta package rather than \`documentdb-18\`, name that instead.) + +## Upgrading + +A package upgrade only replaces files. Afterwards, update the extensions in every database that has DocumentDB installed: + +\`\`\`sql +ALTER EXTENSION documentdb_core UPDATE; +ALTER EXTENSION documentdb UPDATE; +ALTER EXTENSION documentdb_extended_rum UPDATE; -- only if installed +\`\`\` + +PostgreSQL applies intermediate upgrade scripts automatically. In-place upgrades are not yet a fully tested path, so take a backup first. + +## Extension-only hosts + +Ubuntu 22.04, Debian 11/12/13 and RHEL-compatible 8 currently serve the extension package alone — no gateway, setup helper, or systemd units. On those hosts the install command ends in \`postgresql--documentdb\` (APT) or \`postgresql-documentdb\` (RPM), and there is no \`documentdb-setup\`. + +Confirm the extension landed with package metadata: \`\`\`bash # APT -apt-cache policy postgresql-16-documentdb -dpkg -L postgresql-16-documentdb | grep -E 'documentdb.*\\.(control|sql|so)$' | head +apt-cache policy postgresql-18-documentdb +dpkg -L postgresql-18-documentdb | grep -E 'documentdb.*\\.(control|sql|so)$' | head # RPM -dnf info postgresql16-documentdb -rpm -ql postgresql16-documentdb | grep -E 'documentdb.*\\.(control|sql|so)$' | head +dnf info postgresql18-documentdb +rpm -ql postgresql18-documentdb | grep -E 'documentdb.*\\.(control|sql|so)$' | head +\`\`\` + +For the fastest gateway-backed endpoint on those hosts, use the Docker quick start: + +\`\`\`bash +docker run -dt --name documentdb \\ + -p 10260:10260 \\ + ghcr.io/documentdb/documentdb/documentdb-local:latest \\ + --username \\ + --password \`\`\` -## Turn a package install into a local \`mongosh\` endpoint +Otherwise, run the gateway from the source repository against your host PostgreSQL, as described below. + +## Turn an extension-only install into a local \`mongosh\` endpoint + +On Ubuntu 24.04 and RHEL 9 use \`documentdb-setup\` above instead — this section is only for distributions where the gateway is not packaged yet. If you want to keep PostgreSQL on the host and still connect with \`mongosh\`, install the extension package first and then run the gateway from the source repository against that PostgreSQL instance. @@ -258,7 +368,8 @@ Use this flow when you want a package-backed host install plus a local MongoDB-c If something does not work on the first try: -- Confirm the extension package is installed: \`postgresql--documentdb\` on APT or \`postgresql-documentdb\` on RPM +- Confirm the packages are installed: \`documentdb-\` on a full-stack target, or \`postgresql--documentdb\` (APT) / \`postgresql-documentdb\` (RPM) on an extension-only target +- On a full-stack target, run \`sudo documentdb-setup --status\` first: it reports the gateway listener, the service states and the resolved paths - Re-run the Package Finder command for the exact distro, architecture, and PostgreSQL version you selected - Confirm the PostgreSQL upstream repository was added before the DocumentDB package install - If you are running in a clean container as \`root\`, omit \`sudo\` from the package-install commands and switch to an unprivileged user before the gateway steps @@ -269,7 +380,7 @@ If something does not work on the first try: - If the gateway build fails while reading \`Cargo.lock\`, switch to a current Rust toolchain from \`rustup\` instead of the distro-packaged \`cargo\` - If \`mongosh\` cannot connect, confirm the gateway script is still running and listening on port \`10260\` - For Debian 11, use PostgreSQL 16 or 17; PostgreSQL 18 is blocked by the upstream Bullseye PostGIS dependency -- If you need a gateway endpoint, use DocumentDB Local with Docker or build and run the gateway from source +- If you need a gateway endpoint on an extension-only distribution, use DocumentDB Local with Docker or build and run the gateway from source ## Next steps diff --git a/tests/packageInstall.test.ts b/tests/packageInstall.test.ts index d4b5996..c1c0ba8 100644 --- a/tests/packageInstall.test.ts +++ b/tests/packageInstall.test.ts @@ -1,9 +1,11 @@ import { describe, expect, it } from 'vitest'; import { + aptServesFullStack, aptTargetLabels, aptTargetPgVersions, buildAptInstallCommand, buildRpmInstallCommand, + rpmServesFullStack, rpmTargetLabels, } from '../app/lib/packageInstall'; import type { @@ -89,9 +91,16 @@ describe('buildAptInstallCommand', () => { expect(command).toContain(`documentdb.io/deb stable ${distro}`); }); - it.each(aptMatrix)('installs postgresql-$pg-documentdb for $distro/$arch', ({ distro, arch, pg }) => { + it.each(aptMatrix)('installs the right package for $distro/$arch/pg$pg', ({ distro, arch, pg }) => { const command = buildAptInstallCommand(distro, arch, pg); - expect(command).toContain(`sudo apt install -y postgresql-${pg}-documentdb`); + // v0.116-0 ships the full package set for Tier-1 targets only. There the + // per-major stand-alone pulls the whole stack; everywhere else the + // repository still serves the extension alone, and offering `documentdb-N` + // would be an install command that cannot resolve. + const expected = aptServesFullStack(distro, pg) + ? `documentdb-${pg}` + : `postgresql-${pg}-documentdb`; + expect(command).toContain(`sudo apt install -y ${expected}`); }); it('does not offer PostgreSQL 18 on Debian 11', () => { @@ -126,9 +135,29 @@ describe('buildRpmInstallCommand', () => { expect(command).toContain(`baseurl=https://documentdb.io/rpm/${distro}`); }); - it.each(rpmMatrix)('installs postgresql$pg-documentdb for $distro/$arch', ({ distro, arch, pg }) => { + it.each(rpmMatrix)('installs the right package for $distro/$arch/pg$pg', ({ distro, arch, pg }) => { const command = buildRpmInstallCommand(distro, arch, pg); - expect(command).toContain(`sudo dnf install -y postgresql${pg}-documentdb`); + const expected = rpmServesFullStack(distro, pg) + ? `documentdb-${pg}` + : `postgresql${pg}-documentdb`; + expect(command).toContain(`sudo dnf install -y ${expected}`); + }); + + it('serves the full stack only where v0.116-0 published it', () => { + // PostgreSQL 16 resolves the older extension-only build even on a Tier-1 + // target, so it must keep the extension command. + expect(rpmServesFullStack('rhel9', '18')).toBe(true); + expect(rpmServesFullStack('rhel9', '16')).toBe(false); + expect(rpmServesFullStack('rhel8', '18')).toBe(false); + expect(aptServesFullStack('ubuntu24', '18')).toBe(true); + expect(aptServesFullStack('ubuntu24', '16')).toBe(false); + expect(aptServesFullStack('ubuntu22', '18')).toBe(false); + expect(buildAptInstallCommand('ubuntu22', 'amd64', '18')).toContain( + 'sudo apt install -y postgresql-18-documentdb', + ); + expect(buildAptInstallCommand('ubuntu24', 'amd64', '18')).toContain( + 'sudo apt install -y documentdb-18', + ); }); it('enables gpgcheck against the DocumentDB signing key', () => {