--- /dev/null
+#!/bin/sh
+
+
+# Container Creation and Configuration
+
+
+USAGE="Usage: $(basename $0) <container name>"
+
+if [ $# -ne 1 ]
+then
+ echo "$USAGE" >&2
+ exit 1
+fi
+
+if [ ! -f /etc/lxc/lxc.conf ]
+then
+ echo "No lxc system configuration found" >&2
+ exit 1
+fi
+
+backup_old_container()
+{
+ if [ -z "$OLD_CONTAINER_NAME" ]
+ then
+ return 0
+ fi
+
+ echo "Backing up (the old) container."
+
+ if [ -d $OLD_CONTAINER_BACKUP_PATH ]
+ then
+ rm -Rf ${OLD_CONTAINER_BACKUP_PATH}.old
+ mv $OLD_CONTAINER_BACKUP_PATH ${OLD_CONTAINER_BACKUP_PATH}.old
+ fi
+
+ OLD_CONTAINER_STATE=$(lxc-info --name=$OLD_CONTAINER_NAME --state 2>/dev/null \
+ | cut -f 2 -d ':' | tr -d ' ')
+ case "$OLD_CONTAINER_STATE" in
+ "RUNNING")
+ echo "Stopping (the old) container $OLD_CONTAINER_NAME"
+ lxc-stop --name=$OLD_CONTAINER_NAME
+ echo "Stopped (the old) container $OLD_CONTAINER_NAME"
+ ;;
+ "STOPPED")
+ ;;
+ *)
+ echo "Unknown container state: $OLD_CONTAINER_STATE" >&2
+ exit 1
+ ;;
+ esac
+
+ mv $OLD_CONTAINER_PATH $OLD_CONTAINER_BACKUP_PATH
+
+ echo "(The old) container has been backed up."
+}
+
+copy_and_install()
+{
+ echo "Copying base container files."
+
+ for FQ_DIRECTORY in $BASE_CONTAINER_PATH/rootfs/*
+ do
+ DIRECTORY=$(basename $FQ_DIRECTORY)
+ case "$DIRECTORY" in
+ "usr")
+ # common for all containers
+ cp --archive --link $FQ_DIRECTORY $CONTAINER_BUILDROOT
+ ;;
+ "dev" | "proc")
+ # already mounted, skip
+ ;;
+ *)
+ # each container must have its own version
+ cp --archive $FQ_DIRECTORY $CONTAINER_BUILDROOT
+ ;;
+ esac
+ done
+
+ echo "Installing special packages."
+
+ case "$DISTRIBUTION" in
+ "CentOS")
+ YUM_CONFIG=$SCRIPT_PATH/yum.conf.centos
+ YUM_RELEASEVER=$DISTRIBUTION_VERSION
+ if [ -n "$SPEC_PACKAGES" ]
+ then
+ yum --assumeyes \
+ --config=$YUM_CONFIG \
+ --disablerepo=* \
+ --enablerepo=local-* \
+ --installroot=$CONTAINER_BUILDROOT \
+ --releasever=$YUM_RELEASEVER \
+ install $SPEC_PACKAGES
+ fi
+ yum --assumeyes \
+ --config=$YUM_CONFIG \
+ --disablerepo=* \
+ --enablerepo=local-* \
+ --installroot=$CONTAINER_BUILDROOT \
+ --releasever=$YUM_RELEASEVER \
+ clean all
+ ;;
+ "Fedora")
+ DNF_CONFIG=$SCRIPT_PATH/dnf.conf.fedora
+ DNF_RELEASEVER=$DISTRIBUTION_VERSION
+ if [ -n "$SPEC_PACKAGES" ]
+ then
+ dnf --assumeyes \
+ --config=$DNF_CONFIG \
+ --disablerepo=* \
+ --enablerepo=local-* \
+ --installroot=$CONTAINER_BUILDROOT \
+ --releasever=$DNF_RELEASEVER \
+ install $SPEC_PACKAGES
+ fi
+ dnf --assumeyes \
+ --config=$DNF_CONFIG \
+ --disablerepo=* \
+ --enablerepo=local-* \
+ --installroot=$CONTAINER_BUILDROOT \
+ --releasever=$DNF_RELEASEVER \
+ clean all
+ ;;
+ "Rocky")
+ YUM_CONFIG=$SCRIPT_PATH/yum.conf.rocky
+ YUM_RELEASEVER=$DISTRIBUTION_VERSION
+ if [ -n "$SPEC_PACKAGES" ]
+ then
+ yum --assumeyes \
+ --config=$YUM_CONFIG \
+ --disablerepo=* \
+ --enablerepo=local-* \
+ --installroot=$CONTAINER_BUILDROOT \
+ --releasever=$YUM_RELEASEVER \
+ install $SPEC_PACKAGES
+ fi
+ yum --assumeyes \
+ --config=$YUM_CONFIG \
+ --disablerepo=* \
+ --enablerepo=local-* \
+ --installroot=$CONTAINER_BUILDROOT \
+ --releasever=$YUM_RELEASEVER \
+ clean all
+ ;;
+ esac
+
+ echo "Copied files and installed packages."
+}
+
+create_build_path()
+{
+ echo "Creating and populating the container build directory."
+
+ mkdir --parents $CONTAINER_BUILDROOT
+
+ sed --expression="s|__CONTAINER_FILESYSTEMS_PATH__|$CONTAINER_FILESYSTEMS_PATH|" \
+ --expression="s|__CONTAINER_PATH__|$CONTAINER_PATH|" \
+ --expression="s|__CONTAINER_START_ORDER__|$CONTAINER_START_ORDER|" \
+ <$CONTAINER_SOURCE_PATH/config \
+ >$CONTAINER_BUILD_PATH/config
+ if [ -d $CONTAINER_SOURCE_PATH/hooks ]
+ then
+ cp --archive $CONTAINER_SOURCE_PATH/hooks $CONTAINER_BUILD_PATH
+ fi
+
+ echo "Created and populated the container build directory."
+}
+
+create_environment_file()
+{
+ ENV_FILE=$(mktemp)
+
+
+ echo "Creating environment file."
+
+ echo "SCRIPT_PATH=$SCRIPT_PATH" >>$ENV_FILE
+ echo "BASE_PATH=$BASE_PATH" >>$ENV_FILE
+ echo "BACKUPS_PATH=$BACKUPS_PATH" >>$ENV_FILE
+ echo "BUILD_PATH=$BUILD_PATH" >>$ENV_FILE
+ echo "FILESYSTEMS_PATH=$FILESYSTEMS_PATH" >>$ENV_FILE
+ echo "SOURCES_PATH=$SOURCES_PATH" >>$ENV_FILE
+ echo "SSH_KEYS_PATH=$SSH_KEYS_PATH" >>$ENV_FILE
+ echo >>$ENV_FILE
+ echo "CONTAINER_BASENAME=$CONTAINER_BASENAME" >>$ENV_FILE
+ echo "CONTAINER_NAME=$CONTAINER_NAME" >>$ENV_FILE
+ echo "CONTAINER_OS=$CONTAINER_OS" >>$ENV_FILE
+ echo "CONTAINER_PATH=$CONTAINER_PATH" >>$ENV_FILE
+ echo "CONTAINER_BUILD_PATH=$CONTAINER_BUILD_PATH" >>$ENV_FILE
+ echo "CONTAINER_FILESYSTEMS_PATH=$CONTAINER_FILESYSTEMS_PATH" >>$ENV_FILE
+ echo "CONTAINER_SOURCE_PATH=$CONTAINER_SOURCE_PATH" >>$ENV_FILE
+ echo "CONTAINER_BUILDROOT=$CONTAINER_BUILDROOT" >>$ENV_FILE
+ echo >>$ENV_FILE
+ echo "OLD_CONTAINER_NAME=$OLD_CONTAINER_NAME" >>$ENV_FILE
+ echo "OLD_CONTAINER_PATH=$OLD_CONTAINER_PATH" >>$ENV_FILE
+ echo "OLD_CONTAINER_BACKUP_PATH=$OLD_CONTAINER_BACKUP_PATH" >>$ENV_FILE
+
+ echo "Environment file created."
+}
+
+populate_c3d()
+{
+ echo "Creating and populating the container creation and configuration directory."
+
+ if [ -d $CONTAINER_SOURCE_PATH/c3d ]
+ then
+ cp --archive $CONTAINER_SOURCE_PATH/c3d $CONTAINER_BUILDROOT
+ find $CONTAINER_BUILDROOT/c3d -type d -print0 \
+ | xargs --null chmod 755
+ find $CONTAINER_BUILDROOT/c3d -type f -print0 \
+ | xargs --null chmod 644
+ chown --recursive root.root $CONTAINER_BUILDROOT/c3d
+ fi
+
+ MODE_LIST=$CONTAINER_BUILDROOT/c3d/mode.txt
+ if [ -f $MODE_LIST ]
+ then
+ grep --invert-match \
+ --regexp='^#' \
+ --regexp='^$' $MODE_LIST | while read LINE
+ do
+ TGT_MODE=$(echo "$LINE" | cut -f 1 -d ' ')
+ TGT_PATH=$(echo "$LINE" | cut -f 2 -d ' ')
+ chmod $TGT_MODE $CONTAINER_BUILDROOT/c3d/$TGT_PATH
+ done
+ fi
+
+ OWNER_LIST=$CONTAINER_BUILDROOT/c3d/owner.txt
+ if [ -f $OWNER_LIST ]
+ then
+ grep --invert-match \
+ --regexp='^#' \
+ --regexp='^$' $OWNER_LIST | while read LINE
+ do
+ TGT_OWNER=$(echo "$LINE" | cut -f 1 -d ' ')
+ TGT_PATH=$(echo "$LINE" | cut -f 2 -d ' ')
+ chroot $CONTAINER_BUILDROOT chown $TGT_OWNER /c3d/$TGT_PATH
+ done
+ fi
+
+ echo "Created and populated the container creation and configuration directory."
+}
+
+set_variables()
+{
+ CONTAINER_NAME_PARAMETER=$1
+
+
+ echo "Setting variables."
+
+ CONTAINER_BASENAME=$(basename $CONTAINER_NAME_PARAMETER)
+
+ LXC_PATH=$(grep '^lxc.lxcpath' /etc/lxc/lxc.conf \
+ | cut -f 2 -d '=' | tr -d ' ')
+
+ SCRIPT_PATH=$(dirname $(realpath $0))
+ BASE_PATH=$(dirname $SCRIPT_PATH)
+ BACKUPS_PATH=$BASE_PATH/backups
+ BUILD_PATH=$BASE_PATH/build
+ FILESYSTEMS_PATH=$BASE_PATH/filesystems
+ SOURCES_PATH=$BASE_PATH/sources
+ SSH_KEYS_PATH=$BASE_PATH/user-ssh
+
+ CONTAINER_FILESYSTEMS_PATH=$FILESYSTEMS_PATH/$CONTAINER_BASENAME
+ CONTAINER_SOURCE_PATH=$SOURCES_PATH/$CONTAINER_BASENAME
+
+ if [ ! -d $CONTAINER_SOURCE_PATH ]
+ then
+ echo "No source found for $CONTAINER_BASENAME" >&2
+ return 1
+ fi
+
+ if [ ! -f $CONTAINER_SOURCE_PATH/config ]
+ then
+ echo "No config file found for $CONTAINER_BASENAME" >&2
+ return 2
+ fi
+
+ if [ ! -f $CONTAINER_SOURCE_PATH/envvars ]
+ then
+ echo "No envvars file found for $CONTAINER_BASENAME" >&2
+ return 3
+ fi
+
+ source $CONTAINER_SOURCE_PATH/envvars
+ RC=$?
+ if [ $RC -ne 0 ]
+ then
+ echo "Could not source $CONTAINER_SOURCE_PATH/envvars, rc: $RC" >&2
+ return 4
+ fi
+
+ if [ -z "$DISTRIBUTION" ]
+ then
+ echo "No DISTRIBUTION variable set in envvars file" >&2
+ return 5
+ fi
+ if [ -z "$DISTRIBUTION_VERSION" ]
+ then
+ echo "No DISTRIBUTION_VERSION variable set in envvars file" >&2
+ return 6
+ fi
+
+ case "$DISTRIBUTION" in
+ "CentOS")
+ YUM_RELEASEVER=$DISTRIBUTION_VERSION
+ ;;
+ "Fedora")
+ DNF_RELEASEVER=$DISTRIBUTION_VERSION
+ ;;
+ "Rocky")
+ YUM_RELEASEVER=$DISTRIBUTION_VERSION
+ ;;
+ *)
+ echo "Unknown distribution: $DISTRIBUTION" >&2
+ return 7
+ ;;
+ esac
+
+ DISTRIBUTION_FIRST_CHAR=$(echo "$DISTRIBUTION" | cut -c 1 | tr '[A-Z]' '[a-z]')
+ CONTAINER_OS="${DISTRIBUTION_FIRST_CHAR}$DISTRIBUTION_VERSION"
+
+ BASE_CONTAINER_PATH=$LXC_PATH/base.$CONTAINER_OS
+
+ if [ ! -d $BASE_CONTAINER_PATH ]
+ then
+ echo "The container does not have a base container (base.${CONTAINER_OS})." >&2
+ return 8
+ fi
+
+ CONTAINER_NAME="${CONTAINER_BASENAME}.$CONTAINER_OS"
+
+ CONTAINER_PATH=$LXC_PATH/$CONTAINER_NAME
+ CONTAINER_BUILD_PATH=$BUILD_PATH/$CONTAINER_NAME
+
+ if [ -d $CONTAINER_BUILD_PATH ]
+ then
+ echo "The build directory already exists." >&2
+ return 9
+ fi
+
+ CONTAINER_BUILDROOT=$CONTAINER_BUILD_PATH/rootfs
+
+ CONTAINER_START_ORDER=$(grep -w $CONTAINER_BASENAME $SOURCES_PATH/start-order.txt \
+ | awk '{print $2}')
+
+ if [ -z "$CONTAINER_START_ORDER" ]
+ then
+ echo "The container does not have a start order." >&2
+ return 10
+ fi
+
+ OLD_CONTAINER_NAME=$(lxc-ls -1 | grep -w "^$CONTAINER_BASENAME")
+ #OLD_CONTAINER_OS=$(echo $OLD_CONTAINER_NAME | sed 's/.*\.\([^\.]*\)$/\1/')
+ OLD_CONTAINER_PATH=$LXC_PATH/$OLD_CONTAINER_NAME
+ OLD_CONTAINER_BACKUP_PATH=$BACKUPS_PATH/$OLD_CONTAINER_NAME
+
+ echo "All variables set."
+}
+
+preinstall()
+{
+ echo "Executing preinstall phase."
+
+ if [ -d $CONTAINER_BUILDROOT/c3d/preinstall/scripts ]
+ then
+ shopt -s nullglob
+ for SCRIPT in $CONTAINER_BUILDROOT/c3d/preinstall/scripts/*.sh
+ do
+ echo $SCRIPT $ENV_FILE
+ $SCRIPT $ENV_FILE
+ done
+ shopt -u nullglob
+ fi
+
+ mkdir $CONTAINER_BUILDROOT/dev
+ mkdir $CONTAINER_BUILDROOT/proc
+ mount -o bind /dev $CONTAINER_BUILDROOT/dev
+ mount -t proc proc $CONTAINER_BUILDROOT/proc
+
+ echo "Finished preinstall phase."
+}
+
+postinstall()
+{
+ echo "Executing postinstall phase."
+
+ POSTINSTALL_PATH=$CONTAINER_BUILDROOT/c3d/postinstall
+ if [ -d $POSTINSTALL_PATH ]
+ then
+ RUN_LIST=$POSTINSTALL_PATH/ssh/run.list
+ if [ -f $RUN_LIST ]
+ then
+ grep -v '^#' $RUN_LIST | while read LINE
+ do
+ TGT_HOST=$(echo "$LINE" | cut -f 1 -d ' ')
+ TGT_USER=$(echo "$LINE" | cut -f 2 -d ' ')
+ TGT_EXECUTABLE=$(echo "$LINE" | cut -f 3 -d ' ')
+ ssh -i $SSH_KEYS_PATH/scripts -l $TGT_USER $TGT_HOST $TGT_EXECUTABLE
+ done
+ fi
+ COPY_PATH=$POSTINSTALL_PATH/scp
+ COPY_LIST=$COPY_PATH/copy.list
+ if [ -f $COPY_LIST ]
+ then
+ grep -v '^#' $COPY_LIST | while read LINE
+ do
+ SRC_HOST=$(echo "$LINE" | cut -f 1 -d ' ')
+ SRC_PATH=$(echo "$LINE" | cut -f 2 -d ' ')
+ scp -i $SSH_KEYS_PATH/scripts -p -r \
+ root@${SRC_HOST}:$SRC_PATH $COPY_PATH
+ done
+ fi
+ if [ -d $POSTINSTALL_PATH/install-data ]
+ then
+ tar --create \
+ --directory=$POSTINSTALL_PATH \
+ --exclude=.placeholder \
+ --to-stdout \
+ install-data \
+ | tar --extract \
+ --backup \
+ --directory=$CONTAINER_BUILDROOT \
+ --no-overwrite-dir \
+ --strip-components=1 \
+ --suffix=.orig
+ fi
+
+ shopt -s nullglob
+ for SCRIPT in $POSTINSTALL_PATH/scripts/*.sh
+ do
+ POSTINSTALL_SCRIPT=$(echo $SCRIPT | sed "s|^$CONTAINER_BUILDROOT||")
+ echo chroot $CONTAINER_BUILDROOT $POSTINSTALL_SCRIPT
+ chroot $CONTAINER_BUILDROOT $POSTINSTALL_SCRIPT
+ done
+ shopt -u nullglob
+ fi
+
+ umount $CONTAINER_BUILDROOT/dev
+ umount $CONTAINER_BUILDROOT/proc
+
+ echo "Finished postinstall phase."
+}
+
+firstboot()
+{
+ echo "Executing firstboot phase."
+
+ CONTAINER_ROOTFS=$CONTAINER_PATH/rootfs
+ FIRSTBOOT_PATH=$CONTAINER_ROOTFS/c3d/firstboot
+
+ if [ -d $FIRSTBOOT_PATH ]
+ then
+ shopt -s nullglob
+ for SCRIPT in $FIRSTBOOT_PATH/scripts/*.sh
+ do
+ FIRSTBOOT_SCRIPT=$(echo $SCRIPT | sed "s|^$CONTAINER_ROOTFS||")
+ echo lxc-attach --name=$CONTAINER_NAME -- $FIRSTBOOT_SCRIPT
+ lxc-attach --name=$CONTAINER_NAME -- $FIRSTBOOT_SCRIPT
+ done
+ shopt -u nullglob
+ fi
+
+ echo "Finished firstboot phase."
+}
+
+set_variables $1
+RC=$?
+if [ $RC -ne 0 ]
+then
+ echo "Could not set variables, rc: $RC" >&2
+ exit $RC
+fi
+
+echo "Starting at $(date)"
+
+create_environment_file
+
+create_build_path
+
+preinstall
+
+copy_and_install
+
+populate_c3d
+
+postinstall
+
+backup_old_container
+
+mv $CONTAINER_BUILD_PATH $CONTAINER_PATH
+
+echo "Starting (the new) container $CONTAINER_NAME"
+lxc-start --name=$CONTAINER_NAME --daemon
+
+echo "Waiting for (the new) container $CONTAINER_NAME to start up."
+lxc-wait --name=$CONTAINER_NAME --state=RUNNING
+
+firstboot
+
+rm --force $ENV_FILE
+
+echo rm --force --recursive $CONTAINER_ROOTFS/c3d
+
+echo "Finishing at $(date)"