1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
|
#! /bin/bash
# Purpose: install Debian packages in a Docker image
#
# Usage: deb2docker [options] packages
# -B do NOT build the image (default is to build)
# -c CMD command to run in the container (default to '/bin/bash')
# -f FROM indicate which distribution is to be used (default to debian:stable)
# -h show this help
# -n NAME name of the image (default to package list)
# -o DIR use given directory for the build (default in /tmp)
# -p PRE execute the given script during the container build (before packages install)
# -s POST execute the given script during the container build (after packages install)
# -v show package version
# The package list can be any Debian package, as well as local .deb
#
# Example: 'deb2docker -o /tmp/xeyes x11-apps' then '/tmp/xeyes/start xeyes'
# build: docker build --rm Dockerfile
# run: docker run --rm -it NAME
# clean: docker rmi NAME
# clean ALL: docker system prune -a
# (c) E. Farhi - Synchrotron SOLEIL - GPL3
# requires:
# bash
# docker.io
# requires: docker privileges:
# sudo usermod -aG docker $USER
set -e
# default settings -------------------------------------------------------------
FROM=debian:stable
BUILD=1
NAME=
SETNAME=0
CMD="/bin/bash"
WORK_DIR=
SCRIPT=
PRE=
VERSION=1.0.8
ARGS="$@"
# handle input arguments -------------------------------------------------------
while getopts vhBf:n:c:o:d:p:s: flag
do
case "${flag}" in
h) # display help
echo "Usage: $0 [-hB][-c CMD][-f FROM][-n NAME][-o DIR][-s SCRIPT] packages..."
echo " Build a Docker image with given Debian packages."
echo " Options:"
echo " -B do NOT build the image (default is to build)"
echo " -c EXEC command to run in the container (default to $CMD)"
echo " -f FROM indicate which distribution is to be used (default to $FROM)"
echo " -h show this help"
echo " -n NAME name of the image (default to package list)"
echo " -o DIR use given directory for the build (default is in /tmp)"
echo " -p PRE execute script PRE before packages install"
echo " -s POST execute script POST after packages install"
echo " -v show package version"
echo " The package list can be any Debian package, as well as local .deb"
echo " "
echo "Example: '$0 -o /tmp/xeyes x11-apps' then '/tmp/xeyes/start xeyes'"
exit 0;
;;
B) # do not build the image
BUILD=0
;;
f) # set FROM image
FROM=${OPTARG}
;;
n) # set image name
NAME=${OPTARG}
;;
c) # command to execute
CMD=${OPTARG}
;;
o|d) # output directory
WORK_DIR=${OPTARG}
;;
p) # PRE SCRIPT
PRE=${OPTARG}
;;
s) # SCRIPT (POST)
SCRIPT=${OPTARG}
;;
v) # VERSION
echo "$0 version $VERSION"
exit 0;
;;
*)
echo "ERROR: Invalid option. Use -h for help."
exit 1;
;;
esac
done
shift $((OPTIND-1))
# check that docker is installed
if ! command -v docker > /dev/null
then
echo "ERROR: docker could not be found. Install it with: apt install docker.io"
exit 1
fi
# set name when not set
if [ "x$NAME" = "x" ]; then
SETNAME=1
fi
# create a temporary directory to work in --------------------------------------
if [ "x$WORK_DIR" = "x" ]; then
N=`basename $0`
WORK_DIR=`mktemp -p /tmp -d $N-XXXXXXXX`
else
mkdir -p $WORK_DIR || echo "ERROR: Invalid directory $WORK_DIR"
fi
PW=$PWD
# search for executable commands -----------------
DEB=
# get a local copy of packages and find bin and desktop files
mkdir -p $WORK_DIR/apt || exit # hold deb pkg copies for analysis
echo "$0: creating image $NAME in $WORK_DIR"
echo "Getting Debian packages..."
for i in $@; do
echo " $i"
if [ -f "$i" ]; then
cp $i $WORK_DIR/apt/
n=`basename $i`
DEB="$DEB /opt/install/$n"
else
DEB="$DEB $i"
cd $WORK_DIR/apt
apt download $i
cd $PW
fi
done
echo " " >> $WORK_DIR/README
echo "Created with $0" >> $WORK_DIR/README
echo "$ARGS" >> $WORK_DIR/README
echo " " >> $WORK_DIR/file_list.txt
for i in $WORK_DIR/apt/*.deb; do
echo "Analyzing $i"
N=$(dpkg-deb -f $i Package) || continue
# set the container name if needed
if [ "x$SETNAME" = "x2" ]; then
NAME="$NAME-$N"
fi
if [ "x$SETNAME" = "x1" ]; then
SETNAME=2
NAME=$N
fi
echo "Package $N ------------------------------------------" >> $WORK_DIR/README
dpkg-deb -I $i >> $WORK_DIR/README
F=`dpkg -c $i`
echo "$F" >> $WORK_DIR/file_list.txt
echo " " >> $WORK_DIR/README
done
# the base command to start the containers from image
cmd="docker run -it --net=host --env=DISPLAY --env='QT_X11_NO_MITSHM=1' --volume=\$HOME/.Xauthority:/home/user/.Xauthority:rw $NAME"
# prepare the Dockerfile -------------------------------------------------------
FILE="$WORK_DIR/Dockerfile"
DATE=`date`
if [ -f "$PRE" ]; then
cp $PRE $WORK_DIR/
N=`basename $PRE`
PRE="RUN chmod a+x /opt/install/$N && sh -c \"/opt/install/$N\""
PRE_FILE="ADD $N /opt/install/"
else
PRE_FILE=
fi
if [ -f "$SCRIPT" ]; then
cp $SCRIPT $WORK_DIR/
N=`basename $SCRIPT`
SCRIPT="RUN chmod a+x /opt/install/$N && sh -c \"/opt/install/$N\""
SCRIPT_FILE="ADD $N /opt/install/"
else
SCRIPT_FILE=
fi
echo "Creating Dockerfile $NAME into $FILE"
dd status=none of=${FILE} << EOF
# created by $0 on $DATE
# $ARGS
#
# Docker image $NAME
#
# build: docker build --rm Dockerfile
# run: docker run --rm -it $NAME
# clean: docker rmi $NAME
# clean ALL: docker system prune -a
FROM $FROM
# copy required packages
ADD apt/ /opt/install/
ADD README /opt/install/
ADD file_list.txt /opt/install/
ADD Dockerfile /opt/install/
$PRE_FILE
$SCRIPT_FILE
# execute/install
$PRE
RUN apt-get update -y
RUN apt-get install -y --no-install-recommends bash xauth $DEB
$SCRIPT
RUN rm /opt/install/*.deb || echo " "
# start the container (interactive/terminal)
RUN useradd -ms /bin/bash user
ENV DISPLAY :0
USER user
CMD ["$CMD"]
EOF
# build docker -----------------------------------------------------------------
FILE=$WORK_DIR/build
dd status=none of=${FILE} << EOF
#!/bin/bash
# created by $0 on $DATE
# $ARGS
#
# build image $NAME
#
# Usage: build
docker build --rm -t $NAME .
# handle of Desktop launchers
mkdir -p launchers/
mkdir -p icons/
# get .desktop files ----------------------------------------------------------
D=\$(grep '\.desktop' file_list.txt) || echo "WARNING: No desktop files found."
# get the desktop files
D=\$(echo "\$D" | awk '{ print \$6 }')
# we need to copy them back, as well as their icons, and change the Exec lines
# create a container from image to access the files
id=\$(docker create $NAME)
for i in \$D; do
docker cp \$id:\$i launchers/ || echo "WARNING: Failed to get desktop launcher \$i"
done
# get icon files --------------------------------------------------------------
D=\$(grep 'icon' file_list.txt) || echo "WARNING: No icon files found."
# get the icon files
D=\$(echo "\$D" | awk '{ print \$6 }')
# we need to copy them back, as well as their icons, and change the Exec lines
# create a container from image to access the files
id=\$(docker create $NAME)
for i in \$D; do
docker cp \$id:\$i icons/ &> /dev/null|| echo "WARNING: Failed to get icon \$i"
done
# cleanup
docker rm -v \$id
# adapt the Desktop launchers to insert 'run', set Icons=
for i in launchers/*; do
if [ ! -f "\$i" ]; then continue; fi
I=\$(grep 'Icon=' \$i | cut -d = -f 2) || I=
if [ ! -z "\$I" ]; then
n=\`basename \$I\`
if [ ! -f "icons/\$n" ]; then
# get closest file that match Icon name when initial name is not present as a file
n1=( icons/\$n* ) || n1=
if [ ! -z "\$n1" ]; then
n=\`basename \${n1[0]}\`
fi
fi
sed -i "s|Icon=.*|Icon=icons/\$n|g" \$i || echo " "
fi
I=\$(grep 'Exec=' \$i | cut -d = -f 2-) || I=
sed -i "s|Exec=.*|Exec=sh -c \"echo '$NAME'; $cmd \$I\"|g" \$i || echo " "
# make sure terminal is set (else 'docker -it' fails)
I=\$(grep 'Terminal=' \$i | cut -d = -f 2) || I=
if [ ! -z "\$I" ]; then
sed -i 's|Terminal=.*|Terminal=true|g' \$i || echo " "
else
echo "Terminal=true" >> \$i
fi
chmod a+x \$i || echo " "
done
# create a Terminal launcher
echo "[Desktop Entry]" > launchers/$NAME-terminal.desktop
echo "Type=Application" >> launchers/$NAME-terminal.desktop
echo "Name=$NAME Terminal" >> launchers/$NAME-terminal.desktop
echo "Terminal=true" >> launchers/$NAME-terminal.desktop
echo "Exec=sh -c \"echo '$NAME'; $cmd\"" >> launchers/$NAME-terminal.desktop
chmod a+x launchers/$NAME-terminal.desktop
EOF
chmod a+x $WORK_DIR/build
if [ "x$BUILD" = "x1" ]; then
# build the image
(cd $WORK_DIR && ./build)
else
echo "INFO: To build this image, use: cd $WORK_DIR; ./build"
echo " "
cat $WORK_DIR/build
fi
# ------------------------------------------------------------------------------
# get executables and Desktop launchers (from the Docker)
echo "------------------------------------------------------" >> $WORK_DIR/README
B=$(grep '\.desktop' $WORK_DIR/file_list.txt) || echo " "
echo "$B" >> $WORK_DIR/README
FILE=$WORK_DIR/start
dd status=none of=${FILE} << EOF
#!/bin/bash
# created by $0 on $DATE
#
# start a container from image $NAME
#
# Usage: start [CMD]
# default CMD is $CMD
$cmd \$@
EOF
chmod a+x $WORK_DIR/start
# display final message
echo "--------------------------------------------"
echo "The image $NAME has been prepared in $WORK_DIR"
echo " Desktop launchers are available in $WORK_DIR/launchers"
echo "To start $NAME, use:"
echo " cd $WORK_DIR; ./start [cmd]"
echo " "
|