aboutsummaryrefslogtreecommitdiff
path: root/reproduce/software/containers/docker.sh
diff options
context:
space:
mode:
Diffstat (limited to 'reproduce/software/containers/docker.sh')
-rwxr-xr-xreproduce/software/containers/docker.sh486
1 files changed, 0 insertions, 486 deletions
diff --git a/reproduce/software/containers/docker.sh b/reproduce/software/containers/docker.sh
deleted file mode 100755
index d5b5682..0000000
--- a/reproduce/software/containers/docker.sh
+++ /dev/null
@@ -1,486 +0,0 @@
-#!/bin/sh
-#
-# Create a Docker container from an existing image of the built software
-# environment, but with the source, data and build (analysis) directories
-# directly within the host file system. This script is assumed to be run in
-# the top project source directory (that has 'README.md' and
-# 'paper.tex'). If not, use the '--source-dir' option to specify where the
-# Maneage'd project source is located.
-#
-# Usage:
-#
-# - When you are at the top Maneage'd project directory, you can run this
-# script like the example below. Just set all the '/PATH/TO/...'
-# directories (see below for '--tmp-dir'). See the items below for
-# optional values.
-#
-# ./reproduce/software/containers/docker.sh --shm-size=15gb \
-# --software-dir=/PATH/TO/SOFTWARE/TARBALLS \
-# --build-dir=/PATH/TO/BUILD/DIRECTORY
-#
-# - Non-mandatory options:
-#
-# - If you already have the input data that is necessary for your
-# project's, use the '--input-dir' option to specify its location
-# on your host file system. Otherwise the necessary analysis
-# files will be downloaded directly into the build
-# directory. Note that this is only necessary when '--build-only'
-# is not given.
-#
-# - The '--software-dir' is only useful if you want to build a
-# container. Even in that case, it is not mandatory: if not
-# given, the software tarballs will be downloaded (thus requiring
-# internet).
-#
-# - To avoid having to set the directory(s) every time you want to
-# start the docker environment, you can put this command (with the
-# proper directories) into a 'run.sh' script in the top Maneage'd
-# project source directory and simply execute that. The special name
-# 'run.sh' is in Maneage's '.gitignore', so it will not be included
-# in your git history by mistake.
-#
-# Known problems:
-#
-# - As of 2025-04-06 the log file containing the output of the 'docker
-# build' command that configures the Maneage'd project does not keep
-# all the output (which gets clipped by Docker). with a "[output
-# clipped, log limit 2MiB reached]" message. We need to find a way to
-# fix this (so nothing gets clipped: useful for debugging).
-#
-# Copyright (C) 2021-2025 Mohammad Akhlaghi <mohammad@akhlaghi.org>
-#
-# This script is free software: you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the
-# Free Software Foundation, either version 3 of the License, or (at your
-# option) any later version.
-#
-# This script is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-# Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this script. If not, see <http://www.gnu.org/licenses/>.
-
-
-
-
-
-# Script settings
-# ---------------
-# Stop the script if there are any errors.
-set -e
-
-
-
-
-
-# Default option values
-jobs=
-quiet=0
-source_dir=
-build_only=
-image_file=""
-shm_size=20gb
-scriptname="$0"
-project_shell=0
-container_shell=0
-project_name=maneaged
-base_name=maneage-base
-base_os=debian:stable-slim
-
-print_help() {
- # Print the output.
- cat <<EOF
-Usage: $scriptname [OPTIONS]
-
-Top-level script to build and run a Maneage'd project within Docker.
-
- Host OS directories (to be mounted in the container):
- -b, --build-dir=STR Dir. to build in (only analysis in host).
- -i, --input-dir=STR Dir. of input datasets (optional).
- -s, --software-dir=STR Directory of necessary software tarballs.
- --source-dir=STR Directory of source code (default: 'pwd -P').
-
- Docker images
- --base-os=STR Base OS name (default: '$base_os').
- --base-name=STR Base OS docker image (default: $base_name).
- --project-name=STR Project's docker image (default: $project_name).
- --image-file=STR [Docker only] Load (if given file exists), or
- save (if given file does not exist), the image.
- For saving, the given name has to have an
- '.tar.gz' suffix.
-
- Interactive shell
- --project-shell Open the project's shell within the container.
- --container-shell Open the container shell.
-
- Operating mode:
- --quiet Do not print informative statements.
- -?, --help Give this help list.
- --shm-size=STR Passed to 'docker build' (default: $shm_size).
- -j, --jobs=INT Number of threads to use in each phase.
- --build-only Just build the container, don't run it.
-
-Mandatory or optional arguments to long options are also mandatory or
-optional for any corresponding short options.
-
-Maneage URL: https://maneage.org
-
-Report bugs to mohammad@akhlaghi.org
-EOF
-}
-
-on_off_option_error() {
- if [ "x$2" = x ]; then
- echo "$scriptname: '$1' doesn't take any values"
- else
- echo "$scriptname: '$1' (or '$2') doesn't take any values"
- fi
- exit 1
-}
-
-check_v() {
- if [ x"$2" = x ]; then
- printf "$scriptname: option '$1' requires an argument. "
- printf "Try '$scriptname --help' for more information\n"
- exit 1;
- fi
-}
-
-while [ $# -gt 0 ]
-do
- case $1 in
-
- # OS directories
- -b|--build-dir) build_dir="$2"; check_v "$1" "$build_dir"; shift;shift;;
- -b=*|--build-dir=*) build_dir="${1#*=}"; check_v "$1" "$build_dir"; shift;;
- -b*) build_dir=$(echo "$1" | sed -e's/-b//'); check_v "$1" "$build_dir"; shift;;
- -i|--input-dir) input_dir="$2"; check_v "$1" "$input_dir"; shift;shift;;
- -i=*|--input-dir=*) input_dir="${1#*=}"; check_v "$1" "$input_dir"; shift;;
- -i*) input_dir=$(echo "$1" | sed -e's/-i//'); check_v "$1" "$input_dir"; shift;;
- -s|--software-dir) software_dir="$2"; check_v "$1" "$software_dir"; shift;shift;;
- -s=*|--software-dir=*) software_dir="${1#*=}"; check_v "$1" "$software_dir"; shift;;
- -s*) software_dir=$(echo "$1" | sed -e's/-s//'); check_v "$1" "$software_dir"; shift;;
- --source-dir) source_dir="$2"; check_v "$1" "$source_dir"; shift;shift;;
- --source-dir=*) source_dir="${1#*=}"; check_v "$1" "$source_dir"; shift;;
-
- # Container options.
- --base-name) base_name="$2"; check_v "$1" "$base_name"; shift;shift;;
- --base-name=*) base_name="${1#*=}"; check_v "$1" "$base_name"; shift;;
-
- # Interactive shell.
- --project-shell) project_shell=1; shift;;
- --project_shell=*) on_off_option_error --project-shell;;
- --container-shell) container_shell=1; shift;;
- --container_shell=*) on_off_option_error --container-shell;;
-
- # Operating mode
- --quiet) quiet=1; shift;;
- --quiet=*) on_off_option_error --quiet;;
- -j|--jobs) jobs="$2"; check_v "$1" "$jobs"; shift;shift;;
- -j=*|--jobs=*) jobs="${1#*=}"; check_v "$1" "$jobs"; shift;;
- -j*) jobs=$(echo "$1" | sed -e's/-j//'); check_v "$1" "$jobs"; shift;;
- --build-only) build_only=1; shift;;
- --build-only=*) on_off_option_error --build-only;;
- --shm-size) shm_size="$2"; check_v "$1" "$shm_size"; shift;shift;;
- --shm-size=*) shm_size="${1#*=}"; check_v "$1" "$shm_size"; shift;;
- -'?'|--help) print_help; exit 0;;
- -'?'*|--help=*) on_off_option_error --help -?;;
-
- # Output file
- --image-file) image_file="$2"; check_v "$1" "$image_file"; shift;shift;;
- --image-file=*) image_file="${1#*=}"; check_v "$1" "$image_file"; shift;;
-
- # Unrecognized option:
- -*) echo "$scriptname: unknown option '$1'"; exit 1;;
- esac
-done
-
-
-
-
-
-# Sanity checks
-# -------------
-#
-# Make sure that the build directory is given and that it exists.
-if [ x$build_dir = x ]; then
- printf "$scriptname: '--build-dir' not provided, this is the location "
- printf "that all built analysis files will be kept on the host OS\n"
- exit 1;
-else
- if ! [ -d $build_dir ]; then
- printf "$scriptname: '$build_dir' (value to '--build-dir') doesn't "
- printf "exist\n"; exit 1;
- fi
-fi
-
-# The temporary directory to place the Dockerfile.
-tmp_dir="$build_dir"/temporary-docker-container-dir
-
-
-
-
-# Directory preparations
-# ----------------------
-#
-# If the host operating system has '/dev/shm', then give Docker access
-# to it also for improved speed in some scenarios (like configuration).
-if [ -d /dev/shm ]; then shm_mnt="-v /dev/shm:/dev/shm";
-else shm_mnt=""; fi
-
-# If the following directories do not exist within the build directory,
-# create them to make sure the '--mount' commands always work and
-# that any file. Ideally, the 'input' directory should not be under the 'build'
-# directory, but if the user hasn't given it then they don't care about
-# potentially deleting it later (Maneage will download the inputs), so put
-# it in the build directory.
-analysis_dir="$build_dir"/analysis
-if ! [ -d $analysis_dir ]; then mkdir $analysis_dir; fi
-
-# If no '--source-dir' was given, set it to the output of 'pwd -P' (to get
-# the path without potential symbolic links) in the running directory.
-if [ x"$source_dir" = x ]; then source_dir=$(pwd -P); fi
-
-# Only when an an input directory is given, we need the respective 'mount'
-# option for the 'docker run' command.
-input_dir_mnt=""
-if ! [ x"$input_dir" = x ]; then
- input_dir_mnt="-v $input_dir:/home/maneager/input"
-fi
-
-# If no '--jobs' has been specified, use the maximum available jobs to the
-# operating system.
-if [ x$jobs = x ]; then jobs=$(nproc); fi
-
-# [DOCKER-ONLY] Make sure the user is a member of the 'docker' group:
-glist=$(groups $(whoami) | awk '/docker/')
-if [ x"$glist" = x ]; then
- printf "$scriptname: you are not a member of the 'docker' group "
- printf "You can run the following command as root to fix this: "
- printf "'usermod -aG docker $(whoami)'\n"
- exit 1
-fi
-
-# [DOCKER-ONLY] Function to check the temporary directory for building the
-# base operating system docker image. It is necessary that this directory
-# be empty because Docker will inherit the sub-directories of the directory
-# that the Dockerfile is located in.
-tmp_dir_check () {
- if [ -d $tmp_dir ]; then
- printf "$scriptname: '$tmp_dir' already exists, please "
- printf "delete it and re-run this script. This is a temporary "
- printf "directory only necessary when building a Docker image "
- printf "and gets deleted automatically after a successful "
- printf "build. The fact that it remains hints at a problem "
- printf "in a previous attempt to build a Docker image\n"
- exit 1
- else
- mkdir $tmp_dir
- fi
-}
-
-
-
-
-
-# Base operating system
-# ---------------------
-#
-# If the base image does not exist, then create it. If it does, inform the
-# user that it will be used.
-if docker image list | grep $base_name &> /dev/null; then
- if [ $quiet = 0 ]; then
- printf "$scriptname: info: base OS docker image ('$base_name') "
- printf "already exists and will be used. If you want to build a "
- printf "new base OS image, give a new name to '--base-name'. "
- printf "To remove this message run with '--quiet'\n"
- fi
-else
-
- # In case an image file is given, load the environment from that (no
- # need to build the environment from scratch).
- if ! [ x"$image_file" = x ] && [ -f "$image_file" ]; then
- docker load --input $image_file
- else
-
- # Build the temporary directory.
- tmp_dir_check
-
- # Build the Dockerfile.
- uid=$(id -u)
- cat <<EOF > $tmp_dir/Dockerfile
-FROM $base_os
-RUN useradd -ms /bin/sh --uid $uid maneager; \\
- printf '123\n123' | passwd maneager; \\
- printf '456\n456' | passwd root
-RUN apt update; apt install -y gcc g++ wget; echo 'export PS1="[\[\033[01;31m\]\u@\h \W\[\033[32m\]\[\033[00m\]]# "' >> ~/.bashrc
-USER maneager
-WORKDIR /home/maneager
-RUN mkdir build; mkdir build/analysis; echo 'export PS1="[\[\033[01;35m\]\u@\h \W\[\033[32m\]\[\033[00m\]]$ "' >> ~/.bashrc
-EOF
-
- # Build the base-OS container and delete the temporary directory.
- curdir="$(pwd)"
- cd $tmp_dir
- docker build ./ \
- -t $base_name \
- --shm-size=$shm_size
- cd "$curdir"
- rm -rf $tmp_dir
- fi
-fi
-
-
-
-
-
-# Maneage software configuration
-# ------------------------------
-#
-# Having the base operating system in place, we can now construct the
-# project's docker file.
-if docker image list | grep $project_name &> /dev/null; then
- if [ $quiet = 0 ]; then
- printf "$scriptname: info: project's image ('$project_name') "
- printf "already exists and will be used. If you want to build a "
- printf "new project image, give a new name to '--project-name'. "
- printf "To remove this message run with '--quiet'\n"
- fi
-else
-
- # Build the temporary directory.
- tmp_dir_check
- df=$tmp_dir/Dockerfile
-
- # The only way to mount a directory inside the Docker build environment
- # is the 'RUN --mount' command. But Docker doesn't recognize things
- # like symbolic links. So we need to copy the project's source under
- # this temporary directory.
- sdir=source
- mkdir $tmp_dir/$sdir
- dsr=/home/maneager/source-raw
- cp -r $source_dir/* $source_dir/.git $tmp_dir/$sdir
-
- # Start constructing the Dockerfile.
- #
- # Note on the printf's '\x5C\n' part: this will print out as a
- # backslash at the end of the line to allow easy human readability of
- # the Dockerfile (necessary for debugging!).
- echo "FROM $base_name" > $df
- printf "RUN --mount=type=bind,source=$sdir,target=$dsr \x5C\n" >> $df
-
- # If a software directory was given, copy it and add its line.
- tsdir=tarballs-software
- dts=/home/maneager/tarballs-software
- if ! [ x"$software_dir" = x ]; then
-
- # Make the directory to host the software and copy the contents
- # that the user gave there.
- mkdir $tmp_dir/$tsdir
- cp -r "$software_dir"/* $tmp_dir/$tsdir/
- printf " --mount=type=bind,source=$tsdir,target=$dts \x5C\n" >> $df
- fi
-
- # Construct the rest of the 'RUN' command.
- printf " cp -r $dsr /home/maneager/source; \x5C\n" >> $df
- printf " cd /home/maneager/source; \x5C\n" >> $df
- printf " ./project configure --jobs=$jobs \x5C\n" >> $df
- printf " --build-dir=/home/maneager/build \x5C\n" >> $df
- printf " --input-dir=/home/maneager/input \x5C\n" >> $df
- printf " --software-dir=$dts; \x5C\n" >> $df
-
- # We are deleting the '.build/software/tarballs' directory because this
- # directory is not relevant for the analysis of the project. But in
- # case any tarball was downloaded, it will consume space within the
- # container.
- printf " rm -rf .build/software/tarballs; \x5C\n" >> $df
-
- # We are deleting the source directory becaues later (at 'docker run'
- # time), the 'source' will be mounted directly from the host operating
- # system.
- printf " cd /home/maneager; \x5C\n" >> $df
- printf " rm -rf source\n" >> $df
-
- # Build the Maneage container and delete the temporary directory. The
- # '--progress plain' option is for Docker to print all the outputs
- # (otherwise, it will only print a very small part!).
- cd $tmp_dir
- docker build ./ -t $project_name \
- --progress=plain \
- --shm-size=$shm_size \
- --no-cache \
- 2>&1 | tee build.log
- cd ..
- rm -rf $tmp_dir
-fi
-
-# If the user wants to save the container (into a file that does not
-# exist), do it here. If the file exists, it will only be used for creating
-# the container in the previous stages.
-if ! [ x"$image_file" = x ] && ! [ -f "$image_file" ]; then
-
- # Save the image into a tarball
- tarname=$(echo $image_file | sed -e's|.gz$||')
- if [ $quiet = 0 ]; then
- printf "$scriptname: info: saving docker image to '$tarname'"
- fi
- docker save -o $tarname $project_name
-
- # Compress the saved image
- if [ $quiet = 0 ]; then
- printf "$scriptname: info: compressing to '$image_file' (can "
- printf "take +10 minutes, but volume decreases by more than half!)"
- fi
- gzip --best $tarname
-fi
-
-# If the user just wanted to build the base operating system, abort the
-# script here.
-if ! [ x"$build_only" = x ]; then
- if [ $quiet = 0 ]; then
- printf "$scriptname: info: Maneaged project has been configured "
- printf "successfully in the '$project_name' image"
- fi
- exit 0
-fi
-
-
-
-
-
-# Run the analysis within the Maneage'd container
-# -----------------------------------------------
-#
-# The startup command of the container is managed though the 'shellopt'
-# variable that starts here.
-shellopt=""
-if [ $container_shell = 1 ] || [ $project_shell = 1 ]; then
-
- # If the user wants to start the project shell within the container,
- # add the necessary command.
- if [ $project_shell = 1 ]; then
- shellopt="/bin/bash -c 'cd source; ./project shell;'"
- fi
-
- # Finish the 'shellop' string with a single quote (necessary in any
- # case) and run Docker.
- interactiveopt="-it"
-
-# No interactive shell requested, just run the project.
-else
- interactiveopt=""
- shellopt="/bin/bash -c 'cd source; ./project make --jobs=$jobs;'"
-fi
-
-# Execute Docker. The 'eval' is because the 'shellopt' variable contains a
-# single-quote that the shell should "evaluate".
-eval docker run \
- -v "$analysis_dir":/home/maneager/build/analysis \
- -v "$source_dir":/home/maneager/source \
- $input_dir_mnt \
- $shm_mnt \
- $interactiveopt \
- $project_name \
- $shellopt