feat(database-import): add DocumentDB import job and script with example data

This commit is contained in:
Cédric Verstraeten
2026-08-11 13:00:10 +00:00
parent 2150a146b9
commit d888d7e243
5 changed files with 338 additions and 0 deletions

View File

@@ -163,6 +163,7 @@ through the `mongodb-config` ConfigMap.
```bash
helm repo add kerberos https://charts.kerberos.io
helm install hub kerberos/hub \
--version 0.127.0 \
-n kerberos-hub \
-f your-hub-values.yaml \
-f hub-documentdb-values.yaml
@@ -186,6 +187,32 @@ kubectl run mongosh --rm -it --restart=Never -n kerberos-hub \
--overrides='{"spec":{"volumes":[{"name":"ca","secret":{"secretName":"mongodb-ca"}}],"containers":[{"name":"mongosh","image":"mongodb/mongodb-community-server:7.0-ubi8","stdin":true,"tty":true,"command":["mongosh"],"args":["'"$(terraform output -raw mongodb_uri)"'&tls=true&tlsCAFile=/certs/global-bundle.pem"],"volumeMounts":[{"name":"ca","mountPath":"/certs"}]}]}}'
```
### 5. Import the example Hub data
This module has a separate DocumentDB import under [`database-import`](database-import).
It uses the chart-managed `mongodb-config`, mounts `mongodb-ca`, forces TLS with
the Amazon RDS CA bundle, and refuses to run unless the backend flavor is
`documentdb` with retryable writes disabled. Install Hub chart `0.127.0` or
newer before running it.
Run it after installing Hub:
```bash
./database-import/run.sh
```
The import is idempotent: it upserts two example users, one subscription and
five settings documents using fixed IDs, then verifies those records. It can be
rerun after deleting or replacing the DocumentDB cluster.
| Account | Password | Role |
| ------- | -------- | ---- |
| `example-user` | `example-password` | Hub owner |
| `example-application` | `example-password` | Admin application |
These are public example credentials. Do not use this seed data in a production
deployment.
## Persistent volumes
The EBS CSI driver is installed, but EKS ships `gp2` as the default storage

View File

@@ -0,0 +1,163 @@
const database = db.getSiblingDB('Kerberos');
function upsert(collection, id, values) {
const result = database.getCollection(collection).updateOne(
{ _id: ObjectId(id) },
{ $set: values },
{ upsert: true },
);
if (!result.acknowledged) {
throw new Error(`Upsert was not acknowledged for ${collection}/${id}`);
}
}
// Login: example-user / example-password
upsert('users', '57e1011e3178aa6c5cc774d1', {
username: 'example-user',
email: 'example-user@email.com',
password: '$2a$10$jwLcD/.UT/1WLK7ct1XuHewI3GQXwW3zerPhCCs7QDrReEuIHbVYi',
role: 'owner',
google2fa_enabled: false,
timezone: 'Europe/Brussels',
isActive: NumberLong('1'),
registerToken: '',
updated_at: ISODate('2020-06-14T05:01:35.000Z'),
created_at: ISODate('2016-09-20T09:27:58.811Z'),
amazon_secret_access_key: 'K6rRLBI1xxxCk3C1H',
amazon_access_key_id: 'AKIAxxxxxxG5Q',
card_brand: 'Visa',
card_last_four: '0000',
card_status: 'ok',
card_status_message: null,
});
// Admin login: example-application / example-password
upsert('users', '57e1011e3178aa6c5cc774d2', {
username: 'example-application',
email: 'example-application@email.com',
password: '$2a$10$jwLcD/.UT/1WLK7ct1XuHewI3GQXwW3zerPhCCs7QDrReEuIHbVYi',
role: 'application',
google2fa_enabled: false,
timezone: 'Europe/Brussels',
isActive: NumberLong('1'),
registerToken: '',
updated_at: ISODate('2020-06-14T05:01:35.000Z'),
created_at: ISODate('2016-09-20T09:27:58.811Z'),
});
upsert('subscriptions', '57e1011e3178aa6c5cc774d1', {
name: 'default',
stripe_id: 'sub_9ECyjjMz3R7etK',
stripe_plan: 'enterprise',
quantity: 1,
trial_ends_at: null,
ends_at: null,
user_id: '57e1011e3178aa6c5cc774d1',
updated_at: ISODate('2021-04-27T09:45:30.169Z'),
created_at: ISODate('2016-09-20T09:35:03.448Z'),
stripe_status: 'active',
});
upsert('settings', '5a72c509e17699d18ada9154', {
key: 'plan',
map: {
basic: {
level: NumberInt(1),
uploadLimit: NumberInt(100),
videoLimit: NumberInt(100),
usage: NumberInt(500),
analysisLimit: NumberInt(0),
dayLimit: NumberInt(3),
},
premium: {
level: NumberInt(2),
uploadLimit: NumberInt(500),
videoLimit: NumberInt(500),
usage: NumberInt(1000),
analysisLimit: NumberInt(0),
dayLimit: NumberInt(7),
},
gold: {
level: NumberInt(3),
uploadLimit: NumberInt(1000),
videoLimit: NumberInt(1000),
usage: NumberInt(3000),
analysisLimit: NumberInt(1000),
dayLimit: NumberInt(30),
},
business: {
level: NumberInt(4),
uploadLimit: NumberInt(99999999),
videoLimit: NumberInt(99999999),
usage: NumberInt(10000),
analysisLimit: NumberInt(1000),
dayLimit: NumberInt(30),
},
enterprise: {
level: NumberInt(5),
uploadLimit: NumberInt(99999999),
videoLimit: NumberInt(99999999),
usage: NumberInt(99999999),
analysisLimit: NumberInt(5000),
dayLimit: NumberInt(30),
},
},
});
upsert('settings', '63f346ec64011a574161cf99', {
key: 'classifications',
map: {
objects: [
{ text: 'Car', value: 'car', icon: 'car' },
{ text: 'Person', value: 'pedestrian', icon: 'pedestrian' },
],
},
});
upsert('settings', '5a43fa12d885eb7da57046b3', {
key: 'sequence',
map: { timeBetween: NumberInt(60) },
});
upsert('settings', '5a4d3a6bd885eb7da5e6b297', {
key: 'throttler',
map: { waitingTime: NumberInt(60) },
});
upsert('settings', '5a53d0a0d885eb7da53ed5a6', {
key: 'analysis',
map: { waitingTime: NumberInt(15) },
});
const importedUsers = database.users.countDocuments({
_id: {
$in: [
ObjectId('57e1011e3178aa6c5cc774d1'),
ObjectId('57e1011e3178aa6c5cc774d2'),
],
},
});
const importedSubscriptions = database.subscriptions.countDocuments({
_id: ObjectId('57e1011e3178aa6c5cc774d1'),
});
const importedSettings = database.settings.countDocuments({
_id: {
$in: [
ObjectId('5a72c509e17699d18ada9154'),
ObjectId('63f346ec64011a574161cf99'),
ObjectId('5a43fa12d885eb7da57046b3'),
ObjectId('5a4d3a6bd885eb7da5e6b297'),
ObjectId('5a53d0a0d885eb7da53ed5a6'),
],
},
});
if (importedUsers !== 2 || importedSubscriptions !== 1 || importedSettings !== 5) {
throw new Error(
`Import verification failed: users=${importedUsers}, `
+ `subscriptions=${importedSubscriptions}, settings=${importedSettings}`,
);
}
print('Imported 2 users, 1 subscription, and 5 settings records into Kerberos.');

View File

@@ -0,0 +1,81 @@
apiVersion: batch/v1
kind: Job
metadata:
name: hub-documentdb-import
labels:
app.kubernetes.io/name: hub-documentdb-import
spec:
activeDeadlineSeconds: 300
backoffLimit: 1
template:
metadata:
labels:
app.kubernetes.io/name: hub-documentdb-import
spec:
restartPolicy: Never
containers:
- name: import
image: mongo:7.0
imagePullPolicy: IfNotPresent
command:
- sh
- -ec
- |
flavor="$(printf '%s' "$MONGODB_FLAVOR" | tr '[:upper:]' '[:lower:]')"
retry_writes="$(printf '%s' "$MONGODB_RETRY_WRITES" | tr '[:upper:]' '[:lower:]')"
if [ "$flavor" != "documentdb" ]; then
echo "Refusing import: MONGODB_FLAVOR must be documentdb" >&2
exit 1
fi
if [ "$retry_writes" != "false" ]; then
echo "Refusing import: MONGODB_RETRY_WRITES must be false for DocumentDB" >&2
exit 1
fi
if [ ! -r /certs/global-bundle.pem ]; then
echo "Refusing import: /certs/global-bundle.pem is not readable" >&2
exit 1
fi
mongosh "$MONGODB_URI" \
--tls \
--tlsCAFile /certs/global-bundle.pem \
--quiet \
--file /scripts/hub-import.js
env:
- name: MONGODB_URI
valueFrom:
configMapKeyRef:
name: mongodb-config
key: MONGODB_URI
- name: MONGODB_FLAVOR
valueFrom:
configMapKeyRef:
name: mongodb-config
key: MONGODB_FLAVOR
- name: MONGODB_RETRY_WRITES
valueFrom:
configMapKeyRef:
name: mongodb-config
key: MONGODB_RETRY_WRITES
resources:
requests:
cpu: 10m
memory: 64Mi
volumeMounts:
- name: import-script
mountPath: /scripts
readOnly: true
- name: mongodb-tls
mountPath: /certs
readOnly: true
volumes:
- name: import-script
configMap:
name: hub-documentdb-import
- name: mongodb-tls
secret:
secretName: mongodb-ca
items:
- key: global-bundle.pem
path: global-bundle.pem

View File

@@ -0,0 +1,13 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
generatorOptions:
disableNameSuffixHash: true
configMapGenerator:
- name: hub-documentdb-import
files:
- hub-import.js
resources:
- job.yaml

View File

@@ -0,0 +1,54 @@
#!/usr/bin/env bash
set -euo pipefail
namespace="${NAMESPACE:-kerberos-hub}"
timeout="${TIMEOUT:-5m}"
job_name="hub-documentdb-import"
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
for command_name in kubectl; do
if ! command -v "$command_name" >/dev/null 2>&1; then
echo "Required command not found: $command_name" >&2
exit 1
fi
done
flavor="$(kubectl get configmap mongodb-config \
--namespace "$namespace" \
--output jsonpath='{.data.MONGODB_FLAVOR}')"
retry_writes="$(kubectl get configmap mongodb-config \
--namespace "$namespace" \
--output jsonpath='{.data.MONGODB_RETRY_WRITES}')"
ca_bundle="$(kubectl get secret mongodb-ca \
--namespace "$namespace" \
--output jsonpath='{.data.global-bundle\.pem}')"
if [[ "${flavor,,}" != "documentdb" ]]; then
echo "Refusing import: mongodb-config MONGODB_FLAVOR must be documentdb" >&2
exit 1
fi
if [[ "${retry_writes,,}" != "false" ]]; then
echo "Refusing import: mongodb-config MONGODB_RETRY_WRITES must be false" >&2
exit 1
fi
if [[ -z "$ca_bundle" ]]; then
echo "Refusing import: mongodb-ca/global-bundle.pem is missing" >&2
exit 1
fi
kubectl delete job "$job_name" \
--namespace "$namespace" \
--ignore-not-found=true \
--wait=true
kubectl apply --kustomize "$script_dir" --namespace "$namespace"
if ! kubectl wait \
--namespace "$namespace" \
--for=condition=complete \
--timeout="$timeout" \
"job/$job_name"; then
kubectl logs --namespace "$namespace" "job/$job_name" --all-containers=true || true
exit 1
fi
kubectl logs --namespace "$namespace" "job/$job_name" --all-containers=true