diff --git a/PACKAGE-INSTALL.md b/PACKAGE-INSTALL.md index 5d99a40..381076c 100644 --- a/PACKAGE-INSTALL.md +++ b/PACKAGE-INSTALL.md @@ -349,9 +349,10 @@ The current release publishes PostgreSQL 17 and 18. Install `documentdb-17` or ## Upgrading an existing install > **Warning:** In-place package upgrades from earlier releases are not supported yet. Use a -> clean host, or remove the earlier packages and perform the -> current fresh installation. Upgrading only `postgresql-N-documentdb` does not install the -> gateway, tools, common payload, or `documentdb-N`. +> clean host or a new, empty PostgreSQL instance for the current fresh installation. +> Removing packages alone does not create a fresh database: package removal preserves PostgreSQL data and +> in-database content, and a later setup run reuses an initialized data directory. Upgrading only +> `postgresql-N-documentdb` does not install the gateway, tools, common payload, or `documentdb-N`. For a later point release that uses the same multi-package layout, move the entire stack together. On a package-managed private PostgreSQL 18 instance: @@ -453,9 +454,13 @@ ubuntu24.04-postgresql-18-documentdb_0.117-0_amd64.deb rhel9-postgresql18-documentdb-0.117.0-1.el9.x86_64.rpm ``` -Because the packages depend on each other, installing a downloaded meta package on its own -fails with `Depends: documentdb-18 ... but it is not installable`. Pass the whole set to a -single command, or just use the repository-backed install above. +For a full stack, pass the five packages for the selected PostgreSQL major to one command: +`documentdb-N`, the matching `postgresql-N-documentdb` / `postgresqlN-documentdb` extension, +`documentdb-common`, `documentdb-gateway`, and `documentdb-postgresql-tools`. For PostgreSQL 18 +only, the optional `documentdb` meta package may be included; it selects `documentdb-18`. +Local files resolve dependencies only against enabled repositories, so a package whose +dependencies are not included still fails. Alternatively, use the repository-backed install +above. - GitHub Releases: https://github.com/documentdb/documentdb/releases - Release metadata: https://documentdb.io/packages/release-info.json diff --git a/app/packages/page.tsx b/app/packages/page.tsx index 559b4a8..a56a7cc 100644 --- a/app/packages/page.tsx +++ b/app/packages/page.tsx @@ -25,10 +25,10 @@ type InstallMethod = "docker" | "packages"; type PackageFamily = "apt" | "rpm"; const dockerCommand = `docker run -dt --name documentdb \\ - -p 10260:10260 \\ + -p 127.0.0.1:10260:10260 \\ ghcr.io/documentdb/documentdb/documentdb-local:latest \\ - --username \\ - --password `; + --username '' \\ + --password ''`; const nextGuides = [ { diff --git a/app/services/articleService.ts b/app/services/articleService.ts index 507aab0..2be8647 100644 --- a/app/services/articleService.ts +++ b/app/services/articleService.ts @@ -50,8 +50,8 @@ Then start the container: docker run -dt --name documentdb \\ -p 127.0.0.1:10260:10260 \\ ghcr.io/documentdb/documentdb/documentdb-local:latest \\ - --username \\ - --password \\ + --username '' \\ + --password '' \\ --init-data true \`\`\` @@ -90,8 +90,8 @@ Use \`mongosh\` to confirm authentication, TLS, and the gateway endpoint are wor \`\`\`bash mongosh localhost:10260 \\ - -u \\ - -p \\ + -u '' \\ + -p '' \\ --authenticationMechanism SCRAM-SHA-256 \\ --tls \\ --tlsAllowInvalidCertificates @@ -220,7 +220,7 @@ Then install \`mongosh\`, which you need to talk to the endpoint: \`\`\`bash # Ubuntu 24.04 -curl -fsSL https://pgp.mongodb.com/server-8.0.asc | sudo gpg --dearmor -o /usr/share/keyrings/mongodb.gpg +curl -fsSL https://pgp.mongodb.com/server-8.0.asc | sudo gpg --dearmor --yes -o /usr/share/keyrings/mongodb.gpg echo "deb [signed-by=/usr/share/keyrings/mongodb.gpg] https://repo.mongodb.org/apt/ubuntu noble/mongodb-org/8.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb.list sudo apt update && sudo apt install -y mongodb-mongosh @@ -382,9 +382,11 @@ For an adopted instance, use the operator's existing PostgreSQL connection inste ## Upgrading > [!WARNING] -> In-place package upgrades from earlier releases are not supported yet. Use a clean host, -> or remove the earlier packages and perform the current -> [fresh installation](/docs/getting-started/packages). Upgrading only +> In-place package upgrades from earlier releases are not supported yet. Use a clean host +> or a new, empty PostgreSQL instance and follow the current +> [fresh installation](/docs/getting-started/packages). Removing packages alone does not +> create a fresh database: package removal preserves PostgreSQL data and in-database content, +> and a later setup run reuses an initialized data directory. Upgrading only > \`postgresql-N-documentdb\` does not install the gateway, tools, common payload, or > \`documentdb-N\`. @@ -609,7 +611,7 @@ Then continue with [Set up and connect](/docs/getting-started/packages#set-up-an If the target already has PostgreSQL, the PGDG extension dependencies (\`postgresql-N-cron\`, \`-pgvector\`, \`-postgis-3\`) and \`jq\`, you do not need a bundle: - **Extension only, one file** — \`sudo apt install ./ubuntu24.04-postgresql-18-documentdb_0.117-0_amd64.deb\`. No gateway and no \`documentdb-setup\`. -- **Full stack from the release assets** — pass all six files for your platform to a *single* \`apt install\` / \`dnf install\`. Local files resolve dependencies only against enabled repositories, so the meta package on its own fails with \`Depends: documentdb-18 ... but it is not installable\`. +- **Full stack from the release assets** — pass the five packages for the selected PostgreSQL major to a *single* \`apt install\` / \`dnf install\`: \`documentdb-N\`, the matching \`postgresql-N-documentdb\` / \`postgresqlN-documentdb\` extension, \`documentdb-common\`, \`documentdb-gateway\`, and \`documentdb-postgresql-tools\`. For PostgreSQL 18 only, the optional \`documentdb\` meta package may be included; it selects \`documentdb-18\`. Local files resolve dependencies only against enabled repositories, so a package whose dependencies are not included still fails. `; const vscodeQuickStartGuideContent = `# Visual Studio Code Quick Start @@ -639,10 +641,10 @@ For the fastest local setup, start DocumentDB Local with Docker: \`\`\`bash docker run -dt --name documentdb \\ - -p 10260:10260 \\ + -p 127.0.0.1:10260:10260 \\ ghcr.io/documentdb/documentdb/documentdb-local:latest \\ - --username \\ - --password + --username '' \\ + --password '' \`\`\` If you prefer a host installation instead of Docker, use the [Linux Packages Quick Start](/docs/getting-started/packages) on a distribution in the current release matrix. @@ -729,14 +731,19 @@ Connect to DocumentDB from Node.js using the official MongoDB driver. ## Start DocumentDB Local \`\`\`bash +export DOCUMENTDB_USERNAME='' +export DOCUMENTDB_PASSWORD='' + docker run -dt --name documentdb \\ - -p 10260:10260 \\ + -p 127.0.0.1:10260:10260 \\ ghcr.io/documentdb/documentdb/documentdb-local:latest \\ - --username \\ - --password + --username "\${DOCUMENTDB_USERNAME:?Set DOCUMENTDB_USERNAME}" \\ + --password "\${DOCUMENTDB_PASSWORD:?Set DOCUMENTDB_PASSWORD}" \`\`\` -> Replace \`\` and \`\` with your own credentials. +> Replace the placeholder values before running the command. The Node.js process below +> reads the same two environment variables, so the credentials are passed as raw values +> rather than embedded in a URI. > > DocumentDB Local uses a self-signed certificate by default, so the quickest local > Node.js connection uses \`tlsAllowInvalidCertificates=true\`. @@ -757,12 +764,26 @@ Create an \`index.js\` file: \`\`\`javascript const { MongoClient } = require("mongodb"); -const uri = - "mongodb://:@localhost:10260/" + - "?authSource=admin&tls=true&tlsAllowInvalidCertificates=true&directConnection=true"; +const username = process.env.DOCUMENTDB_USERNAME; +const password = process.env.DOCUMENTDB_PASSWORD; + +if (!username || !password) { + throw new Error( + "Set DOCUMENTDB_USERNAME and DOCUMENTDB_PASSWORD before running this script" + ); +} + +const uri = "mongodb://localhost:10260/"; +const options = { + auth: { username, password }, + authSource: "admin", + tls: true, + tlsAllowInvalidCertificates: true, + directConnection: true +}; async function main() { - const client = new MongoClient(uri); + const client = new MongoClient(uri, options); try { await client.connect(); @@ -807,16 +828,21 @@ node index.js ## Connect with a trusted local certificate instead If you want certificate validation instead of \`tlsAllowInvalidCertificates=true\`, -copy the generated certificate from the container and point the driver at it. +copy the generated certificate from the container, then replace the \`options\` object +above with the trusted-certificate version below. \`\`\`bash docker cp documentdb:/home/documentdb/.local/state/documentdb-gateway/tls/cert.pem ~/documentdb-cert.pem \`\`\` \`\`\`javascript -const uri = - "mongodb://:@localhost:10260/" + - "?authSource=admin&tls=true&tlsCAFile=/absolute/path/documentdb-cert.pem&directConnection=true"; +const options = { + auth: { username, password }, + authSource: "admin", + tls: true, + tlsCAFile: "/absolute/path/documentdb-cert.pem", + directConnection: true +}; \`\`\` ## Next steps @@ -843,15 +869,22 @@ Use PyMongo to connect to DocumentDB, verify authentication and TLS, and run you For the fastest local setup, start DocumentDB Local with Docker: \`\`\`bash +export DOCUMENTDB_USERNAME='' +export DOCUMENTDB_PASSWORD='' + docker run -dt --name documentdb \\ - -p 10260:10260 \\ + -p 127.0.0.1:10260:10260 \\ ghcr.io/documentdb/documentdb/documentdb-local:latest \\ - --username \\ - --password + --username "\${DOCUMENTDB_USERNAME:?Set DOCUMENTDB_USERNAME}" \\ + --password "\${DOCUMENTDB_PASSWORD:?Set DOCUMENTDB_PASSWORD}" \`\`\` If you prefer a host installation instead of Docker, use the [Linux Packages Quick Start](/docs/getting-started/packages) on a distribution in the current release matrix. +> Replace the placeholder values before running the command. The Python process below +> reads the same two environment variables, so the credentials are passed as raw values +> rather than embedded in a URI. +> > DocumentDB Local uses a self-signed certificate by default, so the quickest local > PyMongo connection uses \`tlsAllowInvalidCertificates=true\`. @@ -877,14 +910,26 @@ python -m pip install pymongo Create a \`quickstart.py\` file: \`\`\`python +import os + from pymongo import MongoClient -uri = ( - "mongodb://:@localhost:10260/" - "?tls=true&tlsAllowInvalidCertificates=true" -) +username = os.environ.get("DOCUMENTDB_USERNAME") +password = os.environ.get("DOCUMENTDB_PASSWORD") + +if not username or not password: + raise RuntimeError( + "Set DOCUMENTDB_USERNAME and DOCUMENTDB_PASSWORD before running this script" + ) -client = MongoClient(uri) +client = MongoClient( + "mongodb://localhost:10260/", + username=username, + password=password, + authSource="admin", + tls=True, + tlsAllowInvalidCertificates=True, +) try: client.admin.command("ping") @@ -934,7 +979,7 @@ for store in client["StoreData"]["stores"].find( ## Use a trusted local certificate instead -If you want certificate validation instead of \`tlsAllowInvalidCertificates=true\`, copy the generated certificate from the container and pass it to \`MongoClient\`. +If you want certificate validation instead of \`tlsAllowInvalidCertificates=true\`, copy the generated certificate from the container, then replace the \`MongoClient\` call above with the trusted-certificate version below. \`\`\`bash docker cp documentdb:/home/documentdb/.local/state/documentdb-gateway/tls/cert.pem ~/documentdb-cert.pem @@ -942,7 +987,11 @@ docker cp documentdb:/home/documentdb/.local/state/documentdb-gateway/tls/cert.p \`\`\`python client = MongoClient( - "mongodb://:@localhost:10260/?tls=true", + "mongodb://localhost:10260/", + username=username, + password=password, + authSource="admin", + tls=True, tlsCAFile="/absolute/path/documentdb-cert.pem", ) \`\`\` @@ -984,10 +1033,10 @@ For the fastest local setup, start DocumentDB Local with Docker: \`\`\`bash docker run -dt --name documentdb \\ - -p 10260:10260 \\ + -p 127.0.0.1:10260:10260 \\ ghcr.io/documentdb/documentdb/documentdb-local:latest \\ - --username \\ - --password + --username '' \\ + --password '' \`\`\` If you prefer a host installation instead of Docker, use the [Linux Packages Quick Start](/docs/getting-started/packages) on a distribution in the current release matrix. @@ -1000,8 +1049,8 @@ If you prefer a host installation instead of Docker, use the [Linux Packages Qui \`\`\`bash mongosh localhost:10260 \\ - -u \\ - -p \\ + -u '' \\ + -p '' \\ --authenticationMechanism SCRAM-SHA-256 \\ --tls \\ --tlsAllowInvalidCertificates @@ -1062,8 +1111,8 @@ the generated certificate from the container and pass it to \`mongosh\`. docker cp documentdb:/home/documentdb/.local/state/documentdb-gateway/tls/cert.pem ~/documentdb-cert.pem mongosh localhost:10260 \\ - -u \\ - -p \\ + -u '' \\ + -p '' \\ --authenticationMechanism SCRAM-SHA-256 \\ --tls \\ --tlsCAFile ~/documentdb-cert.pem @@ -1118,9 +1167,9 @@ DocumentDB Local starts **empty**. Pass \`--init-data true\` to seed the \`Store with the \`stores\` and \`ratings\` collections: \`\`\`bash -docker run -dt -p 10260:10260 --name documentdb \\ +docker run -dt -p 127.0.0.1:10260:10260 --name documentdb \\ ghcr.io/documentdb/documentdb/documentdb-local:latest \\ - --username --password --init-data true + --username '' --password '' --init-data true \`\`\` Seeding happens once per data volume, on a fresh volume. Existing volumes are not migrated @@ -1140,11 +1189,11 @@ The built-in sample dataset currently includes 41,505 store documents and 2 rati \`\`\`bash docker run -dt --name documentdb \\ - -p 10260:10260 \\ + -p 127.0.0.1:10260:10260 \\ -v /path/to/init/scripts:/init_doc_db.d \\ ghcr.io/documentdb/documentdb/documentdb-local:latest \\ - --username \\ - --password \\ + --username '' \\ + --password '' \\ --init-data-path /init_doc_db.d \`\`\` @@ -1183,8 +1232,8 @@ Before moving on to application code, confirm that DocumentDB is reachable and y docker ps --filter "name=documentdb" mongosh localhost:10260 \\ - -u \\ - -p \\ + -u '' \\ + -p '' \\ --authenticationMechanism SCRAM-SHA-256 \\ --tls \\ --tlsAllowInvalidCertificates diff --git a/tests/packageArticles.test.ts b/tests/packageArticles.test.ts index 5c42b2f..63b5482 100644 --- a/tests/packageArticles.test.ts +++ b/tests/packageArticles.test.ts @@ -5,6 +5,11 @@ import { linuxPackagesOperationsContent, } from '../app/services/articleService'; +function getCodeBlocks(content: string, language: string): string[] { + const pattern = new RegExp('```' + language + '\\n([\\s\\S]*?)\\n```', 'g'); + return Array.from(content.matchAll(pattern), (match) => match[1]); +} + describe('Linux package articles', () => { it('keeps advanced setup details out of the quick start', () => { expect(linuxPackagesGuideContent).toContain( @@ -71,11 +76,62 @@ describe('Linux package articles', () => { expect(offlineGuide?.content).toContain( 'ubuntu24.04-postgresql-18-documentdb_0.117-0_amd64.deb', ); + expect(offlineGuide?.content).toContain( + 'pass the five packages for the selected PostgreSQL major', + ); + expect(offlineGuide?.content).toContain( + 'For PostgreSQL 18 only, the optional `documentdb` meta package may be included', + ); + expect(offlineGuide?.content).toContain('`documentdb-common`'); + expect(offlineGuide?.content).toContain('`documentdb-gateway`'); + expect(offlineGuide?.content).toContain('`documentdb-postgresql-tools`'); + expect(offlineGuide?.content).not.toContain('pass all six files'); expect(linuxPackagesOperationsContent).not.toContain( '## Known issues in 0.116', ); }); + it('keeps package setup rerunnable and reinstall wording data-safe', async () => { + const packageBlocks = getCodeBlocks(linuxPackagesGuideContent, 'bash'); + const mongoRepositoryBlock = packageBlocks.find((block) => + block.includes('https://pgp.mongodb.com/server-8.0.asc'), + ); + + expect(mongoRepositoryBlock).toContain( + 'gpg --dearmor --yes -o /usr/share/keyrings/mongodb.gpg', + ); + expect(linuxPackagesOperationsContent).toContain( + 'Removing packages alone does not', + ); + expect(linuxPackagesOperationsContent).toContain( + 'package removal preserves PostgreSQL data and in-database content', + ); + expect(linuxPackagesOperationsContent).not.toContain( + 'remove the earlier packages and perform the current', + ); + + const { readFile } = await import('node:fs/promises'); + const { fileURLToPath } = await import('node:url'); + const packageInstall = await readFile( + fileURLToPath(new URL('../PACKAGE-INSTALL.md', import.meta.url)), + 'utf8', + ); + + expect(packageInstall).toContain( + 'five packages for the selected PostgreSQL major', + ); + expect(packageInstall).toContain( + 'the optional `documentdb` meta package may be included', + ); + expect(packageInstall).toContain( + 'Removing packages', + ); + expect(packageInstall).toContain( + 'alone does not create a fresh database', + ); + expect(packageInstall).not.toContain('Pass the whole set'); + }); + it('does not list the fixed setup core-version update as a current issue', () => { expect(linuxPackagesOperationsContent).not.toContain( 'does not run `ALTER EXTENSION documentdb_core UPDATE`', @@ -117,6 +173,136 @@ describe('Linux package articles', () => { ); }); + it('keeps executable local Docker recipes on loopback with explicit credentials', async () => { + const quickStarts = [ + 'docker', + 'vscode-quickstart', + 'nodejs-setup', + 'python-setup', + 'mongo-shell-quickstart', + ]; + + for (const slug of quickStarts) { + const article = getArticleByPath('getting-started', [slug]); + if (!article) { + throw new Error(`Missing curated article getting-started/${slug}`); + } + + const dockerBlocks = getCodeBlocks(article.content, 'bash').filter( + (block) => + block.includes('docker run') && + block.includes('ghcr.io/documentdb/documentdb/documentdb-local'), + ); + + expect(dockerBlocks.length, slug).toBeGreaterThan(0); + for (const block of dockerBlocks) { + expect(block, slug).toContain('-p 127.0.0.1:10260:10260'); + expect(block, slug).not.toContain('-p 10260:10260'); + expect(block, slug).not.toContain('--username '); + expect(block, slug).not.toContain('--password '); + } + } + + const { readFile } = await import('node:fs/promises'); + const { fileURLToPath } = await import('node:url'); + const articleSource = await readFile( + fileURLToPath(new URL('../app/services/articleService.ts', import.meta.url)), + 'utf8', + ); + const packagePageSource = await readFile( + fileURLToPath(new URL('../app/packages/page.tsx', import.meta.url)), + 'utf8', + ); + + expect(articleSource).toContain( + 'docker run -dt -p 127.0.0.1:10260:10260 --name documentdb', + ); + expect(articleSource).toContain( + " --username '' --password '' --init-data true", + ); + expect(articleSource).toContain( + ' -p 127.0.0.1:10260:10260 \\\\', + ); + expect(articleSource).toContain( + ' -v /path/to/init/scripts:/init_doc_db.d \\\\', + ); + expect(packagePageSource).toContain( + ' -p 127.0.0.1:10260:10260 \\\\', + ); + expect(packagePageSource).toContain( + " --username '' \\\\", + ); + expect(packagePageSource).toContain( + " --password ''", + ); + }); + + it('passes Node.js and Python credentials outside connection URIs', () => { + const nodeGuide = getArticleByPath('getting-started', ['nodejs-setup']); + const pythonGuide = getArticleByPath('getting-started', ['python-setup']); + + if (!nodeGuide || !pythonGuide) { + throw new Error('Missing curated driver quick start'); + } + + const nodeBlocks = getCodeBlocks(nodeGuide.content, 'javascript'); + const nodeMain = nodeBlocks.find((block) => + block.includes('process.env.DOCUMENTDB_USERNAME'), + ); + const nodeTrusted = nodeBlocks.find((block) => + block.includes('tlsCAFile'), + ); + + expect(nodeGuide.content).not.toContain( + 'mongodb://:', + ); + expect(nodeMain).toContain('process.env.DOCUMENTDB_PASSWORD'); + expect(nodeMain).toContain('if (!username || !password)'); + expect(nodeMain).toContain('auth: { username, password }'); + expect(nodeMain).toContain('authSource: "admin"'); + expect(nodeMain).toContain('new MongoClient(uri, options)'); + expect(nodeTrusted).toContain('auth: { username, password }'); + expect(nodeTrusted).toContain('authSource: "admin"'); + expect(nodeTrusted).not.toContain(''); + + const pythonBlocks = getCodeBlocks(pythonGuide.content, 'python'); + const pythonMain = pythonBlocks.find((block) => + block.includes('os.environ.get("DOCUMENTDB_USERNAME")'), + ); + const pythonTrusted = pythonBlocks.find((block) => + block.includes('tlsCAFile'), + ); + + expect(pythonGuide.content).not.toContain( + 'mongodb://:', + ); + expect(pythonMain).toContain('os.environ.get("DOCUMENTDB_PASSWORD")'); + expect(pythonMain).toContain('if not username or not password:'); + expect(pythonMain).toContain('username=username'); + expect(pythonMain).toContain('password=password'); + expect(pythonTrusted).toContain('username=username'); + expect(pythonTrusted).toContain('password=password'); + expect(pythonTrusted).not.toContain(''); + + const nodeDockerBlock = getCodeBlocks(nodeGuide.content, 'bash').find( + (block) => block.includes('docker run'), + ); + const pythonDockerBlock = getCodeBlocks(pythonGuide.content, 'bash').find( + (block) => block.includes('docker run'), + ); + + for (const block of [nodeDockerBlock, pythonDockerBlock]) { + expect(block).toContain("export DOCUMENTDB_USERNAME=''"); + expect(block).toContain("export DOCUMENTDB_PASSWORD=''"); + expect(block).toContain( + '${DOCUMENTDB_USERNAME:?Set DOCUMENTDB_USERNAME}', + ); + expect(block).toContain( + '${DOCUMENTDB_PASSWORD:?Set DOCUMENTDB_PASSWORD}', + ); + } + }); + it('keeps Package Finder advanced hints linked and version-agnostic', async () => { const { readFile } = await import('node:fs/promises'); const { fileURLToPath } = await import('node:url');