mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-08-14 07:50:34 +02:00
feat: add optional MongoDB replica deployment (#765)
This commit is contained in:
@@ -3,6 +3,7 @@ build
|
||||
/repositories
|
||||
repo/
|
||||
db_backups
|
||||
/secrets/mongo-replica-keyfile
|
||||
message.txt
|
||||
|
||||
# macOS
|
||||
|
||||
@@ -79,6 +79,9 @@ docker-compose up -d
|
||||
|
||||
**4. Open** <http://localhost:5000>. The port can be changed in `docker-compose.yml`; putting Anonymous GitHub behind nginx is recommended for HTTPS.
|
||||
|
||||
For an optional remote, hidden, delayed MongoDB replica and backup source, see
|
||||
the [MongoDB replication guide](docs/mongodb-replication.md).
|
||||
|
||||
</details>
|
||||
|
||||
## Scope of anonymization
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
services:
|
||||
mongodb:
|
||||
entrypoint:
|
||||
- /bin/bash
|
||||
- /opt/anonymous-github/mongodb-replica-entrypoint.sh
|
||||
command:
|
||||
- --quiet
|
||||
- --replSet
|
||||
- rs0
|
||||
- --keyFile
|
||||
- /tmp/mongo-replica-keyfile
|
||||
- --bind_ip_all
|
||||
environment:
|
||||
MONGO_REPLICA_KEYFILE_PATH: /run/secrets/mongo-replica-keyfile
|
||||
volumes:
|
||||
- ${MONGO_REPLICA_KEYFILE:-./secrets/mongo-replica-keyfile}:/run/secrets/mongo-replica-keyfile:ro
|
||||
- ./scripts/mongodb-replica-entrypoint.sh:/opt/anonymous-github/mongodb-replica-entrypoint.sh:ro
|
||||
@@ -0,0 +1,38 @@
|
||||
name: anonymous-github-mongo-secondary
|
||||
|
||||
services:
|
||||
mongodb-secondary:
|
||||
image: ${MONGO_IMAGE:-mongo:latest}
|
||||
restart: unless-stopped
|
||||
entrypoint:
|
||||
- /bin/bash
|
||||
- /opt/anonymous-github/mongodb-replica-entrypoint.sh
|
||||
command:
|
||||
- --quiet
|
||||
- --replSet
|
||||
- rs0
|
||||
- --keyFile
|
||||
- /tmp/mongo-replica-keyfile
|
||||
- --bind_ip_all
|
||||
environment:
|
||||
MONGO_REPLICA_KEYFILE_PATH: /run/secrets/mongo-replica-keyfile
|
||||
volumes:
|
||||
- mongodb_secondary_data:/data/db
|
||||
- ${MONGO_REPLICA_KEYFILE:-./secrets/mongo-replica-keyfile}:/run/secrets/mongo-replica-keyfile:ro
|
||||
- ./scripts/mongodb-replica-entrypoint.sh:/opt/anonymous-github/mongodb-replica-entrypoint.sh:ro
|
||||
ports:
|
||||
- ${MONGO_BIND_ADDRESS:-127.0.0.1}:27017:27017
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- mongosh
|
||||
- --quiet
|
||||
- --eval
|
||||
- "db.adminCommand('ping')"
|
||||
interval: 10s
|
||||
timeout: 10s
|
||||
retries: 12
|
||||
start_period: 10s
|
||||
|
||||
volumes:
|
||||
mongodb_secondary_data:
|
||||
+2
-2
@@ -79,7 +79,7 @@ services:
|
||||
retries: 5
|
||||
|
||||
mongodb:
|
||||
image: mongo:latest
|
||||
image: ${MONGO_IMAGE:-mongo:latest}
|
||||
restart: on-failure
|
||||
environment:
|
||||
MONGO_INITDB_ROOT_USERNAME: $DB_USERNAME
|
||||
@@ -87,7 +87,7 @@ services:
|
||||
volumes:
|
||||
- mongodb_data_container:/data/db
|
||||
ports:
|
||||
- 127.0.0.1:27017:27017
|
||||
- ${MONGO_BIND_ADDRESS:-127.0.0.1}:27017:27017
|
||||
command: --quiet
|
||||
healthcheck:
|
||||
test:
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
# Optional remote MongoDB replica
|
||||
|
||||
This deployment option converts the existing standalone MongoDB container into
|
||||
a replica set named `rs0` and adds a remote, hidden, non-voting secondary.
|
||||
Normal single-node deployments continue to use `docker-compose.yml` alone.
|
||||
|
||||
The remote member is designed as a recovery and backup source:
|
||||
|
||||
- It cannot become primary.
|
||||
- Its availability does not affect production majority writes.
|
||||
- It is hidden from application traffic.
|
||||
- It is delayed by one hour by default, providing a short recovery window for
|
||||
accidental deletes or a bad migration.
|
||||
|
||||
Replication is not a backup. Destructive changes eventually reach every
|
||||
member. Continue making encrypted, off-host `mongodump` archives from the
|
||||
secondary.
|
||||
|
||||
## Requirements
|
||||
|
||||
1. Take and verify a `mongodump` backup before converting production.
|
||||
2. Install this repository on both servers.
|
||||
3. Connect the servers through a trusted private network such as WireGuard,
|
||||
Tailscale, a private VLAN, or a cloud private network.
|
||||
4. Create stable DNS names that resolve from both servers and from the
|
||||
application container, for example:
|
||||
|
||||
- `mongo-primary.internal`
|
||||
- `mongo-secondary.internal`
|
||||
|
||||
5. Permit TCP port `27017` only between the required private-network hosts.
|
||||
Do not expose MongoDB to the public internet.
|
||||
6. Run the exact same MongoDB version on both servers. Find the current
|
||||
production version with:
|
||||
|
||||
```bash
|
||||
docker compose exec mongodb mongod --version
|
||||
```
|
||||
|
||||
Set the matching image in both servers' `.env` files:
|
||||
|
||||
```env
|
||||
MONGO_IMAGE=mongo:<exact-version>
|
||||
```
|
||||
|
||||
## 1. Generate the shared member key
|
||||
|
||||
On the primary server:
|
||||
|
||||
```bash
|
||||
./scripts/mongodb-replica.sh generate-key
|
||||
```
|
||||
|
||||
This creates `secrets/mongo-replica-keyfile` without overwriting an existing
|
||||
key. Copy that exact file securely to the same repository-relative path on the
|
||||
secondary server:
|
||||
|
||||
```bash
|
||||
scp secrets/mongo-replica-keyfile \
|
||||
secondary-server:/path/to/anonymous_github/secrets/mongo-replica-keyfile
|
||||
```
|
||||
|
||||
Keep this key outside source control and backups that are accessible to
|
||||
untrusted users. Every replica-set member must share the same key.
|
||||
|
||||
## 2. Configure production
|
||||
|
||||
Add these values to the primary server's `.env`:
|
||||
|
||||
```env
|
||||
# Use the exact version already running in production.
|
||||
MONGO_IMAGE=mongo:<exact-version>
|
||||
|
||||
# The primary server's private/VPN interface.
|
||||
MONGO_BIND_ADDRESS=<primary-private-ip>
|
||||
MONGO_REPLICA_KEYFILE=./secrets/mongo-replica-keyfile
|
||||
|
||||
# Make the replica overlay the default for future Compose commands.
|
||||
COMPOSE_FILE=docker-compose.yml:docker-compose.replica-primary.yml
|
||||
|
||||
# URL-encode special characters in the username and password.
|
||||
MONGODB_URI=mongodb://<user>:<password>@mongo-primary.internal:27017/production?authSource=admin&replicaSet=rs0&retryWrites=true&w=majority
|
||||
```
|
||||
|
||||
Stop application writers, leaving MongoDB available:
|
||||
|
||||
```bash
|
||||
docker compose stop anonymous_github streamer
|
||||
```
|
||||
|
||||
Start MongoDB with replica-set support and initialize the existing database as
|
||||
the primary:
|
||||
|
||||
```bash
|
||||
./scripts/mongodb-replica.sh primary-up \
|
||||
mongo-primary.internal:27017
|
||||
```
|
||||
|
||||
`primary-up` is idempotent. If `rs0` is already initialized, it reports that
|
||||
state instead of replacing the replica-set configuration.
|
||||
|
||||
## 3. Start the remote secondary
|
||||
|
||||
On the secondary server, create a minimal `.env`:
|
||||
|
||||
```env
|
||||
MONGO_IMAGE=mongo:<same-exact-version-as-primary>
|
||||
MONGO_BIND_ADDRESS=<secondary-private-ip>
|
||||
MONGO_REPLICA_KEYFILE=./secrets/mongo-replica-keyfile
|
||||
```
|
||||
|
||||
Start its empty MongoDB data volume:
|
||||
|
||||
```bash
|
||||
./scripts/mongodb-replica.sh secondary-up
|
||||
```
|
||||
|
||||
Do not copy the primary's live WiredTiger volume. MongoDB performs the initial
|
||||
sync after the member is added.
|
||||
|
||||
## 4. Add and verify the secondary
|
||||
|
||||
Back on the primary server, add the remote member with the default one-hour
|
||||
delay:
|
||||
|
||||
```bash
|
||||
./scripts/mongodb-replica.sh add-secondary \
|
||||
mongo-secondary.internal:27017
|
||||
```
|
||||
|
||||
To keep the remote copy current instead, explicitly set a zero-second delay:
|
||||
|
||||
```bash
|
||||
./scripts/mongodb-replica.sh add-secondary \
|
||||
mongo-secondary.internal:27017 0
|
||||
```
|
||||
|
||||
Check initial-sync and replication state:
|
||||
|
||||
```bash
|
||||
./scripts/mongodb-replica.sh status
|
||||
```
|
||||
|
||||
Wait until the remote member reports `SECONDARY`, then restart the application:
|
||||
|
||||
```bash
|
||||
docker compose up -d anonymous_github streamer redis
|
||||
```
|
||||
|
||||
## Operations
|
||||
|
||||
Use the primary overlay for every future operation on the production stack.
|
||||
Keeping `COMPOSE_FILE` in `.env` makes ordinary commands such as
|
||||
`docker compose up -d` and `docker compose logs mongodb` use it automatically.
|
||||
|
||||
Check replica status:
|
||||
|
||||
```bash
|
||||
./scripts/mongodb-replica.sh status
|
||||
```
|
||||
|
||||
If a destructive production operation occurs and the secondary is delayed,
|
||||
stop the secondary container immediately before the delay window elapses:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.replica-secondary.yml stop mongodb-secondary
|
||||
```
|
||||
|
||||
Then take a copy or logical dump of the delayed data before attempting
|
||||
recovery.
|
||||
|
||||
For automatic failover, use three voting data-bearing members instead of
|
||||
turning this two-server recovery topology into a two-voter replica set. Two
|
||||
voters require both servers to acknowledge a majority and can make production
|
||||
unwritable during a network outage.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- All members must use the same replica-set name (`rs0`), MongoDB version, and
|
||||
shared keyfile.
|
||||
- The hostnames stored in `rs.conf()` must resolve from every member.
|
||||
- The application container must also resolve `mongo-primary.internal`. Use
|
||||
private DNS or a Compose `extra_hosts` entry if host DNS is not propagated
|
||||
into Docker.
|
||||
- Check container logs with:
|
||||
|
||||
```bash
|
||||
docker compose logs mongodb
|
||||
docker compose -f docker-compose.replica-secondary.yml logs mongodb-secondary
|
||||
```
|
||||
|
||||
- Re-run `primary-up`, `add-secondary`, or `status` safely; the management
|
||||
operations do not replace existing replica-set members.
|
||||
|
||||
## MongoDB references
|
||||
|
||||
- [Convert a standalone server to a replica set](https://www.mongodb.com/docs/manual/tutorial/convert-standalone-to-replica-set/)
|
||||
- [Deploy a replica set with member authentication](https://www.mongodb.com/docs/v8.0/tutorial/deploy-replica-set-with-keyfile-access-control/)
|
||||
- [Configure a delayed member](https://www.mongodb.com/docs/manual/tutorial/configure-a-delayed-replica-set-member/)
|
||||
- [Hidden replica-set members](https://www.mongodb.com/docs/manual/core/replica-set-hidden-member/)
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
source_keyfile="${MONGO_REPLICA_KEYFILE_PATH:-/run/secrets/mongo-replica-keyfile}"
|
||||
runtime_keyfile="/tmp/mongo-replica-keyfile"
|
||||
|
||||
if [[ ! -s "$source_keyfile" ]]; then
|
||||
echo "MongoDB replica keyfile is missing or empty: $source_keyfile" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Bind-mounted secrets commonly have host ownership or permissive modes that
|
||||
# mongod rejects. Copy to an ephemeral location with the required ownership
|
||||
# and permissions before the official image entrypoint drops privileges.
|
||||
install -m 400 -o mongodb -g mongodb "$source_keyfile" "$runtime_keyfile"
|
||||
|
||||
exec /usr/local/bin/docker-entrypoint.sh "$@"
|
||||
Executable
+176
@@ -0,0 +1,176 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
repository_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
primary_files=(
|
||||
-f "$repository_root/docker-compose.yml"
|
||||
-f "$repository_root/docker-compose.replica-primary.yml"
|
||||
)
|
||||
secondary_files=(
|
||||
-f "$repository_root/docker-compose.replica-secondary.yml"
|
||||
)
|
||||
|
||||
compose_prefix=(docker compose)
|
||||
if [[ -n "${COMPOSE_ENV_FILE:-}" ]]; then
|
||||
compose_prefix+=(--env-file "$COMPOSE_ENV_FILE")
|
||||
fi
|
||||
|
||||
keyfile="${MONGO_REPLICA_KEYFILE:-$repository_root/secrets/mongo-replica-keyfile}"
|
||||
if [[ "$keyfile" != /* ]]; then
|
||||
keyfile="$repository_root/${keyfile#./}"
|
||||
fi
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage:
|
||||
./scripts/mongodb-replica.sh generate-key
|
||||
./scripts/mongodb-replica.sh primary-up <primary-hostname:port>
|
||||
./scripts/mongodb-replica.sh secondary-up
|
||||
./scripts/mongodb-replica.sh add-secondary <secondary-hostname:port> [delay-seconds]
|
||||
./scripts/mongodb-replica.sh status
|
||||
|
||||
Environment:
|
||||
COMPOSE_ENV_FILE Optional Compose environment file.
|
||||
MONGO_REPLICA_KEYFILE Shared keyfile path (default: ./secrets/mongo-replica-keyfile).
|
||||
|
||||
The primary and secondary hostnames must resolve from both MongoDB servers.
|
||||
The remote member is added hidden, priority 0, and non-voting so a WAN outage
|
||||
cannot block production majority writes. Set delay-seconds to 0 for no delay.
|
||||
USAGE
|
||||
}
|
||||
|
||||
validate_host() {
|
||||
if [[ ! "$1" =~ ^[A-Za-z0-9.-]+:[0-9]{1,5}$ ]]; then
|
||||
echo "Expected a resolvable hostname and port, for example mongo-primary.internal:27017" >&2
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
validate_delay() {
|
||||
if [[ ! "$1" =~ ^[0-9]+$ ]]; then
|
||||
echo "Delay must be a non-negative number of seconds" >&2
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
require_keyfile() {
|
||||
if [[ ! -s "$keyfile" ]]; then
|
||||
echo "Replica keyfile not found: $keyfile" >&2
|
||||
echo "Run ./scripts/mongodb-replica.sh generate-key first." >&2
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
primary_compose() {
|
||||
"${compose_prefix[@]}" "${primary_files[@]}" "$@"
|
||||
}
|
||||
|
||||
secondary_compose() {
|
||||
"${compose_prefix[@]}" "${secondary_files[@]}" "$@"
|
||||
}
|
||||
|
||||
mongo_eval() {
|
||||
local javascript="$1"
|
||||
primary_compose exec -T -e "MONGO_RS_EVAL=$javascript" mongodb sh -eu -c \
|
||||
'mongosh --quiet \
|
||||
--username="$MONGO_INITDB_ROOT_USERNAME" \
|
||||
--password="$MONGO_INITDB_ROOT_PASSWORD" \
|
||||
--authenticationDatabase=admin \
|
||||
--eval "$MONGO_RS_EVAL"'
|
||||
}
|
||||
|
||||
wait_for_primary_container() {
|
||||
local attempt
|
||||
for attempt in $(seq 1 60); do
|
||||
if primary_compose exec -T mongodb sh -eu -c \
|
||||
'mongosh --quiet \
|
||||
--username="$MONGO_INITDB_ROOT_USERNAME" \
|
||||
--password="$MONGO_INITDB_ROOT_PASSWORD" \
|
||||
--authenticationDatabase=admin \
|
||||
--eval "db.adminCommand({ ping: 1 }).ok"' >/dev/null 2>&1; then
|
||||
return
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
echo "MongoDB did not become ready within 120 seconds" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
command="${1:-}"
|
||||
case "$command" in
|
||||
generate-key)
|
||||
if [[ -e "$keyfile" ]]; then
|
||||
echo "Refusing to overwrite existing keyfile: $keyfile" >&2
|
||||
exit 2
|
||||
fi
|
||||
mkdir -p "$(dirname "$keyfile")"
|
||||
umask 077
|
||||
openssl rand -base64 756 > "$keyfile"
|
||||
chmod 600 "$keyfile"
|
||||
echo "Created $keyfile"
|
||||
echo "Copy this exact file securely to the secondary server."
|
||||
;;
|
||||
|
||||
primary-up)
|
||||
primary_host="${2:-}"
|
||||
validate_host "$primary_host"
|
||||
require_keyfile
|
||||
primary_compose up -d mongodb
|
||||
wait_for_primary_container
|
||||
mongo_eval "
|
||||
try {
|
||||
const status = rs.status();
|
||||
print('Replica set already initialized as ' + status.set);
|
||||
} catch (error) {
|
||||
if (error.code !== 94 && error.codeName !== 'NotYetInitialized') throw error;
|
||||
printjson(rs.initiate({
|
||||
_id: 'rs0',
|
||||
members: [{ _id: 0, host: '$primary_host', priority: 1, votes: 1 }]
|
||||
}));
|
||||
}
|
||||
"
|
||||
;;
|
||||
|
||||
secondary-up)
|
||||
require_keyfile
|
||||
secondary_compose up -d mongodb-secondary
|
||||
;;
|
||||
|
||||
add-secondary)
|
||||
secondary_host="${2:-}"
|
||||
delay="${3:-3600}"
|
||||
validate_host "$secondary_host"
|
||||
validate_delay "$delay"
|
||||
mongo_eval "
|
||||
const host = '$secondary_host';
|
||||
const existing = rs.conf().members.find((member) => member.host === host);
|
||||
if (existing) {
|
||||
print('Secondary already configured: ' + host);
|
||||
} else {
|
||||
printjson(rs.add({
|
||||
host,
|
||||
priority: 0,
|
||||
votes: 0,
|
||||
hidden: true,
|
||||
secondaryDelaySecs: $delay
|
||||
}));
|
||||
}
|
||||
"
|
||||
;;
|
||||
|
||||
status)
|
||||
mongo_eval "
|
||||
printjson(rs.status().members.map((member) => ({
|
||||
name: member.name,
|
||||
state: member.stateStr,
|
||||
health: member.health,
|
||||
replicationTime: member.optimeDate
|
||||
})));
|
||||
"
|
||||
;;
|
||||
|
||||
*)
|
||||
usage
|
||||
[[ -n "$command" ]] && exit 2
|
||||
;;
|
||||
esac
|
||||
+10
-1
@@ -26,6 +26,12 @@ interface Config {
|
||||
DB_USERNAME: string;
|
||||
DB_PASSWORD: string;
|
||||
DB_HOSTNAME: string;
|
||||
/**
|
||||
* Complete MongoDB connection string. When set, this takes precedence over
|
||||
* DB_USERNAME, DB_PASSWORD, and DB_HOSTNAME and supports replica-set seed
|
||||
* lists and options.
|
||||
*/
|
||||
MONGODB_URI: string;
|
||||
FOLDER: string;
|
||||
additionalExtensions: string[];
|
||||
S3_BUCKET: string | null;
|
||||
@@ -73,6 +79,7 @@ const config: Config = {
|
||||
DB_USERNAME: "admin",
|
||||
DB_PASSWORD: "password",
|
||||
DB_HOSTNAME: "mongodb",
|
||||
MONGODB_URI: "",
|
||||
REDIS_HOSTNAME: "redis",
|
||||
REDIS_PORT: 6379,
|
||||
FOLDER: resolve(__dirname, "..", "repositories"),
|
||||
@@ -142,8 +149,10 @@ if (isProduction) {
|
||||
const insecureDefaults: [string, string][] = [
|
||||
["CLIENT_ID", "CLIENT_ID"],
|
||||
["CLIENT_SECRET", "CLIENT_SECRET"],
|
||||
["DB_PASSWORD", "password"],
|
||||
];
|
||||
if (!config.MONGODB_URI) {
|
||||
insecureDefaults.push(["DB_PASSWORD", "password"]);
|
||||
}
|
||||
for (const [key, badValue] of insecureDefaults) {
|
||||
if ((config as unknown as Record<string, unknown>)[key] === badValue) {
|
||||
throw new Error(
|
||||
|
||||
+10
-4
@@ -8,7 +8,12 @@ import PullRequest from "../core/PullRequest";
|
||||
import AnonymizedGistModel from "../core/model/anonymizedGists/anonymizedGists.model";
|
||||
import Gist from "../core/Gist";
|
||||
|
||||
const MONGO_URL = `mongodb://${config.DB_USERNAME}:${config.DB_PASSWORD}@${config.DB_HOSTNAME}:27017/`;
|
||||
export function getMongoUrl(): string {
|
||||
return (
|
||||
config.MONGODB_URI ||
|
||||
`mongodb://${config.DB_USERNAME}:${config.DB_PASSWORD}@${config.DB_HOSTNAME}:27017/production`
|
||||
);
|
||||
}
|
||||
|
||||
export const database = mongoose.connection;
|
||||
|
||||
@@ -16,11 +21,12 @@ export let isConnected = false;
|
||||
|
||||
export async function connect() {
|
||||
mongoose.set("strictQuery", false);
|
||||
await mongoose.connect(MONGO_URL + "production", {
|
||||
authSource: "admin",
|
||||
const options: ConnectOptions = {
|
||||
appName: "Anonymous GitHub Server",
|
||||
compressors: "zstd",
|
||||
} as ConnectOptions);
|
||||
};
|
||||
if (!config.MONGODB_URI) options.authSource = "admin";
|
||||
await mongoose.connect(getMongoUrl(), options);
|
||||
isConnected = true;
|
||||
|
||||
return database;
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
require("ts-node/register/transpile-only");
|
||||
const { expect } = require("chai");
|
||||
|
||||
const config = require("../src/config").default;
|
||||
const { getMongoUrl } = require("../src/server/database");
|
||||
|
||||
describe("MongoDB connection configuration", function () {
|
||||
const original = {
|
||||
MONGODB_URI: config.MONGODB_URI,
|
||||
DB_USERNAME: config.DB_USERNAME,
|
||||
DB_PASSWORD: config.DB_PASSWORD,
|
||||
DB_HOSTNAME: config.DB_HOSTNAME,
|
||||
};
|
||||
|
||||
afterEach(function () {
|
||||
Object.assign(config, original);
|
||||
});
|
||||
|
||||
it("uses a complete replica-set URI when configured", function () {
|
||||
const uri =
|
||||
"mongodb://user:password@mongo-primary.internal:27017/production" +
|
||||
"?authSource=admin&replicaSet=rs0&w=majority";
|
||||
config.MONGODB_URI = uri;
|
||||
|
||||
expect(getMongoUrl()).to.equal(uri);
|
||||
});
|
||||
|
||||
it("retains the existing single-node connection fallback", function () {
|
||||
config.MONGODB_URI = "";
|
||||
config.DB_USERNAME = "user";
|
||||
config.DB_PASSWORD = "password";
|
||||
config.DB_HOSTNAME = "mongodb";
|
||||
|
||||
expect(getMongoUrl()).to.equal(
|
||||
"mongodb://user:password@mongodb:27017/production"
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user