diff --git a/modules/amazon-eks-documentdb/README.md b/modules/amazon-eks-documentdb/README.md index f742262..24c4faa 100644 --- a/modules/amazon-eks-documentdb/README.md +++ b/modules/amazon-eks-documentdb/README.md @@ -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 diff --git a/modules/amazon-eks-documentdb/database-import/hub-import.js b/modules/amazon-eks-documentdb/database-import/hub-import.js new file mode 100644 index 0000000..f150c1b --- /dev/null +++ b/modules/amazon-eks-documentdb/database-import/hub-import.js @@ -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.'); \ No newline at end of file diff --git a/modules/amazon-eks-documentdb/database-import/job.yaml b/modules/amazon-eks-documentdb/database-import/job.yaml new file mode 100644 index 0000000..e4b39a9 --- /dev/null +++ b/modules/amazon-eks-documentdb/database-import/job.yaml @@ -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 \ No newline at end of file diff --git a/modules/amazon-eks-documentdb/database-import/kustomization.yaml b/modules/amazon-eks-documentdb/database-import/kustomization.yaml new file mode 100644 index 0000000..4f266f8 --- /dev/null +++ b/modules/amazon-eks-documentdb/database-import/kustomization.yaml @@ -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 \ No newline at end of file diff --git a/modules/amazon-eks-documentdb/database-import/run.sh b/modules/amazon-eks-documentdb/database-import/run.sh new file mode 100755 index 0000000..a8fe04f --- /dev/null +++ b/modules/amazon-eks-documentdb/database-import/run.sh @@ -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 \ No newline at end of file