Skip to content
Merged
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
251 changes: 179 additions & 72 deletions container-creation/create-container.sh
Original file line number Diff line number Diff line change
@@ -1,106 +1,213 @@
#!/bin/bash
# Script to create the pct container, run register container, and migrate container accordingly.
# Last Modified by June 30th, 2025 by Maxwell Klema
# Last Modified by August 5th, 2025 by Maxwell Klema
# -----------------------------------------------------

BOLD='\033[1m'
BLUE='\033[34m'
MAGENTA='\033[35m'
GREEN='\033[32m'
RESET='\033[0m'

# Run cleanup commands in case script is interrupted

cleanup()
{

echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
echo "⚠️ Script was abruptly exited. Running cleanup tasks."
echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
pct unlock $CTID_TEMPLATE
for file in \
"/var/lib/vz/snippets/container-public-keys/$PUB_FILE" \
"/var/lib/vz/snippets/container-port-maps/$PROTOCOL_FILE" \
"/var/lib/vz/snippets/container-env-vars/$ENV_BASE_FOLDER" \
"/var/lib/vz/snippets/container-services/$SERVICES_BASE_FILE"
do
[ -f "$file" ] && rm -rf "$file"
done
exit 1
}

# Echo Container Details
echoContainerDetails() {
echo -e "📦 ${BLUE}Container ID :${RESET} $CONTAINER_ID"
echo -e "🌐 ${MAGENTA}Internal IP :${RESET} $CONTAINER_IP"
echo -e "🔗 ${GREEN}Domain Name :${RESET} https://$CONTAINER_NAME.opensource.mieweb.org"
echo -e "🛠️ ${BLUE}SSH Access :${RESET} ssh -p $SSH_PORT $PROXMOX_USERNAME@$CONTAINER_NAME.opensource.mieweb.org"
echo -e "🔑 ${BLUE}Container Password :${RESET} Your proxmox account password"
echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
echo -e "${BOLD}${MAGENTA}NOTE: Additional background scripts are being ran in detached terminal sessions.${RESET}"
echo -e "${BOLD}${MAGENTA}Wait up to two minutes for all processes to complete.${RESET}"
echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
echo -e "${BOLD}${BLUE}Still not working? Contact Max K. at maxklema@gmail.com${RESET}"
echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"

}

trap cleanup SIGINT SIGTERM SIGHUP

CONTAINER_NAME="${CONTAINER_NAME,,}"

CONTAINER_NAME="$1"
CONTAINER_PASSWORD="$2"
GH_ACTION="$2"
HTTP_PORT="$3"
PROXMOX_USERNAME="$4"
USERNAME_ONLY="${PROXMOX_USERNAME%@*}"
PUB_FILE="$5"
PROTOCOL_FILE="$6"
NEXT_ID=$(pvesh get /cluster/nextid) #Get the next available LXC ID

# Run cleanup commands in case script is interrupted

function cleanup()
{
BOLD='\033[1m'
RESET='\033[0m'

echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
echo "⚠️ Script was abruptly exited. Running cleanup tasks."
echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
pct unlock 114
if [ -f "/var/lib/vz/snippets/container-public-keys/$PUB_FILE" ]; then
rm -rf /var/lib/vz/snippets/container-public-keys/$PUB_FILE
fi
if [ -f "/var/lib/vz/snippets/container-port-maps/$PROTOCOL_FILE" ]; then
rm -rf /var/lib/vz/snippets/container-port-maps/$PROTOCOL_FILE
fi
exit 1
}
# Deployment ENVS
DEPLOY_ON_START="$7"
PROJECT_REPOSITORY="$8"
PROJECT_BRANCH="$9"
PROJECT_ROOT="${10}"
INSTALL_COMMAND=$(echo "${11}" | base64 -d)
BUILD_COMMAND=$(echo "${12}" | base64 -d)
START_COMMAND=$(echo "${13}" | base64 -d)
RUNTIME_LANGUAGE=$(echo "${14}" | base64 -d)
ENV_BASE_FOLDER="${15}"
SERVICES_BASE_FILE="${16}"
LINUX_DISTRO="${17}"
MULTI_COMPONENTS="${18}"
ROOT_START_COMMAND="${19}"

# Pick the correct template to clone =====

REPO_BASE_NAME=$(basename -s .git "$PROJECT_REPOSITORY")
REPO_BASE_NAME_WITH_OWNER=$(echo "$PROJECT_REPOSITORY" | cut -d'/' -f4)

TEMPLATE_NAME="template-$REPO_BASE_NAME-$REPO_BASE_NAME_WITH_OWNER"
CTID_TEMPLATE=$( { pct list; ssh root@10.15.0.5 'pct list'; } | awk -v name="$TEMPLATE_NAME" '$3 == name {print $1}')

case "${LINUX_DISTRO^^}" in
DEBIAN) PACKAGE_MANAGER="apt-get" ;;
ROCKY) PACKAGE_MANAGER="dnf" ;;
esac

# If no template ID was provided, assign a default based on distro

if [ -z "$CTID_TEMPLATE" ]; then
case "${LINUX_DISTRO^^}" in
DEBIAN) CTID_TEMPLATE="160" ;;
ROCKY) CTID_TEMPLATE="138" ;;
esac
fi

# Create the Container Clone ====

# Create the Container Clone
if [ "${GH_ACTION^^}" != "Y" ]; then
CONTAINER_ID=$(pvesh get /cluster/nextid) #Get the next available LXC ID

echo "⏳ Cloning Container..."
pct clone 114 $NEXT_ID \
--hostname $CONTAINER_NAME \
--full true \
echo "⏳ Cloning Container..."
pct clone $CTID_TEMPLATE $CONTAINER_ID \
--hostname $CONTAINER_NAME \
--full true > /dev/null 2>&1

# Set Container Options
# Set Container Options

echo "⏳ Setting Container Properties.."
pct set $NEXT_ID \
--tags "$PROXMOX_USERNAME" \
--onboot 1 \
echo "⏳ Setting Container Properties..."
pct set $CONTAINER_ID \
--tags "$PROXMOX_USERNAME" \
--tags "$LINUX_DISTRO" \
--tags "LDAP" \
--onboot 1 > /dev/null 2>&1

pct start $NEXT_ID
pveum aclmod /vms/$NEXT_ID --user "$PROXMOX_USERNAME@pve" --role PVEVMUser
#pct delete $NEXT_ID
pct start $CONTAINER_ID > /dev/null 2>&1
pveum aclmod /vms/$CONTAINER_ID --user "$PROXMOX_USERNAME@pve" --role PVEVMUser > /dev/null 2>&1

# Get the Container IP Address and install some packages
# Get the Container IP Address and install some packages

echo "⏳ Waiting for DHCP to allocate IP address to container..."
sleep 10
echo "⏳ Waiting for DHCP to allocate IP address to container..."
sleep 5
else
CONTAINER_ID=$( { pct list; ssh root@10.15.0.5 'pct list'; } | awk -v name="$CONTAINER_NAME" '$3 == name {print $1}')
fi

CONTAINER_IP=$(pct exec $NEXT_ID -- hostname -I | awk '{print $1}')
pct exec $NEXT_ID -- apt-get upgrade
pct exec $NEXT_ID -- apt install -y sudo
pct exec $NEXT_ID -- apt install -y git
if [ -f "/var/lib/vz/snippets/container-public-keys/$PUB_FILE" ]; then
pct exec $NEXT_ID -- touch ~/.ssh/authorized_keys
pct exec $NEXT_ID -- bash -c "cat > ~/.ssh/authorized_keys"< /var/lib/vz/snippets/container-public-keys/$PUB_FILE
rm -rf /var/lib/vz/snippets/container-public-keys/$PUB_FILE
echo "⏳ Appending Public Key..."
pct exec $CONTAINER_ID -- touch ~/.ssh/authorized_keys > /dev/null 2>&1
pct exec $CONTAINER_ID -- bash -c "cat > ~/.ssh/authorized_keys"< /var/lib/vz/snippets/container-public-keys/$PUB_FILE > /dev/null 2>&1
rm -rf /var/lib/vz/snippets/container-public-keys/$PUB_FILE > /dev/null 2>&1
fi

# Set password inside the container
CONTAINER_IP=""
attempts=0
max_attempts=10

pct exec $NEXT_ID -- bash -c "echo 'root:$CONTAINER_PASSWORD' | chpasswd"
while [[ -z "$CONTAINER_IP" && $attempts -lt $max_attempts ]]; do
CONTAINER_IP=$(pct exec "$CONTAINER_ID" -- hostname -I | awk '{print $1}')
[[ -z "$CONTAINER_IP" ]] && sleep 2 && ((attempts++))
done

# Run Contianer Provision Script to add container to port_map.json
if [[ -z "$CONTAINER_IP" ]]; then
echo "❌ Timed out waiting for container to get an IP address."
exit 1
fi

if [ -f "/var/lib/vz/snippets/container-port-maps/$PROTOCOL_FILE" ]; then
echo "CONTAINS PROTOCOL FILE"
/var/lib/vz/snippets/register-container-test.sh $NEXT_ID $HTTP_PORT /var/lib/vz/snippets/container-port-maps/$PROTOCOL_FILE
rm -rf /var/lib/vz/snippets/container-port-maps/$PROTOCOL_FILE
else
/var/lib/vz/snippets/register-container-test.sh $NEXT_ID $HTTP_PORT
# Set up SSSD to communicate with LDAP server ====
echo "⏳ Configuring LDAP connection via SSSD..."
source /var/lib/vz/snippets/helper-scripts/configureLDAP.sh

# Attempt to Automatically Deploy Project Inside Container

if [ "${DEPLOY_ON_START^^}" == "Y" ]; then
source /var/lib/vz/snippets/helper-scripts/deployOnStart.sh

#cleanup
for file in \
"/var/lib/vz/snippets/container-env-vars/$ENV_BASE_FOLDER" \
"/var/lib/vz/snippets/container-services/$SERVICES_BASE_FILE"
do
[ -f "$file" ] && rm -rf "$file" > /dev/null 2>&1
done
fi

SSH_PORT=$(iptables -t nat -S PREROUTING | grep "to-destination $CONTAINER_IP:22" | awk -F'--dport ' '{print $2}' | awk '{print $1}' | head -n 1 || true)
# Create Log File ====

# Migrate to pve2 if Container ID is even
pct exec $CONTAINER_ID -- bash -c "cd /root && touch container-updates.log"

if (( $NEXT_ID % 2 == 0 )); then
pct stop $NEXT_ID
pct migrate $NEXT_ID intern-phxdc-pve2 --target-storage containers-pve2 --online
ssh root@10.15.0.5 "pct start $NEXT_ID"
# Run Contianer Provision Script to add container to port_map.json
echo "⏳ Running Container Provision Script..."
if [ -f "/var/lib/vz/snippets/container-port-maps/$PROTOCOL_FILE" ]; then
/var/lib/vz/snippets/register-container.sh $CONTAINER_ID $HTTP_PORT /var/lib/vz/snippets/container-port-maps/$PROTOCOL_FILE "$USERNAME_ONLY"
rm -rf /var/lib/vz/snippets/container-port-maps/$PROTOCOL_FILE > /dev/null 2>&1
else
/var/lib/vz/snippets/register-container.sh $CONTAINER_ID $HTTP_PORT "" "$PROXMOX_USERNAME"
fi

# Echo Container Details

# Define friendly, high-contrast colors
BOLD='\033[1m'
BLUE='\033[34m'
MAGENTA='\033[35m'
GREEN='\033[32m'
RESET='\033[0m'
SSH_PORT=$(iptables -t nat -S PREROUTING | grep "to-destination $CONTAINER_IP:22" | awk -F'--dport ' '{print $2}' | awk '{print $1}' | head -n 1 || true)

echo -e "📦 ${BLUE}Container ID :${RESET} $NEXT_ID"
echo -e "🌐 ${MAGENTA}Internal IP :${RESET} $CONTAINER_IP"
echo -e "🔗 ${GREEN}Domain Name :${RESET} https://$CONTAINER_NAME.opensource.mieweb.org"
echo -e "🛠️ ${BLUE}SSH Access :${RESET} ssh -p $SSH_PORT root@$CONTAINER_NAME.opensource.mieweb.org"
echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
# Output container details and start services if necessary =====

echoContainerDetails

BUILD_COMMAND_B64=$(echo -n "$BUILD_COMMAND" | base64)
RUNTIME_LANGUAGE_B64=$(echo -n "$RUNTIME_LANGUAGE" | base64)
START_COMMAND_B64=$(echo -n "$START_COMMAND" | base64)

CMD=(
bash /var/lib/vz/snippets/start_services.sh
"$CONTAINER_ID"
"$CONTAINER_NAME"
"$REPO_BASE_NAME"
"$REPO_BASE_NAME_WITH_OWNER"
"$SSH_PORT"
"$CONTAINER_IP"
"$PROJECT_ROOT"
"$ROOT_START_COMMAND"
"$DEPLOY_ON_START"
"$MULTI_COMPONENTS"
"$START_COMMAND_B64"
"$BUILD_COMMAND_B64"
"$RUNTIME_LANGUAGE_B64"
"$GH_ACTION"
"$PROJECT_BRANCH"
)

# Safely quote each argument for the shell
QUOTED_CMD=$(printf ' %q' "${CMD[@]}")

tmux new-session -d -s "$CONTAINER_NAME" "$QUOTED_CMD"
exit 0
14 changes: 14 additions & 0 deletions create-a-container/container-creator.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[Unit]
Description=Container Creator Node.js App
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/opt/container-creator
ExecStart=/usr/bin/node /opt/container-creator/server.js
Restart=on-failure
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target
Loading