Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
FROM docker:stable
COPY start-mongodb.sh /start-mongodb.sh
RUN chmod +x /start-mongodb.sh
COPY stop-mongodb.sh /stop-mongodb.sh
RUN chmod +x /start-mongodb.sh /stop-mongodb.sh
ENTRYPOINT ["/start-mongodb.sh"]
10 changes: 10 additions & 0 deletions action-types.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,13 @@ inputs:
type: string
mongodb-container-name:
type: string
mongodb-key:
type: string
mongodb-authsource:
type: string
mongodb-replica-set-host:
type: string
docker-network:
type: string
docker-network-alias:
type: string
35 changes: 33 additions & 2 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ inputs:
default: 'mongo'

mongodb-version:
description: 'MongoDB version to use (default "latest")'
description: 'MongoDB version to use (default: "latest")'
required: false
default: 'latest'

Expand All @@ -22,7 +22,7 @@ inputs:
default: ''

mongodb-port:
description: 'MongoDB port to use (default 27017)'
description: 'MongoDB port to use (default: 27017)'
required: false
default: 27017

Expand All @@ -46,6 +46,31 @@ inputs:
required: false
default: 'mongodb'

mongodb-key:
description: 'MongoDB key, required if replica set and auth are setup through username and password (no key set by default)'
required: false
default: ''

mongodb-authsource:
description: 'MongoDB authenticationDatabase a.k.a authSource to use (default: "admin" based on https://github.com/docker-library/mongo/blob/master/8.0/docker-entrypoint.sh#L372C4-L372C20)'
required: false
default: 'admin'

mongodb-replica-set-host:
description: 'MongoDB replica set host, must be accessible from both internal container and external usage (default: "localhost")'
required: false
default: 'localhost'

docker-network:
description: 'Docker network to attach the MongoDB container to. If not provided, will try to use the default GitHub Actions network if available (github_network_<rand>).'
required: false
default: ''

docker-network-alias:
description: 'Network alias for the MongoDB container when attaching to a Docker network. If not provided, will use mongodb-container-name input'
required: false
default: ''

runs:
using: 'docker'
image: 'Dockerfile'
Expand All @@ -58,3 +83,9 @@ runs:
- ${{ inputs.mongodb-username }}
- ${{ inputs.mongodb-password }}
- ${{ inputs.mongodb-container-name }}
- ${{ inputs.mongodb-key }}
- ${{ inputs.mongodb-authsource }}
- ${{ inputs.mongodb-replica-set-host }}
- ${{ inputs.docker-network }}
- ${{ inputs.docker-network-alias }}
post-entrypoint: /stop-mongodb.sh
108 changes: 89 additions & 19 deletions start-mongodb.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,33 @@ MONGODB_DB=$5
MONGODB_USERNAME=$6
MONGODB_PASSWORD=$7
MONGODB_CONTAINER_NAME=$8
MONGODB_KEY=$9
MONGODB_AUTHSOURCE=${10}
MONGODB_REPLICA_SET_HOST=${11:-"localhost"}
DOCKER_NETWORK=${12}
DOCKER_NETWORK_ALIAS=${13:-$MONGODB_CONTAINER_NAME}

# If DOCKER_NETWORK not provided, try to detect the default GitHub Actions network
if [ -z "$DOCKER_NETWORK" ]; then
DOCKER_NETWORK=$(docker network ls --no-trunc --format '{{.Name}}' | grep '^github_network')
fi

# Build network args if a network is set
NETWORK_ARGS=""
if [ -n "$DOCKER_NETWORK" ]; then
NETWORK_ARGS="--network $DOCKER_NETWORK --network-alias $DOCKER_NETWORK_ALIAS"
fi

# Echo selected network info for visibility
echo "::group::Selecting Docker network"
if [ -n "$DOCKER_NETWORK" ]; then
echo " - Docker network: [$DOCKER_NETWORK]"
echo " - Network alias: [$DOCKER_NETWORK_ALIAS]"
else
echo " - No Docker network provided; container will use default Docker network."
fi
echo ""
echo "::endgroup::"

# `mongosh` is used starting from MongoDB 5.x
MONGODB_CLIENT="mongosh --quiet"
Expand Down Expand Up @@ -47,16 +74,8 @@ wait_for_mongodb () {
TIMER=0

MONGODB_ARGS=""

if [ -z "$MONGODB_REPLICA_SET" ]
then
if [ -z "$MONGODB_USERNAME" ]
then
MONGODB_ARGS=""
else
# no replica set, but username given: use them as args
MONGODB_ARGS="--username $MONGODB_USERNAME --password $MONGODB_PASSWORD"
fi
if [ -n "$MONGODB_USERNAME" ]; then
MONGODB_ARGS="--username $MONGODB_USERNAME --password $MONGODB_PASSWORD --authenticationDatabase $MONGODB_AUTHSOURCE"
fi

# until ${WAIT_FOR_MONGODB_COMMAND}
Expand All @@ -66,7 +85,7 @@ wait_for_mongodb () {
sleep 1
TIMER=$((TIMER + 1))

if [[ $TIMER -eq 20 ]]; then
if [ "$TIMER" -eq 20 ]; then
echo "MongoDB did not initialize within 20 seconds. Exiting."
exit 2
fi
Expand All @@ -82,7 +101,7 @@ wait_for_mongodb () {
# docker rm -f $MONGODB_CONTAINER_NAME
# fi


# If no replica set specified, run single node
if [ -z "$MONGODB_REPLICA_SET" ]; then
echo "::group::Starting single-node instance, no replica set"
echo " - port [$MONGODB_PORT]"
Expand All @@ -92,7 +111,13 @@ if [ -z "$MONGODB_REPLICA_SET" ]; then
echo " - container-name [$MONGODB_CONTAINER_NAME]"
echo ""

docker run --name $MONGODB_CONTAINER_NAME --publish $MONGODB_PORT:$MONGODB_PORT -e MONGO_INITDB_DATABASE=$MONGODB_DB -e MONGO_INITDB_ROOT_USERNAME=$MONGODB_USERNAME -e MONGO_INITDB_ROOT_PASSWORD=$MONGODB_PASSWORD --detach $MONGODB_IMAGE:$MONGODB_VERSION --port $MONGODB_PORT
docker run --name $MONGODB_CONTAINER_NAME \
$NETWORK_ARGS \
--publish $MONGODB_PORT:$MONGODB_PORT \
-e MONGO_INITDB_DATABASE=$MONGODB_DB \
-e MONGO_INITDB_ROOT_USERNAME=$MONGODB_USERNAME \
-e MONGO_INITDB_ROOT_PASSWORD=$MONGODB_PASSWORD \
--detach $MONGODB_IMAGE:$MONGODB_VERSION --port $MONGODB_PORT

if [ $? -ne 0 ]; then
echo "Error starting MongoDB Docker container"
Expand All @@ -101,36 +126,81 @@ if [ -z "$MONGODB_REPLICA_SET" ]; then
echo "::endgroup::"

wait_for_mongodb

exit 0
fi


echo "::group::Starting MongoDB as single-node replica set"
echo " - port [$MONGODB_PORT]"
echo " - version [$MONGODB_VERSION]"
echo " - replica set [$MONGODB_REPLICA_SET]"
if [ -n "$MONGODB_KEY" ]; then
echo " - keyFile provided: yes"
else
echo " - keyFile provided: no (random)"
fi
echo ""

# For replica set mode:
# If auth (username/password) is requested, ensure mongodb-key is provided, otherwise generate random
if { [ -n "$MONGODB_USERNAME" ] || [ -n "$MONGODB_PASSWORD" ]; } && [ -z "$MONGODB_KEY" ]; then
MONGODB_KEY=$(dd if=/dev/urandom bs=256 count=1 2>/dev/null | base64 | tr -d '\n')
fi

MONGODB_CMD_ARGS="--port \"$MONGODB_PORT\""

docker run --name $MONGODB_CONTAINER_NAME --publish $MONGODB_PORT:$MONGODB_PORT --detach $MONGODB_IMAGE:$MONGODB_VERSION --port $MONGODB_PORT --replSet $MONGODB_REPLICA_SET
if [ -n "$MONGODB_REPLICA_SET" ]; then
MONGODB_CMD_ARGS="$MONGODB_CMD_ARGS --replSet \"$MONGODB_REPLICA_SET\""
fi

if [ -n "$MONGODB_KEY" ]; then
# NOTE: MONGO_KEY_FILE is interpolated internally
MONGODB_CMD_ARGS="$MONGODB_CMD_ARGS --keyFile \"\$MONGO_KEY_FILE\""
fi


# Start mongod in replica set mode, with optional auth and keyFile
# MONGO_INITDB_* envs will create the root user on first startup

docker run --name $MONGODB_CONTAINER_NAME \
$NETWORK_ARGS \
--publish $MONGODB_PORT:$MONGODB_PORT \
-e MONGO_INITDB_DATABASE=$MONGODB_DB \
-e MONGO_INITDB_ROOT_USERNAME=$MONGODB_USERNAME \
-e MONGO_INITDB_ROOT_PASSWORD=$MONGODB_PASSWORD \
-e MONGO_KEY=$MONGODB_KEY \
-e MONGO_KEY_FILE=/tmp/mongo-keyfile \
--detach \
--entrypoint bash \
$MONGODB_IMAGE:$MONGODB_VERSION \
-c '\
echo "$MONGO_KEY" > "$MONGO_KEY_FILE" && chmod 400 "$MONGO_KEY_FILE" && chown mongodb:mongodb "$MONGO_KEY_FILE" && \
exec docker-entrypoint.sh mongod '"$MONGODB_CMD_ARGS"' \
'

if [ $? -ne 0 ]; then
echo "Error starting MongoDB Docker container"
exit 2
fi

echo "::endgroup::"

wait_for_mongodb

# After mongod is up, initiate the replica set
# Use auth if credentials were supplied
MONGODB_ARGS=""
if [ -n "$MONGODB_USERNAME" ]; then
MONGODB_ARGS="--username $MONGODB_USERNAME --password $MONGODB_PASSWORD"
fi

echo "::group::Initiating replica set [$MONGODB_REPLICA_SET]"

docker exec --tty $MONGODB_CONTAINER_NAME $MONGODB_CLIENT --port $MONGODB_PORT --eval "
docker exec --tty $MONGODB_CONTAINER_NAME $MONGODB_CLIENT --port $MONGODB_PORT $MONGODB_ARGS --eval "
rs.initiate({
\"_id\": \"$MONGODB_REPLICA_SET\",
\"members\": [ {
\"_id\": 0,
\"host\": \"localhost:$MONGODB_PORT\"
\"host\": \"$MONGODB_REPLICA_SET_HOST:$MONGODB_PORT\"
} ]
})
"
Expand All @@ -140,5 +210,5 @@ echo "::endgroup::"


echo "::group::Checking replica set status [$MONGODB_REPLICA_SET]"
docker exec --tty $MONGODB_CONTAINER_NAME $MONGODB_CLIENT --port $MONGODB_PORT --eval "rs.status()"
docker exec --tty $MONGODB_CONTAINER_NAME $MONGODB_CLIENT --port $MONGODB_PORT $MONGODB_ARGS --eval "rs.status()"
echo "::endgroup::"
32 changes: 32 additions & 0 deletions stop-mongodb.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/bin/sh

# Keep argument positions aligned with action.yml "args" so we can reuse them in post-args
MONGODB_IMAGE=$1
MONGODB_VERSION=$2
MONGODB_REPLICA_SET=$3
MONGODB_PORT=$4
MONGODB_DB=$5
MONGODB_USERNAME=$6
MONGODB_PASSWORD=$7
MONGODB_CONTAINER_NAME=$8
MONGODB_KEY=$9
MONGODB_AUTHSOURCE=${10}
MONGODB_REPLICA_SET_HOST=${11:-"localhost"}
DOCKER_NETWORK=${12}
DOCKER_NETWORK_ALIAS=${13:-$MONGODB_CONTAINER_NAME}

# Best-effort cleanup, do not fail the job if cleanup fails
set +e

echo "::group::Cleaning up MongoDB container [$MONGODB_CONTAINER_NAME]"

if docker ps -a --format '{{.Names}}' | grep -Eq "^${MONGODB_CONTAINER_NAME}$"; then
docker rm -f "$MONGODB_CONTAINER_NAME" >/dev/null 2>&1 || true
echo "Removed container $MONGODB_CONTAINER_NAME"
else
echo "Container $MONGODB_CONTAINER_NAME not found; nothing to clean."
fi

echo "::endgroup::"

exit 0
4 changes: 2 additions & 2 deletions test/single-instance/single-instance.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ const { test } = require('uvu')
const { expect } = require('expect')
const Mongoose = require('mongoose')

const { MONGODB_USERNAME, MONGODB_PASSWORD, MONGODB_DB } = process.env
const { MONGODB_USERNAME, MONGODB_PASSWORD, MONGODB_DB, MONGODB_AUTHSOURCE } = process.env

test('connects to MongoDB', async () => {
const connection = await Mongoose.createConnection('mongodb://localhost', {
user: MONGODB_USERNAME,
pass: MONGODB_PASSWORD,
dbName: MONGODB_DB,
authSource: MONGODB_USERNAME && MONGODB_PASSWORD ? 'admin' : undefined
authSource: MONGODB_USERNAME && MONGODB_PASSWORD ? MONGODB_AUTHSOURCE : undefined
})

await connection.close()
Expand Down