From 9dd1f740183055f5dba7cd2668081ae7d64d5ee2 Mon Sep 17 00:00:00 2001 From: Sam Willis Date: Thu, 22 Jun 2023 21:06:54 +1000 Subject: [PATCH 1/3] mongodb: added configure script and options to create a superuser on startup. --- src/modules/services/mongodb.nix | 73 +++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 6 deletions(-) diff --git a/src/modules/services/mongodb.nix b/src/modules/services/mongodb.nix index 1469897d2..48e7157e0 100644 --- a/src/modules/services/mongodb.nix +++ b/src/modules/services/mongodb.nix @@ -15,12 +15,56 @@ let startScript = pkgs.writeShellScriptBin "start-mongodb" '' set -euo pipefail ${setupScript}/bin/setup-mongodb - exec ${cfg.package}/bin/mongod ${lib.concatStringsSep " " cfg.additionalArgs} -dbpath "$MONGODBDATA" + exec ${cfg.package}/bin/mongod ${ + lib.concatStringsSep " " cfg.additionalArgs + } -dbpath "$MONGODBDATA" ''; + + configureScript = pkgs.writeShellScriptBin "configure-mongodb" '' + set -euo pipefail + + mongodArgs=(${lib.concatStringsSep " " cfg.additionalArgs}) + mongoShellArgs="" + + # Loop over the arguments, check if it contains --port + # If it does grab the port which must be the following arg + # wanted to keep the additionalArgs to not break any existing + # configs using it. + for i in "''${!mongodArgs[@]}"; do + if [[ "''${mongodArgs[$i]}" = "--port" ]]; then + mongoShellArgs="--port ''${mongodArgs[i + 1]}" + break + fi + done + + while ! mongo --quiet --eval "{ ping: 1 }" ''${mongoShellArgs} 2>&1 >/dev/null ; do + sleep 1 + done + + if [ "${cfg.initDatabaseUsername}" ] && [ "${cfg.initDatabasePassword}" ]; then + echo "CREATING USER" + rootAuthDatabase="admin" + mongo ''${mongoShellArgs} "$rootAuthDatabase" >/dev/null <<-EOJS + db.createUser({ + user: "${cfg.initDatabaseUsername}", + pwd: "${cfg.initDatabasePassword}",`` + roles: [ { role: 'root', db: "$rootAuthDatabase" } ] + }) + EOJS + fi + + # We need to keep this process running otherwise all processes stop + tail -f /dev/null + ''; + in { imports = [ - (lib.mkRenamedOptionModule [ "mongodb" "enable" ] [ "services" "mongodb" "enable" ]) + (lib.mkRenamedOptionModule [ "mongodb" "enable" ] [ + "services" + "mongodb" + "enable" + ]) ]; options.services.mongodb = { @@ -41,16 +85,33 @@ in Additional arguments passed to `mongod`. ''; }; + + initDatabaseUsername = lib.mkOption { + type = types.str; + default = ""; + example = "mongoadmin"; + description = '' + This used in conjunction with initDatabasePassword, create a new user and set that user's password. This user is created in the admin authentication database and given the role of root, which is a "superuser" role. + ''; + }; + + initDatabasePassword = lib.mkOption { + type = types.str; + default = ""; + example = "secret"; + description = '' + This used in conjunction with initDatabaseUsername, create a new user and set that user's password. This user is created in the admin authentication database and given the role of root, which is a "superuser" role. + ''; + }; }; config = lib.mkIf cfg.enable { - packages = [ - cfg.package - pkgs.mongodb-tools - ]; + packages = [ cfg.package pkgs.mongodb-tools ]; env.MONGODBDATA = config.env.DEVENV_STATE + "/mongodb"; processes.mongodb.exec = "${startScript}/bin/start-mongodb"; + processes.mongodb-configure.exec = + "${configureScript}/bin/configure-mongodb"; }; } From f8b5e098bbcd082242f38407825b3773db6e1c71 Mon Sep 17 00:00:00 2001 From: Sam Willis Date: Thu, 22 Jun 2023 21:24:54 +1000 Subject: [PATCH 2/3] mongodb: final touches for creating initial user. --- src/modules/services/mongodb.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/services/mongodb.nix b/src/modules/services/mongodb.nix index 48e7157e0..beff24449 100644 --- a/src/modules/services/mongodb.nix +++ b/src/modules/services/mongodb.nix @@ -42,7 +42,6 @@ let done if [ "${cfg.initDatabaseUsername}" ] && [ "${cfg.initDatabasePassword}" ]; then - echo "CREATING USER" rootAuthDatabase="admin" mongo ''${mongoShellArgs} "$rootAuthDatabase" >/dev/null <<-EOJS db.createUser({ From b79443d88ce411cfdccc45da14714f8955398650 Mon Sep 17 00:00:00 2001 From: Sam Willis Date: Fri, 23 Jun 2023 13:13:53 +1000 Subject: [PATCH 3/3] Added mongosh to mongo service and added test for mongodb with initial user creation. --- examples/mongodb/.test.sh | 61 ++++++++++++++++++++++++++++++++ examples/mongodb/devenv.nix | 9 +++++ examples/mongodb/devenv.yaml | 4 +++ src/modules/services/mongodb.nix | 7 ++-- 4 files changed, 78 insertions(+), 3 deletions(-) create mode 100755 examples/mongodb/.test.sh create mode 100644 examples/mongodb/devenv.nix create mode 100644 examples/mongodb/devenv.yaml diff --git a/examples/mongodb/.test.sh b/examples/mongodb/.test.sh new file mode 100755 index 000000000..e99f665bd --- /dev/null +++ b/examples/mongodb/.test.sh @@ -0,0 +1,61 @@ +#!/bin/sh +set -x + +mongosh --version +mongod --version + +# Start service in the background and store the PID +echo "Starting mongo service..." +devenv up& +DEVENV_PID=$! + +check_mongo_status() { + echo "Waiting for service to become available..." + MONGO_OUTPUT=$(mongosh --quiet --eval "{ ping: 1 }" 2>&1) + MONGO_EXIT_STATUS=$? +} + +check_if_mongo_user_created() { + # This line queries mongo using the shell and trims the output to make sure + # it is either an empty string or the created user document + createdUser=$(echo "use admin\n db.system.users.find({ user: \"mongouser\", db: \"admin\", \"roles.role\": \"root\", \"roles.db\": \"admin\" })" | mongosh --quiet --eval --shell | tail -n +2 | sed 's/^admin> //') + + if [ -n "$createdUser" ]; then + MONGO_EXIT_STATUS=0 + else + MONGO_EXIT_STATUS=1 + fi +} + +# Just to allow the service some time to start up +sleep 10 + +for i in $(seq 1 10); do + check_mongo_status + if [ $MONGO_EXIT_STATUS -eq 0 ]; then + echo "Service is up..." + break + else + sleep 1 + fi +done + +echo "Startup complete..." +echo "Checking for initial user creation..." +for i in $(seq 1 10); do + check_if_mongo_user_created + if [ $MONGO_EXIT_STATUS -eq 0 ]; then + echo "Initial user created..." + break + else + sleep 1 + fi +done + +echo 'Killing service...' +pkill -P $DEVENV_PID +wait $DEVENV_PID 2>&1 >/dev/null + +# Exit the script +exit $MONGO_EXIT_STATUS + diff --git a/examples/mongodb/devenv.nix b/examples/mongodb/devenv.nix new file mode 100644 index 000000000..17f6befac --- /dev/null +++ b/examples/mongodb/devenv.nix @@ -0,0 +1,9 @@ +{ pkgs, config, ... }: + +{ + services.mongodb = { + enable = true; + initDatabaseUsername = "mongouser"; + initDatabasePassword = "secret"; + }; +} diff --git a/examples/mongodb/devenv.yaml b/examples/mongodb/devenv.yaml new file mode 100644 index 000000000..89a8475be --- /dev/null +++ b/examples/mongodb/devenv.yaml @@ -0,0 +1,4 @@ +allowUnfree: true +inputs: + nixpkgs: + url: github:NixOS/nixpkgs/nixpkgs-unstable diff --git a/src/modules/services/mongodb.nix b/src/modules/services/mongodb.nix index beff24449..60fb0093d 100644 --- a/src/modules/services/mongodb.nix +++ b/src/modules/services/mongodb.nix @@ -37,13 +37,14 @@ let fi done - while ! mongo --quiet --eval "{ ping: 1 }" ''${mongoShellArgs} 2>&1 >/dev/null ; do + while ! ${pkgs.mongosh}/bin/mongosh --quiet --eval "{ ping: 1 }" ''${mongoShellArgs} 2>&1 >/dev/null ; do sleep 1 done if [ "${cfg.initDatabaseUsername}" ] && [ "${cfg.initDatabasePassword}" ]; then + echo "Creating initial user" rootAuthDatabase="admin" - mongo ''${mongoShellArgs} "$rootAuthDatabase" >/dev/null <<-EOJS + ${pkgs.mongosh}/bin/mongosh ''${mongoShellArgs} "$rootAuthDatabase" >/dev/null <<-EOJS db.createUser({ user: "${cfg.initDatabaseUsername}", pwd: "${cfg.initDatabasePassword}",`` @@ -105,7 +106,7 @@ in }; config = lib.mkIf cfg.enable { - packages = [ cfg.package pkgs.mongodb-tools ]; + packages = [ cfg.package pkgs.mongodb-tools pkgs.mongosh ]; env.MONGODBDATA = config.env.DEVENV_STATE + "/mongodb";