Merge pull request #183 from fsquillace/issue_174_namespace

Issue 174 namespace
This commit is contained in:
Filippo Squillace 2017-04-18 12:38:10 +01:00 committed by GitHub
commit 3456857d7a
21 changed files with 1313 additions and 529 deletions

View file

@ -17,9 +17,14 @@ install:
script:
- bash --version
- bash ./tests/unit-tests/unit-tests.sh
- bash ./tests/checkstyle/checkstyle.sh
- junest --check ./bin/junest
- bash ./tests/unit-tests/unit-tests.sh
# Multiple tests against different execution modes:
- junest -f -- ${PWD}/lib/checks/check.sh
- junest -u -- ${PWD}/lib/checks/check.sh --skip-aur-tests
- sudo -E ${PWD}/bin/junest -g -- ${PWD}/lib/checks/check.sh --run-root-tests
- yes | junest --delete
- JUNEST_HOME=~/.junest-arm junest --check ./bin/junest --skip-root-tests
- JUNEST_HOME=~/.junest-arm junest -f -- ./lib/checks/check.sh
- yes | JUNEST_HOME=~/.junest-arm junest --delete

107
README.md
View file

@ -11,13 +11,13 @@ The Arch Linux based distro that runs upon any Linux distros without root access
|Project Status|Donation|Communication|
|:------------:|:------:|:-----------:|
| [![Build status](https://api.travis-ci.org/fsquillace/junest.png?branch=master)](https://travis-ci.org/fsquillace/junest) [![OpenHub](https://www.openhub.net/p/junest/widgets/project_thin_badge.gif)](https://www.openhub.net/p/junest) | [![PayPal](https://img.shields.io/badge/PayPal-Donate%20a%20beer-blue.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=8LEHQKBCYTACY) [![Gratipay](https://img.shields.io/badge/Gratipay-Donate%20to%20JuNest-green.svg)](https://gratipay.com/junest/) | [![Join the gitter chat at https://gitter.im/fsquillace/junest](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/fsquillace/junest?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the IRC chat at https://webchat.freenode.net/?channels=junest](https://img.shields.io/badge/IRC-JuNest-yellow.svg)](https://webchat.freenode.net/?channels=junest) [![Join the group at https://groups.google.com/d/forum/junest](https://img.shields.io/badge/Google Groups-JuNest-red.svg)](https://groups.google.com/d/forum/junest) [![RSS](https://img.shields.io/badge/RSS-News-orange.svg)](http://fsquillace.github.io/junest-site/feed.xml) |
| [![Build status](https://api.travis-ci.org/fsquillace/junest.png?branch=master)](https://travis-ci.org/fsquillace/junest) [![OpenHub](https://www.openhub.net/p/junest/widgets/project_thin_badge.gif)](https://www.openhub.net/p/junest) | [![PayPal](https://img.shields.io/badge/PayPal-Donate%20a%20beer-blue.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=8LEHQKBCYTACY) [![Gratipay](https://img.shields.io/badge/Gratipay-Donate%20to%20JuNest-green.svg)](https://gratipay.com/junest/) | [![Join the gitter chat at https://gitter.im/fsquillace/junest](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/fsquillace/junest?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the IRC chat at https://webchat.freenode.net/?channels=junest](https://img.shields.io/badge/IRC-JuNest-yellow.svg)](https://webchat.freenode.net/?channels=junest) [![Join the group at https://groups.google.com/d/forum/junest](https://img.shields.io/badge/GoogleGroups-JuNest-red.svg)](https://groups.google.com/d/forum/junest) [![RSS](https://img.shields.io/badge/RSS-News-orange.svg)](http://fsquillace.github.io/junest-site/feed.xml) |
**Table of Contents**
- [Description](#description)
- [Quickstart](#quickstart)
- [Installation](#installation)
- [Dependencies](#dependencies)
- [Usage](#usage)
- [Advanced usage](#advanced-usage)
- [Internals](#internals)
- [Troubleshooting](#troubleshooting)
@ -48,19 +48,22 @@ JuNest follows the [Arch Linux philosophy](https://wiki.archlinux.org/index.php/
Quickstart
==========
There are three different ways you can run JuNest:
The basic way to run JuNest is via the [Proot](https://wiki.archlinux.org/index.php/Proot) as the backend program:
- As normal user - Allow to make basic operations: ```junest```
- As fakeroot - Allow to install/remove packages: ```junest -f```
- As root - Allow to have fully root privileges inside JuNest environment (you need to be root for executing this): ```junest -r```
To know more about the JuNest execution modes depending on the backend program
used, see the [Usage](#usage) section below.
After running JuNest
--------------------
If the JuNest image has not been downloaded yet, the script will download
the image and will place it to the default directory ~/.junest.
You can change the default directory by changing the environment variable *JUNEST\_HOME*.
the image from the repository and will place it to the default directory `~/.junest`.
You can change the default directory by changing the environment variable `JUNEST_HOME`.
If you are new on Archlinux and you are not familiar with *pacman* package manager
If you are new on Arch Linux and you are not familiar with `pacman` package manager
visit the [pacman rosetta page](https://wiki.archlinux.org/index.php/Pacman_Rosetta).
Installation
@ -90,7 +93,7 @@ Just clone the JuNest repo somewhere (for example in ~/.local/share/junest):
### Installation using AUR (Arch Linux only) ###
If you are using an Arch Linux system you can, alternatively, install JuNest from the [AUR repository](https://aur.archlinux.org/):
yogurt -S junest-git
yaourt -S junest-git
export PATH=/opt/junest/bin:$PATH
## Method two ##
@ -98,12 +101,72 @@ Alternatively, another installation method would be to directly download the JuN
ARCH=<one of "x86_64", "x86", "arm">
mkdir ~/.junest
curl https://dl.dropboxusercontent.com/u/42449030/junest/junest-${ARCH}.tar.gz | tar -xz -C ~/.junest
curl https://s3-eu-west-1.amazonaws.com/junest-repo/junest-${ARCH}.tar.gz | tar -xz -C ~/.junest
export PATH=~/.junest/opt/junest/bin:$PATH
Usage
=====
There are three different ways you can run JuNest depending on the backend program you decide to use.
PRoot based
-----------
[Proot](https://wiki.archlinux.org/index.php/Proot) represents the default
program used for accessing to the JuNest environments.
The main reason to choose Proot as default backend program is because
it represents a portable solution that works well in most of GNU/Linux distros available.
One of the major drawbacks is the fact that Proot is not officially
supported anymore, therefore, Proot bugs may no longer be fixed.
In order to run JuNest via Proot:
- As normal user - Allow to make basic operations: ```junest```
- As fakeroot - Allow to install/remove packages: ```junest -f```
Linux namespaces based
----------------------
The [Linux namespaces](http://man7.org/linux/man-pages/man7/namespaces.7.html)
represents the next generation backend program for JuNest.
The major drawback about the namespace is portability, as certain requirements
need to be satisfied: 1) Only starting from Linux 3.8, unprivileged processes can
create the required user and mount namespaces.
2) Moreover, the Linux kernel distro must have the user namespace enabled.
Hopefully, in the future the major GNU/Linux distros will start enabling such feature by default.
For instance, Ubuntu (version 14.04+) already has such feature enabled.
In order to run JuNest via Linux namespaces:
- As fakeroot - Allow to install/remove packages: ```junest -u```
Chroot based
------------
This solution suits only for privileged users. JuNest provides the possibility
to run the environment via `chroot` program.
In particular, it uses a special program called `GRoot`, an enhanced `chroot`
wrapper that allows to bind mount directories specified by the user, such as
/proc, /sys, /dev, /tmp and $HOME, before
executing any programs inside the JuNest sandbox. In case the mounting will not
work, JuNest is even providing the possibility to run the environment directly via
the pure `chroot` command.
In order to run JuNest via `chroot` solutions:
- As root via `GRoot` - Allow to have fully root privileges inside JuNest environment (you need to be root for executing this): ```junest -g```
- As root via `chroot` - Allow to have fully root privileges inside JuNest environment (you need to be root for executing this): ```junest -r```
Execution modes comparison table
----------------
The following table shows the capabilities that each backend program is able to perform:
| | QEMU | Root privileges required | Manage Official Packages | Manage AUR Packages | Portability | Support | User modes |
| --- | ---- | ------------------------ | ------------------------ | ------------------- | ----------- | ------- | ---------- |
| **Proot** | YES | NO | YES | YES | YES | Poor | Normal user and `fakeroot` |
| **Linux Namespaces** | NO | NO | YES | NO | Poor | YES | `fakeroot` only |
| **Chroot** | NO | YES | YES | YES | YES | YES | `root` only |
Advanced usage
==============
## Build image ##
You can build a new JuNest image from scratch by running the following command:
@ -114,7 +177,7 @@ files in order to make JuNest working properly (such as pacman, yogurt and proot
The option **-n** will skip the final validation tests if they are not needed.
Remember that the script to build the image must run in an Arch Linux OS with
arch-install-scripts, package-query, git and the base-devel packages installed.
To change the build directory just use the *JUNEST_TEMPDIR* (by default /tmp).
To change the build directory just use the `JUNEST_TEMPDIR` (by default /tmp).
After creating the image junest-x86\_64.tar.gz you can install it by running:
@ -141,12 +204,12 @@ To bind a host directory to a guest location, you can use proot arguments:
junest -p "-b /mnt/mydata:/home/user/mydata"
Check out the proot options with:
This will works with PRoot, Namespace and GRoot backend programs.
Check out the backend program options by passing `--help` option:
junest -p "--help"
junest [-u|-g] -p "--help"
## Systemd integration ##
Although JuNest has not been designed to be a complete container, it is even possible to
virtualize the process tree thanks to the [systemd container](https://wiki.archlinux.org/index.php/Systemd-nspawn).
The JuNest containter allows to run services inside the container that can be
@ -169,14 +232,14 @@ Internals
There are two main chroot jail used in JuNest.
The main one is [proot](https://wiki.archlinux.org/index.php/Proot) which
allows unprivileged users to execute programs inside a sandbox and
jchroot, a small and portable version of
GRoot, a small and portable version of
[arch-chroot](https://wiki.archlinux.org/index.php/Chroot) which is an
enhanced chroot for privileged users that mounts the primary directories
(i.e. /proc, /sys, /dev and /run) before executing any programs inside
the sandbox.
## Automatic fallback to classic chroot ##
If jchroot fails for some reasons in the host system (i.e. it is not able to
If GRoot fails for some reasons in the host system (i.e. it is not able to
mount one of the directories),
JuNest automatically tries to fallback to the classic chroot.
@ -345,6 +408,18 @@ Troubleshooting
> since JuNest will try to preserve the JuNest environment by assigning ownership
> of the files to the real user.
## Not enabled User namespace or kernel too old ##
> **Q**: Why do I get warning when I run JuNest via Linux namespaces?
$> junest -u
User namespace is not enabled or Kernel too old (<3.8). Proceeding anyway...
> **A**: This means that JuNest detected that the host OS either
> does not have a newer Linux version or the user namespace is not enabled.
> JuNest does not stop the execution of the program but it attempts to run it
> anyway. Try to use Proot as backend program in case is not possible to invoke namespaces.
More documentation
==================
There are additional tutorials in the

190
bin/groot Executable file
View file

@ -0,0 +1,190 @@
#!/bin/bash
#
# This script is the simplified and portable version of arch-chroot
# (https://wiki.archlinux.org/index.php/Change_root#Using_arch-chroot)
#
set -e
# JUNEST_BASE can be overridden for testing purposes.
# There is no need for doing it for normal usage.
JUNEST_BASE="${JUNEST_BASE:-$(readlink -f $(dirname $(readlink -f "$0"))/..)}"
NAME='GRoot'
CMD='groot'
DESCRIPTION="I am $NAME!"
CHROOTCMD=${CHROOTCMD:-chroot}
SHELL="/bin/sh"
MOUNT=mount
UMOUNT=umount
MOUNTPOINT=mountpoint
MKDIR=mkdir
TOUCH=touch
CUT=cut
SORT=sort
UNIQ=uniq
GREP=grep
MOUNTS_FILE=/proc/self/mounts
NOT_EXISTING_FILE=103
NOT_ABSOLUTE_PATH=111
NO_ROOT_PRIVILEGES=110
source "${JUNEST_BASE}/lib/utils/utils.sh"
################################ MAIN FUNCTIONS ###########################
function chroot_teardown() {
# Remove all mounts starting from the most nested ones.
# Suffix the CHROOTDIR with / to avoid umounting directories not belonging
# to CHROOTDIR.
for mp in $($GREP "${CHROOTDIR%/}/" $MOUNTS_FILE | $CUT -f2 -d' ' | $SORT -r | $UNIQ)
do
$UMOUNT $mp
done
$UMOUNT ${CHROOTDIR%/}
}
function chroot_maybe_add_mount() {
local cond=$1
shift
if eval "$cond"; then
$MOUNT "$@"
return
fi
return 1
}
function chroot_setup() {
$OPT_NO_UMOUNT || check_and_trap 'chroot_teardown' QUIT EXIT ABRT KILL TERM INT
if ! chroot_maybe_add_mount "! $MOUNTPOINT -q '$CHROOTDIR'" --bind "$CHROOTDIR" "$CHROOTDIR"
then
warn "Failed mount of directories. $CHROOTDIR is already a mountpoint. Skipping it..."
return 0
fi
local re='(.*):(.*)'
for binds in ${BINDINGS[@]}
do
local host_path=""
local guest_path=""
if [[ $binds =~ $re ]]
then
local host_path="${BASH_REMATCH[1]}"
local guest_path="${BASH_REMATCH[2]}"
else
local host_path="$binds"
local guest_path="$binds"
fi
create_node "${host_path}" "${CHROOTDIR}${guest_path}"
$MOUNT --rbind "${host_path}" "${CHROOTDIR}${guest_path}"
done
}
function create_node() {
local src="$1"
local dst="$2"
if [[ ! -e $src ]]
then
die_on_status $NOT_EXISTING_FILE "${src} does not exist."
elif [[ $src != /* ]]
then
die_on_status $NOT_ABSOLUTE_PATH "${src} is not an absolute path."
elif [[ -f $src ]]
then
$TOUCH "$dst"
elif [[ -d $src ]]
then
$MKDIR -p "$dst"
fi
}
function usage() {
cat <<EOF
$NAME (v$(cat $JUNEST_BASE/VERSION)): $DESCRIPTION
Usage:
$CMD [options] [<chroot-dir> [command]]
Options:
-b, --bind <path>
Make the content of <path> accessible in the guest rootfs.
This option makes any file or directory of the host rootfs
accessible in the confined environment just as if it were part of
the guest rootfs. By default the host path is bound to the same
path in the guest rootfs but users can specify any other location
with the syntax: -b <host_path>:<guest_location>. This option can
be invoked multiple times and the paths specified must be absolutes.
-n, --no-umount
Do not umount after chroot session finished.
-h, --help Print this help message
-V, --version Show the $NAME version
If 'command' is unspecified, $CMD will launch $SHELL.
EOF
}
version() {
echo -e "$NAME $(cat $JUNEST_BASE/VERSION)"
}
function parse_arguments() {
OPT_BIND=false
BINDINGS=()
OPT_NO_UMOUNT=false
OPT_HELP=false
OPT_VERSION=false
for opt in "$@"
do
case "$1" in
-b|--bind) OPT_BIND=true ; shift ; BINDINGS+=("$1") ; shift ;;
-n|--no-umount) OPT_NO_UMOUNT=true ; shift ;;
-h|--help) OPT_HELP=true ; shift ;;
-V|--version) OPT_VERSION=true ; shift ;;
-*) die "Invalid option $1" ;;
*) break ;;
esac
done
if [[ ! -z $1 ]]
then
CHROOTDIR="$1"
shift
fi
ARGS=()
for arg in "$@"
do
ARGS+=("$arg")
done
}
function is_user_root() {
(( EUID == 0 ))
}
function execute_operation() {
$OPT_HELP && usage && return
$OPT_VERSION && version && return
is_user_root || die_on_status $NO_ROOT_PRIVILEGES 'This script must be run with root privileges'
[[ -d $CHROOTDIR ]] || die_on_status $NOT_EXISTING_FILE "Can't create chroot on non-directory $CHROOTDIR"
chroot_setup "$CHROOTDIR" || die "Failed to setup chroot $CHROOTDIR"
$CHROOTCMD "$CHROOTDIR" "${ARGS[@]}"
}
function main() {
parse_arguments "$@"
execute_operation
}
main "$@"

View file

@ -1,98 +0,0 @@
#!/bin/bash
#
# This script is the simplified and portable version of arch-chroot
# (https://wiki.archlinux.org/index.php/Change_root#Using_arch-chroot)
#
set -e
JUNEST_BASE="$(readlink -f $(dirname $(readlink -f "$0"))/..)"
source "${JUNEST_BASE}/lib/utils/utils.sh"
################################ MAIN FUNCTIONS ###########################
chroot_add_mount() {
mount "$@" && CHROOT_ACTIVE_MOUNTS=("$2" "${CHROOT_ACTIVE_MOUNTS[@]}")
}
chroot_maybe_add_mount() {
local cond=$1; shift
if eval "$cond"; then
chroot_add_mount "$@"
fi
}
chroot_setup() {
CHROOT_ACTIVE_MOUNTS=()
[[ $(trap -p EXIT) ]] && die '(BUG): attempting to overwrite existing EXIT trap'
trap 'chroot_teardown' EXIT
chroot_maybe_add_mount "! mountpoint -q '$1'" "$1" "$1" --bind &&
chroot_add_mount proc "$1/proc" -t proc -o nosuid,noexec,nodev &&
chroot_add_mount sys "$1/sys" -t sysfs -o nosuid,noexec,nodev,ro &&
chroot_add_mount udev "$1/dev" -t devtmpfs -o mode=0755,nosuid &&
chroot_add_mount devpts "$1/dev/pts" -t devpts -o mode=0620,gid=5,nosuid,noexec &&
chroot_add_mount shm "$1/dev/shm" -t tmpfs -o mode=1777,nosuid,nodev &&
chroot_add_mount run "$1/run" -t tmpfs -o nosuid,nodev,mode=0755 &&
chroot_add_mount tmp "$1/tmp" -t tmpfs -o mode=1777,atime,nodev,nosuid &&
mkdir -p "$1/$HOME" &&
chroot_add_mount $HOME "$1/$HOME" --bind
mkdir -p "$1/run/lock"
}
chroot_teardown() {
umount "${CHROOT_ACTIVE_MOUNTS[@]}"
unset CHROOT_ACTIVE_MOUNTS
}
usage() {
cat <<EOF
usage: ${0##*/} chroot-dir [command]
-h Print this help message
If 'command' is unspecified, ${0##*/} will launch /bin/sh.
EOF
}
chroot_add_resolv_conf() {
local chrootdir=$1 resolv_conf=$1/etc/resolv.conf
# Handle resolv.conf as a symlink to somewhere else.
if [[ -L $chrootdir/etc/resolv.conf ]]; then
# readlink(1) should always give us *something* since we know at this point
# it's a symlink. For simplicity, ignore the case of nested symlinks.
resolv_conf=$(readlink "$chrootdir/etc/resolv.conf")
if [[ $resolv_conf = /* ]]; then
resolv_conf=$chrootdir$resolv_conf
else
resolv_conf=$chrootdir/etc/$resolv_conf
fi
# ensure file exists to bind mount over
if [[ ! -f $resolv_conf ]]; then
install -Dm644 /dev/null "$resolv_conf" || return 1
fi
fi
chroot_add_mount /etc/resolv.conf "$resolv_conf" --bind
}
if [[ -z $1 || $1 == -h || $1 == --help ]]; then
usage
exit $(( $# ? 0 : 1 ))
fi
(( EUID == 0 )) || die 'This script must be run with root privileges'
chrootdir=$1
shift
[[ -d $chrootdir ]] || die "Can't create chroot on non-directory $chrootdir"
chroot_setup "$chrootdir" || die "failed to setup chroot $chrootdir"
chroot_add_resolv_conf "$chrootdir" || die "failed to setup resolv.conf"
SHELL="/bin/sh" chroot "$chrootdir" "$@"

View file

@ -3,14 +3,21 @@
# This file is part of JuNest (https://github.com/fsquillace/junest).
#
JUNEST_BASE="$(readlink -f $(dirname $(readlink -f "$0"))/..)"
set -e
# JUNEST_BASE can be overridden for testing purposes.
# There is no need for doing it for normal usage.
JUNEST_BASE="${JUNEST_BASE:-$(readlink -f $(dirname $(readlink -f "$0"))/..)}"
source "${JUNEST_BASE}/lib/utils/utils.sh"
source "${JUNEST_BASE}/lib/core/common.sh"
source "${JUNEST_BASE}/lib/core/build.sh"
source "${JUNEST_BASE}/lib/core/setup.sh"
source "${JUNEST_BASE}/lib/core/proot.sh"
source "${JUNEST_BASE}/lib/core/chroot.sh"
source "${JUNEST_BASE}/lib/core/namespace.sh"
source "${JUNEST_BASE}/lib/core/proot.sh"
source "${JUNEST_BASE}/lib/core/setup.sh"
###################################
### General functions ###
@ -29,19 +36,21 @@ usage() {
echo
echo -e "Access options:"
echo -e "-f, --fakeroot Run $NAME with fakeroot privileges"
echo -e "-r, --root Run $NAME with root privileges"
echo -e "-p, --proot-args <args> Proot arguments (use $CMD -p \"--help\" to check out the proot options)"
echo -e "-g, --groot Run $NAME with root privileges via GRoot"
echo -e "-r, --root Run $NAME with root privileges via classic chroot"
echo -e "-u, --namespace Use Linux Namespace (with GRoot) instead of PRoot"
echo -e "-p, --backend-args <args> Arguments for backend program (PRoot or GRoot)"
echo -e " ($CMD -p \"--help\" to check out the PRoot options"
echo -e " $CMD -g -p \"--help\" to check out the GRoot options"
echo -e " $CMD -r -p \"--help\" to check out the chroot options)"
echo
echo -e "Building options:"
echo -e "-b, --build-image Build a $NAME image (must run in ArchLinux)"
echo -e "-n, --disable-validation Disable the $NAME image validation"
echo -e "-c, --check <${CMD}_script> Validate the env located in ${JUNEST_HOME}"
echo -e " using ${CMD}_script. This will alterate the environment"
echo -e "-s, --skip-root-tests Skip the root tests during the validation process"
echo
echo -e "General options:"
echo -e "-h, --help Show this help message"
echo -e "-v, --version Show the $NAME version"
echo -e "-V, --version Show the $NAME version"
}
version() {
@ -50,33 +59,17 @@ version() {
check_cli(){
if $OPT_BUILD_IMAGE
then
if $OPT_DELETE || $OPT_HELP || $OPT_VERSION || $OPT_SETUP_FROM_FILE || \
$OPT_FAKEROOT || $OPT_ROOT || $OPT_CHECK
then
die "The build image option must be used exclusively"
fi
fi
if $OPT_SKIP_ROOT_TEST
then
if $OPT_DELETE || $OPT_HELP || $OPT_VERSION || $OPT_SETUP_FROM_FILE || \
$OPT_FAKEROOT || $OPT_ROOT
then
die "The skip root tests option must be used with either build image or check options"
fi
fi
if $OPT_CHECK
then
if $OPT_DELETE || $OPT_HELP || $OPT_VERSION || $OPT_SETUP_FROM_FILE || \
$OPT_FAKEROOT || $OPT_ROOT || $OPT_BUILD_IMAGE
then
die "The validation image option must be used exclusively"
die "The build image option must be used exclusively"
fi
fi
if $OPT_DISABLE_VALIDATION
then
if $OPT_DELETE || $OPT_HELP || $OPT_VERSION || $OPT_SETUP_FROM_FILE || \
$OPT_FAKEROOT || $OPT_ROOT || $OPT_CHECK
$OPT_FAKEROOT || $OPT_ROOT
then
die "The disable validation option must be used with the build image option only"
fi
@ -84,7 +77,7 @@ check_cli(){
if $OPT_DELETE
then
if $OPT_BUILD_IMAGE || $OPT_HELP || $OPT_VERSION || $OPT_SETUP_FROM_FILE || \
$OPT_FAKEROOT || $OPT_ROOT || $OPT_DISABLE_VALIDATION || $OPT_CHECK
$OPT_FAKEROOT || $OPT_ROOT || $OPT_DISABLE_VALIDATION
then
die "The $NAME delete option must be used exclusively"
fi
@ -92,7 +85,7 @@ check_cli(){
if $OPT_HELP
then
if $OPT_BUILD_IMAGE || $OPT_DELETE || $OPT_VERSION || $OPT_SETUP_FROM_FILE || \
$OPT_FAKEROOT || $OPT_ROOT || $OPT_DISABLE_VALIDATION || $OPT_CHECK
$OPT_FAKEROOT || $OPT_ROOT || $OPT_DISABLE_VALIDATION
then
die "The $NAME help option must be used exclusively"
fi
@ -100,7 +93,7 @@ check_cli(){
if $OPT_VERSION
then
if $OPT_BUILD_IMAGE || $OPT_DELETE || $OPT_HELP || $OPT_SETUP_FROM_FILE || \
$OPT_FAKEROOT || $OPT_ROOT || $OPT_DISABLE_VALIDATION || $OPT_CHECK
$OPT_FAKEROOT || $OPT_ROOT || $OPT_DISABLE_VALIDATION
then
die "The $NAME version option must be used exclusively"
fi
@ -109,10 +102,10 @@ check_cli(){
then
die "You must access to $NAME with either fakeroot or root permissions"
fi
if $OPT_PROOT_ARGS || $OPT_ARCH
if $OPT_BACKEND_ARGS || $OPT_ARCH
then
if $OPT_BUILD_IMAGE || $OPT_DELETE || $OPT_HELP || \
$OPT_ROOT || $OPT_VERSION || $OPT_DISABLE_VALIDATION || $OPT_CHECK
$OPT_ROOT || $OPT_VERSION || $OPT_DISABLE_VALIDATION
then
die "Invalid syntax: Proot and arch args are not allowed with the other options"
fi
@ -120,7 +113,7 @@ check_cli(){
if [ "$ARGS" != "" ]
then
if $OPT_BUILD_IMAGE || $OPT_DELETE || $OPT_HELP || $OPT_SETUP_FROM_FILE || \
$OPT_VERSION || $OPT_DISABLE_VALIDATION || $OPT_CHECK
$OPT_VERSION || $OPT_DISABLE_VALIDATION
then
die "No arguments are needed. For the CLI syntax run: $CMD --help"
fi
@ -135,15 +128,15 @@ function parse_arguments(){
IMAGE_FILE=""
OPT_FAKEROOT=false
OPT_ROOT=false
OPT_PROOT_ARGS=false
PROOT_ARGS=""
OPT_GROOT=false
OPT_USER_NAMESPACE=false
OPT_BACKEND_ARGS=false
BACKEND_ARGS=""
OPT_ARCH=false
ARCH_ARG=""
OPT_BUILD_IMAGE=false
OPT_DISABLE_VALIDATION=false
OPT_CHECK=false
CHECK_ARG=""
OPT_SKIP_ROOT_TEST=false
OPT_DELETE=false
OPT_HELP=false
OPT_VERSION=false
@ -153,15 +146,15 @@ function parse_arguments(){
-i|--setup-from-file) OPT_SETUP_FROM_FILE=true ; shift ; IMAGE_FILE=$1 ; shift ;;
-f|--fakeroot) OPT_FAKEROOT=true ; shift ;;
-r|--root) OPT_ROOT=true ; shift ;;
-p|--proot-args) OPT_PROOT_ARGS=true ; shift ; PROOT_ARGS=$1; shift ;;
-g|--groot) OPT_GROOT=true ; shift ;;
-u|--namespace) OPT_USER_NAMESPACE=true ; shift ;;
-p|--backend-args) OPT_BACKEND_ARGS=true ; shift ; BACKEND_ARGS=$1; shift ;;
-a|--arch) OPT_ARCH=true ; shift ; ARCH_ARG=$1; shift ;;
-b|--build-image) OPT_BUILD_IMAGE=true ; shift ;;
-n|--disable-validation) OPT_DISABLE_VALIDATION=true ; shift ;;
-c|--check) OPT_CHECK=true ; shift ; CHECK_ARG=$1; shift ;;
-s|--skip-root-tests) OPT_SKIP_ROOT_TEST=true ; shift ;;
-d|--delete) OPT_DELETE=true ; shift ;;
-h|--help) OPT_HELP=true ; shift ;;
-v|--version) OPT_VERSION=true ; shift ;;
-V|--version) OPT_VERSION=true ; shift ;;
--) shift ; break ;;
-*) die "Invalid option $1" ;;
*) break ;;
@ -180,14 +173,11 @@ function execute_operation(){
$OPT_VERSION && version && return
if $OPT_BUILD_IMAGE; then
build_image_env $OPT_DISABLE_VALIDATION $OPT_SKIP_ROOT_TEST
build_image_env $OPT_DISABLE_VALIDATION
return
elif $OPT_DELETE; then
delete_env
return
elif $OPT_CHECK; then
check_env "${JUNEST_HOME}" "${CHECK_ARG}" $OPT_SKIP_ROOT_TEST
return
fi
if ! is_env_installed
@ -205,20 +195,28 @@ function execute_operation(){
[ -z "${ARCH_ARG}" ] || \
die "The option --arch cannot be specified since JuNest has already been downloaded in $JUNEST_HOME"
if $OPT_FAKEROOT; then
run_env_as_fakeroot "${PROOT_ARGS}" "${ARGS[@]}"
local run_env
if $OPT_USER_NAMESPACE; then
run_env=run_env_with_namespace
elif $OPT_FAKEROOT; then
run_env=run_env_as_fakeroot
elif $OPT_ROOT; then
run_env_as_root "${ARGS[@]}"
run_env=run_env_as_chroot
elif $OPT_GROOT; then
run_env=run_env_as_groot
else
run_env_as_user "${PROOT_ARGS}" "${ARGS[@]}"
run_env=run_env_as_user
fi
$run_env "${BACKEND_ARGS}" "${ARGS[@]}"
}
function cli() {
function main() {
parse_arguments "$@"
check_cli
execute_operation
}
cli "$@"
main "$@"
# vim: set ts=4 sw=4 noet:

62
lib/checks/check.sh Executable file
View file

@ -0,0 +1,62 @@
#!/usr/bin/env bash
#
# This modules is used for:
# - Running checks against the building JuNest image
# - Integration tests on JuNest script against different execution modes (i.e. -f, -u, -r modes)
#
# Dependencies:
# - None
#
# vim: ft=sh
set -eu
OPT_RUN_ROOT_TESTS=${1:-false}
RUN_ROOT_TESTS=false
[[ ${OPT_RUN_ROOT_TESTS} == "--run-root-tests" ]] && RUN_ROOT_TESTS=true
OPT_SKIP_AUR_TESTS=${1:-false}
SKIP_AUR_TESTS=false
[[ ${OPT_SKIP_AUR_TESTS} == "--skip-aur-tests" ]] && SKIP_AUR_TESTS=true
JUNEST_HOME=${JUNEST_HOME:-$HOME/.junest}
# JUNEST_BASE can be overridden for testing purposes.
# There is no need for doing it for normal usage.
JUNEST_BASE="${JUNEST_BASE:-$(readlink -f $(dirname $(readlink -f "$0"))/../..)}"
source "${JUNEST_BASE}/lib/utils/utils.sh"
source "${JUNEST_BASE}/lib/core/common.sh"
info "Validating JuNest located in ${JUNEST_HOME}..."
info "Initial JuNest setup..."
echo "Server = ${DEFAULT_MIRROR}" >> /etc/pacman.d/mirrorlist
pacman --noconfirm -Syy
pacman --noconfirm -S base-devel
info "Checking essential executables work..."
pacman -Qi pacman 1> /dev/null
yogurt -V 1> /dev/null
/opt/proot/proot-$ARCH --help 1> /dev/null
repo_package1=tree
echo "Checking ${repo_package1} package from official repo..."
pacman --noconfirm -S ${repo_package1}
tree -L 1
pacman --noconfirm -Rsn ${repo_package1}
repo_package2=iftop
info "Checking ${repo_package2} package from official repo..."
pacman --noconfirm -S ${repo_package2}
$RUN_ROOT_TESTS && iftop -t -s 5
pacman --noconfirm -Rsn ${repo_package2}
if ! $SKIP_AUR_TESTS
then
aur_package=tcptraceroute
info "Checking ${aur_package} package from AUR repo..."
yogurt -A --noconfirm -S ${aur_package}
$RUN_ROOT_TESTS && tcptraceroute localhost
pacman --noconfirm -Rsn ${aur_package}
fi

View file

@ -40,7 +40,6 @@ function build_image_env(){
_check_package git
local disable_validation=$1
local skip_root_tests=$2
local maindir=$(TMPDIR=$JUNEST_TEMPDIR mktemp -d -t ${CMD}.XXXXXXXXXX)
sudo mkdir -p ${maindir}/root
@ -51,7 +50,8 @@ function build_image_env(){
# All the essential executables (ln, mkdir, chown, etc) are in coreutils
# yaourt requires sed
# localedef (called by locale-gen) requires gzip
sudo pacstrap -G -M -d ${maindir}/root pacman coreutils libunistring archlinux-keyring sed gzip
# unshare command belongs to util-linux
sudo pacstrap -G -M -d ${maindir}/root pacman coreutils libunistring archlinux-keyring sed gzip util-linux
sudo bash -c "echo 'Server = $DEFAULT_MIRROR' >> ${maindir}/root/etc/pacman.d/mirrorlist"
sudo mkdir -p ${maindir}/root/run/lock
@ -69,11 +69,11 @@ function build_image_env(){
# sed command is required for locale-gen
sudo ln -sf /usr/share/zoneinfo/posix/UTC ${maindir}/root/etc/localtime
sudo bash -c "echo 'en_US.UTF-8 UTF-8' >> ${maindir}/root/etc/locale.gen"
sudo ${maindir}/root/opt/junest/bin/jchroot ${maindir}/root locale-gen
sudo ${maindir}/root/opt/junest/bin/groot ${maindir}/root locale-gen
sudo bash -c "echo LANG=\"en_US.UTF-8\" >> ${maindir}/root/etc/locale.conf"
info "Setting up the pacman keyring (this might take a while!)..."
sudo ${maindir}/root/opt/junest/bin/jchroot ${maindir}/root bash -c \
sudo ${maindir}/root/opt/junest/bin/groot ${maindir}/root bash -c \
"pacman-key --init; pacman-key --populate archlinux; [ -e /etc/pacman.d/gnupg/S.gpg-agent ] && gpg-connect-agent -S /etc/pacman.d/gnupg/S.gpg-agent killagent /bye"
sudo rm ${maindir}/root/var/cache/pacman/pkg/*
@ -88,7 +88,8 @@ function build_image_env(){
then
mkdir -p ${maindir}/root_test
$TAR -zxpf ${imagefile} -C "${maindir}/root_test"
check_env "${maindir}/root_test" "${maindir}/root_test/opt/${CMD}/bin/${CMD}" $skip_root_tests
JUNEST_HOME="${maindir}/root_test" ${maindir}/root_test/opt/${CMD}/bin/${CMD} -f ./lib/checks/check.sh
JUNEST_HOME="${maindir}/root_test" sudo -E ${maindir}/root_test/opt/${CMD}/bin/${CMD} -g ./lib/checks/check.sh --run-root-tests
fi
sudo cp ${maindir}/output/${imagefile} ${ORIGIN_WD}
@ -97,44 +98,3 @@ function build_image_env(){
trap - QUIT EXIT ABRT KILL TERM INT
sudo rm -fr "$maindir"
}
function check_env(){
local testdir=$1
local cmd=$2
local skip_root_tests=$3
info "Validating ${NAME} located in ${testdir} using the ${cmd} script..."
echo "Server = ${DEFAULT_MIRROR}" >> ${testdir}/etc/pacman.d/mirrorlist
JUNEST_HOME=${testdir} ${cmd} -f pacman --noconfirm -Syy
# Check most basic executables work
$skip_root_tests || JUNEST_HOME=${testdir} sudo -E ${cmd} -r pacman -Qi pacman 1> /dev/null
JUNEST_HOME=${testdir} ${cmd} -- pacman -Qi pacman 1> /dev/null
JUNEST_HOME=${testdir} ${cmd} -f -- pacman -Qi pacman 1> /dev/null
$skip_root_tests || JUNEST_HOME=${testdir} sudo -E ${cmd} -r yogurt -V 1> /dev/null
JUNEST_HOME=${testdir} ${cmd} -- yogurt -V 1> /dev/null
JUNEST_HOME=${testdir} ${cmd} -f -- yogurt -V 1> /dev/null
$skip_root_tests || JUNEST_HOME=${testdir} sudo -E ${cmd} -r /opt/proot/proot-$ARCH --help 1> /dev/null
JUNEST_HOME=${testdir} ${cmd} -- /opt/proot/proot-$ARCH --help 1> /dev/null
JUNEST_HOME=${testdir} ${cmd} -f -- /opt/proot/proot-$ARCH --help 1> /dev/null
local repo_package=tree
info "Installing ${repo_package} package from official repo using proot..."
JUNEST_HOME=${testdir} ${cmd} -f pacman --noconfirm -S ${repo_package}
JUNEST_HOME=${testdir} ${cmd} tree
JUNEST_HOME=${testdir} ${cmd} -f tree
local repo_package=iftop
info "Installing ${repo_package} package from official repo using root..."
JUNEST_HOME=${testdir} ${cmd} -f pacman --noconfirm -S ${repo_package}
$skip_root_tests || JUNEST_HOME=${testdir} sudo -E ${cmd} -r iftop -t -s 5
JUNEST_HOME=${testdir} ${cmd} -f pacman --noconfirm -S base-devel
local aur_package=tcptraceroute
info "Installing ${aur_package} package from AUR repo using proot..."
JUNEST_HOME=${testdir} ${cmd} -f -- yogurt -A --noconfirm -S ${aur_package}
$skip_root_tests || JUNEST_HOME=${testdir} sudo -E ${cmd} -r tcptraceroute localhost
info "Removing the previous packages..."
JUNEST_HOME=${testdir} ${cmd} -f pacman --noconfirm -Rsn tcptraceroute tree iftop
}

View file

@ -8,27 +8,12 @@
#
# vim: ft=sh
#######################################
# Run JuNest as real root.
#
# Globals:
# JUNEST_HOME (RO) : The JuNest home directory.
# UID (RO) : The user ID.
# SUDO_USER (RO) : The sudo user ID.
# SUDO_GID (RO) : The sudo group ID.
# SH (RO) : Contains the default command to run in JuNest.
# Arguments:
# cmd ($@?) : The command to run inside JuNest environment.
# Default command is defined by SH variable.
# Returns:
# $ARCHITECTURE_MISMATCH : If host and JuNest architecture are different.
# Output:
# - : The command output.
#######################################
function run_env_as_root(){
source ${JUNEST_HOME}/etc/junest/info
[ "$JUNEST_ARCH" != "$ARCH" ] && \
die_on_status $ARCHITECTURE_MISMATCH "The host system architecture is not correct: $ARCH != $JUNEST_ARCH"
function _run_env_as_xroot(){
local cmd=$1
local backend_args="$2"
shift 2
check_same_arch
local uid=$UID
# SUDO_USER is more reliable compared to SUDO_UID
@ -41,5 +26,63 @@ function run_env_as_root(){
trap - QUIT EXIT ABRT KILL TERM INT
trap "[ -z $uid ] || chown_cmd -R ${uid} ${JUNEST_HOME};" EXIT QUIT ABRT KILL TERM INT
JUNEST_ENV=1 chroot_cmd "$JUNEST_HOME" "${SH[@]}" "-c" "${main_cmd}"
copy_common_files
check_nested_env
JUNEST_ENV=1 $cmd $backend_args "$JUNEST_HOME" "${SH[@]}" "-c" "${main_cmd}"
}
#######################################
# Run JuNest as real root via GRoot command.
#
# Globals:
# JUNEST_HOME (RO) : The JuNest home directory.
# UID (RO) : The user ID.
# SUDO_USER (RO) : The sudo user ID.
# SUDO_GID (RO) : The sudo group ID.
# SH (RO) : Contains the default command to run in JuNest.
# Arguments:
# backend_args ($1) : The arguments to pass to proot
# cmd ($2-?) : The command to run inside JuNest environment.
# Default command is defined by SH variable.
# Returns:
# $ARCHITECTURE_MISMATCH : If host and JuNest architecture are different.
# Output:
# - : The command output.
#######################################
function run_env_as_groot(){
local backend_args="$1"
shift
provide_common_bindings
local bindings=${RESULT}
unset RESULT
_run_env_as_xroot "$GROOT $bindings" "$backend_args" "$@"
}
#######################################
# Run JuNest as real root via chroot command.
#
# Globals:
# JUNEST_HOME (RO) : The JuNest home directory.
# UID (RO) : The user ID.
# SUDO_USER (RO) : The sudo user ID.
# SUDO_GID (RO) : The sudo group ID.
# SH (RO) : Contains the default command to run in JuNest.
# Arguments:
# backend_args ($1) : The arguments to pass to proot
# cmd ($2-?) : The command to run inside JuNest environment.
# Default command is defined by SH variable.
# Returns:
# $ARCHITECTURE_MISMATCH : If host and JuNest architecture are different.
# Output:
# - : The command output.
#######################################
function run_env_as_chroot(){
local backend_args="$1"
shift
_run_env_as_xroot chroot_cmd "$backend_args" "$@"
}

View file

@ -7,8 +7,6 @@
#
# vim: ft=sh
set -e
NAME='JuNest'
CMD='junest'
DESCRIPTION='The Arch Linux based distro that runs upon any Linux distros without root access'
@ -19,21 +17,11 @@ ARCHITECTURE_MISMATCH=104
ROOT_ACCESS_ERROR=105
NESTED_ENVIRONMENT=106
VARIABLE_NOT_SET=107
NO_CONFIG_FOUND=108
if [ "$JUNEST_ENV" == "1" ]
then
die_on_status $NESTED_ENVIRONMENT "Error: Nested ${NAME} environments are not allowed"
elif [ ! -z $JUNEST_ENV ] && [ "$JUNEST_ENV" != "0" ]
then
die_on_status $VARIABLE_NOT_SET "The variable JUNEST_ENV is not properly set"
fi
[ -z ${JUNEST_HOME} ] && JUNEST_HOME=~/.${CMD}
[ -z ${JUNEST_BASE} ] && JUNEST_BASE=${JUNEST_HOME}/opt/junest
if [ -z ${JUNEST_TEMPDIR} ] || [ ! -d ${JUNEST_TEMPDIR} ]
then
JUNEST_TEMPDIR=/tmp
fi
JUNEST_HOME=${JUNEST_HOME:-~/.${CMD}}
JUNEST_BASE=${JUNEST_BASE:-${JUNEST_HOME}/opt/junest}
JUNEST_TEMPDIR=${JUNEST_TEMPDIR:-/tmp}
# The update of the variable PATH ensures that the executables are
# found on different locations
@ -78,8 +66,8 @@ SH=("/bin/sh" "--login")
# List of executables that are run in the host OS:
PROOT="${JUNEST_HOME}/opt/proot/proot-${ARCH}"
CHROOT=${JUNEST_BASE}/bin/jchroot
CLASSIC_CHROOT="chroot"
GROOT=${JUNEST_BASE}/bin/groot
CLASSIC_CHROOT=chroot
WGET="wget --no-check-certificate"
CURL="curl -L -J -O -k"
TAR=tar
@ -89,6 +77,9 @@ RM=rm
MKDIR=mkdir
GETENT=getent
CP=cp
# Used for checking user namespace in config.gz file
ZGREP=zgrep
UNSHARE=unshare
LD_EXEC="$LD_LIB --library-path ${JUNEST_HOME}/usr/lib:${JUNEST_HOME}/lib"
@ -97,27 +88,58 @@ LD_EXEC="$LD_LIB --library-path ${JUNEST_HOME}/usr/lib:${JUNEST_HOME}/lib"
# image.
function ln_cmd(){
$LN $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/$LN $@
$LN "$@" || $LD_EXEC ${JUNEST_HOME}/usr/bin/$LN "$@"
}
function getent_cmd(){
$GETENT $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/$GETENT $@
$GETENT "$@" || $LD_EXEC ${JUNEST_HOME}/usr/bin/$GETENT "$@"
}
function cp_cmd(){
$CP $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/$CP $@
$CP "$@" || $LD_EXEC ${JUNEST_HOME}/usr/bin/$CP "$@"
}
function rm_cmd(){
$RM $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/$RM $@
$RM "$@" || $LD_EXEC ${JUNEST_HOME}/usr/bin/$RM "$@"
}
function chown_cmd(){
$CHOWN $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/$CHOWN $@
$CHOWN "$@" || $LD_EXEC ${JUNEST_HOME}/usr/bin/$CHOWN "$@"
}
function mkdir_cmd(){
$MKDIR $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/$MKDIR $@
$MKDIR "$@" || $LD_EXEC ${JUNEST_HOME}/usr/bin/$MKDIR "$@"
}
function zgrep_cmd(){
# No need for LD_EXEC as zgrep is a POSIX shell script
$ZGREP "$@" || ${JUNEST_HOME}/usr/bin/$ZGREP "$@"
}
function download_cmd(){
$WGET "$@" || $CURL "$@"
}
function chroot_cmd(){
$CLASSIC_CHROOT "$@" || $LD_EXEC ${JUNEST_HOME}/usr/bin/$CLASSIC_CHROOT "$@"
}
function unshare_cmd(){
# Most of the distros do not have the `unshare` command updated
# with --user option available.
# Hence, give priority to the `unshare` executable in JuNest image.
# Also, unshare provides an environment in which /bin/sh maps to dash shell,
# therefore it ignores all the remaining SH arguments (i.e. --login) as
# they are not supported by dash.
if $LD_EXEC ${JUNEST_HOME}/usr/bin/$UNSHARE --user "${SH[0]}" "-c" ":"
then
$LD_EXEC ${JUNEST_HOME}/usr/bin/$UNSHARE "${@}"
elif $UNSHARE --user "${SH[0]}" "-c" ":"
then
$UNSHARE "$@"
else
die "Error: Something went wrong with unshare command. Exiting"
fi
}
function proot_cmd(){
@ -130,20 +152,60 @@ function proot_cmd(){
then
PROOT_NO_SECCOMP=1 ${PROOT} ${proot_args} "${@}"
else
die "Error: Check if the ${CMD} arguments are correct and if the kernel is too old use the option ${CMD} -p \"-k 3.10\""
die "Error: Something went wrong with proot command. Exiting"
fi
}
function download_cmd(){
$WGET $@ || $CURL $@
}
function chroot_cmd(){
$CHROOT "$@" || $CLASSIC_CHROOT "$@" || $LD_EXEC ${JUNEST_HOME}/usr/bin/chroot "$@"
}
############## COMMON FUNCTIONS ###############
#######################################
# Check if the executable is being running inside a JuNest environment.
#
# Globals:
# JUNEST_ENV (RO) : The boolean junest env check
# NESTED_ENVIRONMENT (RO) : The nest env exception
# VARIABLE_NOT_SET (RO) : The var not set exception
# NAME (RO) : The JuNest name
# Arguments:
# None
# Returns:
# VARIABLE_NOT_SET : If no JUNEST_ENV is not properly set
# NESTED_ENVIRONMENT : If the script is executed inside JuNest env
# Output:
# None
#######################################
function check_nested_env() {
if [[ $JUNEST_ENV == "1" ]]
then
die_on_status $NESTED_ENVIRONMENT "Error: Nested ${NAME} environments are not allowed"
elif [[ ! -z $JUNEST_ENV ]] && [[ $JUNEST_ENV != "0" ]]
then
die_on_status $VARIABLE_NOT_SET "The variable JUNEST_ENV is not properly set"
fi
}
#######################################
# Check if the architecture between Host OS and Guest OS is the same.
#
# Globals:
# JUNEST_HOME (RO) : The JuNest home path.
# ARCHITECTURE_MISMATCH (RO) : The arch mismatch exception
# ARCH (RO) : The host OS arch
# JUNEST_ARCH (RO) : The JuNest arch
# Arguments:
# None
# Returns:
# ARCHITECTURE_MISMATCH : If arch between host and guest is not the same
# Output:
# None
#######################################
function check_same_arch() {
source ${JUNEST_HOME}/etc/junest/info
[ "$JUNEST_ARCH" != "$ARCH" ] && \
die_on_status $ARCHITECTURE_MISMATCH "The host system architecture is not correct: $ARCH != $JUNEST_ARCH"
return 0
}
#######################################
# Provide the proot common binding options for both normal user and fakeroot.
# The list of bindings can be found in `proot --help`. This function excludes
@ -160,7 +222,7 @@ function chroot_cmd(){
# Output:
# None
#######################################
function _provide_common_bindings(){
function provide_common_bindings(){
RESULT=""
local re='(.*):.*'
for bind in "/dev" "/sys" "/proc" "/tmp" "$HOME"
@ -191,7 +253,7 @@ function _provide_common_bindings(){
# Output:
# None
#######################################
function _copy_passwd_and_group(){
function copy_passwd_and_group(){
# Enumeration of users/groups is disabled/limited depending on how nsswitch.conf
# is configured.
# Try to at least get the current user via `getent passwd $USER` since it uses
@ -200,27 +262,27 @@ function _copy_passwd_and_group(){
! getent_cmd passwd ${USER} >> ${JUNEST_HOME}/etc/passwd
then
warn "getent command failed or does not exist. Binding directly from /etc/passwd."
_copy_file /etc/passwd ${JUNEST_HOME}/etc/passwd
copy_file /etc/passwd ${JUNEST_HOME}/etc/passwd
fi
if ! getent_cmd group > ${JUNEST_HOME}/etc/group
then
warn "getent command failed or does not exist. Binding directly from /etc/group."
_copy_file /etc/group ${JUNEST_HOME}/etc/group
copy_file /etc/group ${JUNEST_HOME}/etc/group
fi
return 0
}
function _copy_file() {
function copy_file() {
local file="${1}"
[[ -r "$file" ]] && cp_cmd "$file" "${JUNEST_HOME}/$file"
return 0
}
function _copy_common_files() {
_copy_file /etc/host.conf
_copy_file /etc/hosts
_copy_file /etc/nsswitch.conf
_copy_file /etc/resolv.conf
function copy_common_files() {
copy_file /etc/host.conf
copy_file /etc/hosts
copy_file /etc/nsswitch.conf
copy_file /etc/resolv.conf
return 0
}

100
lib/core/namespace.sh Normal file
View file

@ -0,0 +1,100 @@
#!/usr/bin/env bash
#
# This module contains all namespace functionalities for JuNest.
#
# http://man7.org/linux/man-pages/man7/namespaces.7.html
# http://man7.org/linux/man-pages/man2/unshare.2.html
#
# Dependencies:
# - lib/utils/utils.sh
# - lib/core/common.sh
#
# vim: ft=sh
CONFIG_PROC_FILE="/proc/config.gz"
CONFIG_BOOT_FILE="/boot/config-$($UNAME -r)"
function _is_user_namespace_enabled() {
local config_file=""
if [[ -e $CONFIG_PROC_FILE ]]
then
config_file=$CONFIG_PROC_FILE
elif [[ -e $CONFIG_BOOT_FILE ]]
then
config_file=$CONFIG_BOOT_FILE
else
return $NOT_EXISTING_FILE
fi
if ! zgrep_cmd -q "CONFIG_USER_NS=y" $config_file
then
return $NO_CONFIG_FOUND
fi
}
function _check_user_namespace() {
set +e
_is_user_namespace_enabled
case $? in
$NOT_EXISTING_FILE) warn "Could not understand if user namespace is enabled. No config.gz file found. Proceeding anyway..." ;;
$NO_CONFIG_FOUND) warn "User namespace is not enabled or Kernel too old (<3.8). Proceeding anyway..." ;;
esac
set -e
}
function _run_env_with_namespace(){
local backend_args="$1"
shift
check_nested_env
provide_common_bindings
local bindings=${RESULT}
unset RESULT
# Use option -n in groot because umount do not work sometimes.
# As soon as the process terminates, the namespace
# will terminate too with its own mounted directories.
if [[ "$1" != "" ]]
then
JUNEST_ENV=1 unshare_cmd --mount --user --map-root-user $GROOT -n $bindings $backend_args "$JUNEST_HOME" "${SH[@]}" "-c" "$(insert_quotes_on_spaces "${@}")"
else
JUNEST_ENV=1 unshare_cmd --mount --user --map-root-user $GROOT -n $bindings $backend_args "$JUNEST_HOME" "${SH[@]}"
fi
}
#######################################
# Run JuNest as fakeroot user via user namespace.
#
# Globals:
# JUNEST_HOME (RO) : The JuNest home directory.
# GROOT (RO) : The groot program.
# SH (RO) : Contains the default command to run in JuNest.
# Arguments:
# backend_args ($1) : The arguments to pass to proot
# cmd ($2-?) : The command to run inside JuNest environment.
# Default command is defined by SH variable.
# Returns:
# $ARCHITECTURE_MISMATCH : If host and JuNest architecture are different.
# Depends on the unshare command outcome.
# Output:
# - : The command output.
#######################################
function run_env_with_namespace() {
local backend_args="$1"
shift
_check_user_namespace
check_same_arch
copy_common_files
copy_file /etc/hosts.equiv
copy_file /etc/netgroup
copy_file /etc/networks
# No need for localtime as it is setup during the image build
#copy_file /etc/localtime
copy_passwd_and_group
_run_env_with_namespace "$backend_args" "$@"
}

View file

@ -12,6 +12,7 @@ function _run_env_with_proot(){
local proot_args="$1"
shift
check_nested_env
if [ "$1" != "" ]
then
JUNEST_ENV=1 proot_cmd "${proot_args}" "${SH[@]}" "-c" "$(insert_quotes_on_spaces "${@}")"
@ -44,30 +45,33 @@ function _run_env_with_qemu(){
# Run JuNest as fakeroot.
#
# Globals:
# JUNEST_HOME (RO) : The JuNest home directory.
# EUID (RO) : The user ID.
# SH (RO) : Contains the default command to run in JuNest.
# JUNEST_HOME (RO) : The JuNest home directory.
# EUID (RO) : The user ID.
# SH (RO) : Contains the default command to run in JuNest.
# Arguments:
# cmd ($@?) : The command to run inside JuNest environment.
# backend_args ($1) : The arguments to pass to proot
# cmd ($2-?) : The command to run inside JuNest environment.
# Default command is defined by SH variable.
# Returns:
# $ROOT_ACCESS_ERROR : If the user is the real root.
# $ROOT_ACCESS_ERROR : If the user is the real root.
# Output:
# - : The command output.
# - : The command output.
#######################################
function run_env_as_fakeroot(){
(( EUID == 0 )) && \
die_on_status $ROOT_ACCESS_ERROR "You cannot access with root privileges. Use --root option instead."
die_on_status $ROOT_ACCESS_ERROR "You cannot access with root privileges. Use --groot option instead."
local backend_args="$1"
shift
_copy_common_files
copy_common_files
_provide_common_bindings
provide_common_bindings
local bindings=${RESULT}
unset RESULT
# An alternative is via -S option:
#_run_env_with_qemu "-S ${JUNEST_HOME} $1" "${@:2}"
_run_env_with_qemu "-0 ${bindings} -r ${JUNEST_HOME} $1" "${@:2}"
_run_env_with_qemu "-0 ${bindings} -r ${JUNEST_HOME} $backend_args" "$@"
}
#######################################
@ -78,7 +82,8 @@ function run_env_as_fakeroot(){
# EUID (RO) : The user ID.
# SH (RO) : Contains the default command to run in JuNest.
# Arguments:
# cmd ($@?) : The command to run inside JuNest environment.
# backend_args ($1) : The arguments to pass to proot
# cmd ($2-?) : The command to run inside JuNest environment.
# Default command is defined by SH variable.
# Returns:
# $ROOT_ACCESS_ERROR : If the user is the real root.
@ -87,23 +92,25 @@ function run_env_as_fakeroot(){
#######################################
function run_env_as_user(){
(( EUID == 0 )) && \
die_on_status $ROOT_ACCESS_ERROR "You cannot access with root privileges. Use --root option instead."
die_on_status $ROOT_ACCESS_ERROR "You cannot access with root privileges. Use --groot option instead."
local backend_args="$1"
shift
# Files to bind are visible in `proot --help`.
# This function excludes /etc/mtab file so that
# it will not give conflicts with the related
# symlink in the Arch Linux image.
_copy_common_files
_copy_file /etc/hosts.equiv
_copy_file /etc/netgroup
_copy_file /etc/networks
copy_common_files
copy_file /etc/hosts.equiv
copy_file /etc/netgroup
copy_file /etc/networks
# No need for localtime as it is setup during the image build
#_copy_file /etc/localtime
_copy_passwd_and_group
#copy_file /etc/localtime
copy_passwd_and_group
_provide_common_bindings
provide_common_bindings
local bindings=${RESULT}
unset RESULT
_run_env_with_qemu "${bindings} -r ${JUNEST_HOME} $1" "${@:2}"
_run_env_with_qemu "${bindings} -r ${JUNEST_HOME} $backend_args" "$@"
}

View file

@ -170,6 +170,20 @@ function ask(){
[ "$res" == "Y" ]
}
function check_and_trap() {
local sigs="${@:2:${#@}}"
local traps="$(trap -p $sigs)"
[[ $traps ]] && die "Attempting to overwrite existing $sigs trap: $traps"
trap $@
}
function check_and_force_trap() {
local sigs="${@:2:${#@}}"
local traps="$(trap -p $sigs)"
[[ $traps ]] && warn "Attempting to overwrite existing $sigs trap: $traps"
trap $@
}
function insert_quotes_on_spaces(){
# It inserts quotes between arguments.
# Useful to preserve quotes on command

View file

@ -18,6 +18,7 @@ function oneTimeSetUp(){
function setUp(){
cwdSetUp
junestSetUp
init_mocks
}
function tearDown(){
@ -25,28 +26,42 @@ function tearDown(){
cwdTearDown
}
function test_run_env_as_root_different_arch(){
echo "JUNEST_ARCH=XXX" > ${JUNEST_HOME}/etc/junest/info
assertCommandFailOnStatus 104 run_env_as_root pwd
}
function _test_run_env_as_root() {
function init_mocks() {
chroot_cmd() {
[ "$JUNEST_ENV" != "1" ] && return 1
echo $@
echo "chroot_cmd $@"
}
assertCommandSuccess run_env_as_root $@
GROOT=chroot_cmd
}
function test_run_env_as_root_cmd(){
_test_run_env_as_root pwd
assertEquals "$JUNEST_HOME /bin/sh --login -c pwd" "$(cat $STDOUTF)"
function test_run_env_as_groot_cmd(){
assertCommandSuccess run_env_as_groot "" pwd
assertEquals "chroot_cmd -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c pwd" "$(cat $STDOUTF)"
}
function test_run_env_as_classic_root_no_cmd(){
_test_run_env_as_root
assertEquals "$JUNEST_HOME /bin/sh --login -c /bin/sh --login" "$(cat $STDOUTF)"
function test_run_env_as_groot_no_cmd(){
assertCommandSuccess run_env_as_groot ""
assertEquals "chroot_cmd -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c /bin/sh --login" "$(cat $STDOUTF)"
}
function test_run_env_as_groot_cmd_with_backend_args(){
assertCommandSuccess run_env_as_groot "-n -b /home/blah" pwd
assertEquals "chroot_cmd -b $HOME -b /tmp -b /proc -b /sys -b /dev -n -b /home/blah $JUNEST_HOME /bin/sh --login -c pwd" "$(cat $STDOUTF)"
}
function test_run_env_as_chroot_cmd(){
assertCommandSuccess run_env_as_chroot "" pwd
assertEquals "chroot_cmd $JUNEST_HOME /bin/sh --login -c pwd" "$(cat $STDOUTF)"
}
function test_run_env_as_chroot_no_cmd(){
assertCommandSuccess run_env_as_chroot ""
assertEquals "chroot_cmd $JUNEST_HOME /bin/sh --login -c /bin/sh --login" "$(cat $STDOUTF)"
}
function test_run_env_as_chroot_cmd_with_backend_args(){
assertCommandSuccess run_env_as_chroot "-n -b /home/blah" pwd
assertEquals "chroot_cmd -n -b /home/blah $JUNEST_HOME /bin/sh --login -c pwd" "$(cat $STDOUTF)"
}
source $JUNEST_ROOT/tests/utils/shunit2

View file

@ -1,181 +0,0 @@
#!/bin/bash
source "$(dirname $0)/../utils/utils.sh"
source $(dirname $0)/../../bin/junest -h &> /dev/null
# Disable the exiterr
set +e
function oneTimeSetUp(){
setUpUnitTests
}
function setUp(){
function is_env_installed(){
return 0
}
}
## Mock functions ##
function usage(){
echo "usage"
}
function version(){
echo "version"
}
function build_image_env(){
local disable_validation=$1
local skip_root_tests=$2
echo "build_image_env($disable_validation,$skip_root_tests)"
}
function check_env(){
local env_home=$1
local cmd_script=$2
local skip_root_tests=$3
echo "check_env($env_home,$cmd_script,$skip_root_tests)"
}
function delete_env(){
echo "delete_env"
}
function setup_env_from_file(){
echo "setup_env_from_file($1)"
}
function setup_env(){
echo "setup_env($1)"
}
function run_env_as_fakeroot(){
local proot_args="$1"
shift
echo "run_env_as_fakeroot($proot_args,$@)"
}
function run_env_as_root(){
echo "run_env_as_root $@"
}
function run_env_as_user(){
local proot_args="$1"
shift
echo "run_env_as_user($proot_args,$@)"
}
function test_help(){
assertCommandSuccess cli -h
assertEquals "usage" "$(cat $STDOUTF)"
assertCommandSuccess cli --help
assertEquals "usage" "$(cat $STDOUTF)"
}
function test_version(){
assertCommandSuccess cli -v
assertEquals "version" "$(cat $STDOUTF)"
assertCommandSuccess cli --version
assertEquals "version" "$(cat $STDOUTF)"
}
function test_build_image_env(){
assertCommandSuccess cli -b
assertEquals "build_image_env(false,false)" "$(cat $STDOUTF)"
assertCommandSuccess cli --build-image
assertEquals "build_image_env(false,false)" "$(cat $STDOUTF)"
assertCommandSuccess cli -b -s
assertEquals "build_image_env(false,true)" "$(cat $STDOUTF)"
assertCommandSuccess cli -b -n
assertEquals "build_image_env(true,false)" "$(cat $STDOUTF)"
assertCommandSuccess cli -b -n -s
assertEquals "build_image_env(true,true)" "$(cat $STDOUTF)"
assertCommandSuccess cli --build-image --disable-validation --skip-root-tests
assertEquals "build_image_env(true,true)" "$(cat $STDOUTF)"
}
function test_check_env(){
assertCommandSuccess cli -c myscript
assertEquals "check_env(${JUNEST_HOME},myscript,false)" "$(cat $STDOUTF)"
assertCommandSuccess cli --check myscript
assertEquals "check_env(${JUNEST_HOME},myscript,false)" "$(cat $STDOUTF)"
assertCommandSuccess cli -c myscript -s
assertEquals "check_env(${JUNEST_HOME},myscript,true)" "$(cat $STDOUTF)"
assertCommandSuccess cli --check myscript --skip-root-tests
assertEquals "check_env(${JUNEST_HOME},myscript,true)" "$(cat $STDOUTF)"
}
function test_delete_env(){
assertCommandSuccess cli -d
assertEquals "delete_env" "$(cat $STDOUTF)"
assertCommandSuccess cli --delete
assertEquals "delete_env" "$(cat $STDOUTF)"
}
function test_setup_env_from_file(){
is_env_installed(){
return 1
}
assertCommandSuccess cli -i myimage
assertEquals "$(echo -e "setup_env_from_file(myimage)\nrun_env_as_user(,)")" "$(cat $STDOUTF)"
assertCommandSuccess cli --setup-from-file myimage
assertEquals "$(echo -e "setup_env_from_file(myimage)\nrun_env_as_user(,)")" "$(cat $STDOUTF)"
is_env_installed(){
return 0
}
assertCommandFail cli -i myimage
}
function test_setup_env(){
is_env_installed(){
return 1
}
assertCommandSuccess cli -a arm
assertEquals "$(echo -e "setup_env(arm)\nrun_env_as_user(,)")" "$(cat $STDOUTF)"
assertCommandSuccess cli --arch arm
assertEquals "$(echo -e "setup_env(arm)\nrun_env_as_user(,)")" "$(cat $STDOUTF)"
assertCommandSuccess cli
assertEquals "$(echo -e "setup_env()\nrun_env_as_user(,)")" "$(cat $STDOUTF)"
is_env_installed(){
return 0
}
assertCommandFail cli -a arm
}
function test_run_env_as_fakeroot(){
assertCommandSuccess cli -f
assertEquals "run_env_as_fakeroot(,)" "$(cat $STDOUTF)"
assertCommandSuccess cli --fakeroot
assertEquals "run_env_as_fakeroot(,)" "$(cat $STDOUTF)"
assertCommandSuccess cli -f -p "-b arg"
assertEquals "run_env_as_fakeroot(-b arg,)" "$(cat $STDOUTF)"
assertCommandSuccess cli -f -p "-b arg" -- command -kv
assertEquals "run_env_as_fakeroot(-b arg,command -kv)" "$(cat $STDOUTF)"
assertCommandSuccess cli -f command --as
assertEquals "run_env_as_fakeroot(,command --as)" "$(cat $STDOUTF)"
assertCommandFail cli -a "myarch" -f command --as
}
function test_run_env_as_user(){
assertCommandSuccess cli
assertEquals "run_env_as_user(,)" "$(cat $STDOUTF)"
assertCommandSuccess cli -p "-b arg"
assertEquals "run_env_as_user(-b arg,)" "$(cat $STDOUTF)"
assertCommandSuccess cli -p "-b arg" -- command -ll
assertEquals "run_env_as_user(-b arg,command -ll)" "$(cat $STDOUTF)"
assertCommandSuccess cli command -ls
assertEquals "run_env_as_user(,command -ls)" "$(cat $STDOUTF)"
assertCommandFail cli -a "myarch" -- command -ls
}
function test_run_env_as_root(){
assertCommandSuccess cli -r
assertEquals "run_env_as_root " "$(cat $STDOUTF)"
assertCommandSuccess cli -r command
assertEquals "run_env_as_root command" "$(cat $STDOUTF)"
}
function test_check_cli(){
assertCommandFail cli -b -h
assertCommandFail cli -b -c
assertCommandFail cli -d -s
assertCommandFail cli -n -v
assertCommandFail cli -d -r
assertCommandFail cli -h -f
assertCommandFail cli -v -i fsd
assertCommandFail cli -f -r
assertCommandFail cli -p args -v
assertCommandFail cli -a arch -v
assertCommandFail cli -d args
}
source $(dirname $0)/../utils/shunit2

View file

@ -20,10 +20,20 @@ function oneTimeTearDown(){
}
function setUp(){
ld_exec() {
ld_exec_mock() {
echo "ld_exec $@"
}
LD_EXEC=ld_exec
ld_exec_mock_false() {
echo "ld_exec $@"
return 1
}
LD_EXEC=ld_exec_mock
unshare_mock() {
echo "unshare $@"
}
UNSHARE=unshare_mock
}
function test_ln(){
@ -100,17 +110,40 @@ function test_mkdir(){
MKDIR=false LD_EXEC=false assertCommandFail mkdir_cmd -p new_dir/new_dir
}
function test_zgrep(){
ZGREP=echo assertCommandSuccess zgrep_cmd new_file
assertEquals "new_file" "$(cat $STDOUTF)"
mkdir -p ${JUNEST_HOME}/usr/bin
touch ${JUNEST_HOME}/usr/bin/false
chmod +x ${JUNEST_HOME}/usr/bin/false
echo -e "#!/bin/bash\necho zgrep" > ${JUNEST_HOME}/usr/bin/false
ZGREP=false assertCommandSuccess zgrep_cmd new_file
assertEquals "zgrep" "$(cat $STDOUTF)"
echo -e "#!/bin/bash\nexit 1" > ${JUNEST_HOME}/usr/bin/false
ZGREP=false assertCommandFail zgrep_cmd new_file
}
function test_unshare(){
assertCommandSuccess unshare_cmd new_program
assertEquals "$(echo -e "ld_exec ${JUNEST_HOME}/usr/bin/$UNSHARE --user /bin/sh -c :\nld_exec ${JUNEST_HOME}/usr/bin/$UNSHARE new_program")" "$(cat $STDOUTF)"
LD_EXEC=ld_exec_mock_false assertCommandSuccess unshare_cmd new_program
assertEquals "$(echo -e "ld_exec ${JUNEST_HOME}/usr/bin/unshare_mock --user /bin/sh -c :\nunshare --user /bin/sh -c :\nunshare new_program")" "$(cat $STDOUTF)"
UNSHARE=false LD_EXEC=false assertCommandFail unshare_cmd new_program
}
function test_chroot(){
CHROOT=echo assertCommandSuccess chroot_cmd root
CLASSIC_CHROOT=echo assertCommandSuccess chroot_cmd root
assertEquals "root" "$(cat $STDOUTF)"
CHROOT=false CLASSIC_CHROOT=echo assertCommandSuccess chroot_cmd root
assertEquals "root" "$(cat $STDOUTF)"
CLASSIC_CHROOT=false assertCommandSuccess chroot_cmd root
assertEquals "ld_exec $JUNEST_HOME/usr/bin/false root" "$(cat $STDOUTF)"
CHROOT=false CLASSIC_CHROOT=false assertCommandSuccess chroot_cmd root
assertEquals "ld_exec $JUNEST_HOME/usr/bin/chroot root" "$(cat $STDOUTF)"
CHROOT=false CLASSIC_CHROOT=false LD_EXEC=false assertCommandFail chroot_cmd root
CLASSIC_CHROOT=false LD_EXEC=false assertCommandFail chroot_cmd root
}
function test_proot_cmd_compat(){
@ -143,7 +176,7 @@ function test_copy_passwd_and_group(){
getent_cmd_mock() {
echo $@
}
GETENT=getent_cmd_mock assertCommandSuccess _copy_passwd_and_group
GETENT=getent_cmd_mock assertCommandSuccess copy_passwd_and_group
assertEquals "$(echo -e "passwd\npasswd $USER")" "$(cat $JUNEST_HOME/etc/passwd)"
assertEquals "group" "$(cat $JUNEST_HOME/etc/group)"
}
@ -152,20 +185,31 @@ function test_copy_passwd_and_group_fallback(){
cp_cmd_mock() {
echo $@
}
CP=cp_cmd_mock GETENT=false LD_EXEC=false assertCommandSuccess _copy_passwd_and_group
CP=cp_cmd_mock GETENT=false LD_EXEC=false assertCommandSuccess copy_passwd_and_group
assertEquals "$(echo -e "/etc/passwd $JUNEST_HOME//etc/passwd\n/etc/group $JUNEST_HOME//etc/group")" "$(cat $STDOUTF)"
}
function test_copy_passwd_and_group_failure(){
CP=false GETENT=false LD_EXEC=false assertCommandFailOnStatus 1 _copy_passwd_and_group
CP=false GETENT=false LD_EXEC=false assertCommandFailOnStatus 1 copy_passwd_and_group
}
function test_nested_env(){
JUNEST_ENV=1 assertCommandFailOnStatus 106 bash -c "source $JUNEST_ROOT/lib/utils/utils.sh; source $JUNEST_ROOT/lib/core/common.sh"
JUNEST_ENV=1 assertCommandFailOnStatus 106 check_nested_env
}
function test_nested_env_not_set_variable(){
JUNEST_ENV=aaa assertCommandFailOnStatus 107 bash -c "source $JUNEST_ROOT/lib/utils/utils.sh; source $JUNEST_ROOT/lib/core/common.sh"
JUNEST_ENV=aaa assertCommandFailOnStatus 107 check_nested_env
}
function test_check_same_arch_not_same(){
echo "JUNEST_ARCH=XXX" > ${JUNEST_HOME}/etc/junest/info
assertCommandFailOnStatus 104 check_same_arch
}
function test_check_same_arch(){
echo "JUNEST_ARCH=$ARCH" > ${JUNEST_HOME}/etc/junest/info
assertCommandSuccess check_same_arch
}
source $JUNEST_ROOT/tests/utils/shunit2

161
tests/unit-tests/test-groot.sh Executable file
View file

@ -0,0 +1,161 @@
#!/bin/bash
source "$(dirname $0)/../utils/utils.sh"
JUNEST_BASE="$(readlink -f $(dirname $(readlink -f "$0"))/../..)"
# Disable the exiterr
set +e
function oneTimeSetUp(){
setUpUnitTests
}
function setUp(){
# Attempt to source the files under test to revert variable overrides
source $JUNEST_BASE/bin/groot -h &> /dev/null
set +e
cwdSetUp
mkdir -p chrootdir
init_mocks
}
function tearDown(){
cwdTearDown
}
## Mock functions ##
function init_mocks() {
function usage(){
echo "usage"
}
function is_user_root() {
return 0
}
function chroot() {
echo "chroot($@)"
}
function mountpoint() {
echo "mountpoint($@)"
# As default suppose the mountpoint does not exist
return 1
}
function mount() {
echo "mount($@)"
}
function umount() {
echo "umount($@)"
}
function check_and_trap() {
echo "check_and_trap($@)"
}
}
function test_help(){
assertCommandSuccess main -h
assertEquals "usage" "$(cat $STDOUTF)"
assertCommandSuccess main --help
assertEquals "usage" "$(cat $STDOUTF)"
}
function test_version(){
assertCommandSuccess main -V
assertEquals "$NAME $(cat $JUNEST_BASE/VERSION)" "$(cat $STDOUTF)"
assertCommandSuccess main --version
assertEquals "$NAME $(cat $JUNEST_BASE/VERSION)" "$(cat $STDOUTF)"
}
function test_groot_no_root(){
is_user_root() {
return 1
}
assertCommandFailOnStatus $NO_ROOT_PRIVILEGES main
}
function test_groot_no_directory(){
assertCommandFailOnStatus $NOT_EXISTING_FILE main no-directory
}
function test_groot_mountpoint_exist(){
mountpoint_mock() {
echo "mountpoint($@)"
}
MOUNTPOINT=mountpoint_mock
assertCommandSuccess main chrootdir
assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nchroot(chrootdir)")" "$(cat $STDOUTF)"
}
function test_groot_mountpoint_does_not_exist(){
assertCommandSuccess main chrootdir
assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nchroot(chrootdir)")" "$(cat $STDOUTF)"
}
function test_groot_with_bind(){
assertCommandSuccess main -b /tmp chrootdir
[[ -d chrootdir/tmp ]]
assertEquals 0 $?
assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nmount(--rbind /tmp chrootdir/tmp)\nchroot(chrootdir)")" "$(cat $STDOUTF)"
}
function test_groot_with_bind_file(){
touch file_src
assertCommandSuccess main -b ${PWD}/file_src:/file_src chrootdir
[[ -f chrootdir/file_src ]]
assertEquals 0 $?
assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nmount(--rbind ${PWD}/file_src chrootdir/file_src)\nchroot(chrootdir)")" "$(cat $STDOUTF)"
}
function test_groot_with_bind_not_existing_node(){
assertCommandFailOnStatus $NOT_EXISTING_FILE main -b ${PWD}/file_src:/file_src chrootdir
assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)")" "$(cat $STDOUTF)"
}
function test_groot_with_bind_not_absolute_path_node(){
touch file_src
assertCommandFailOnStatus $NOT_ABSOLUTE_PATH main -b file_src:/file_src chrootdir
assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)")" "$(cat $STDOUTF)"
}
function test_groot_with_bind_guest_host(){
assertCommandSuccess main -b /tmp:/home/tmp chrootdir
[[ -d chrootdir/home/tmp ]]
assertEquals 0 $?
assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nmount(--rbind /tmp chrootdir/home/tmp)\nchroot(chrootdir)")" "$(cat $STDOUTF)"
}
function test_groot_with_multiple_bind(){
assertCommandSuccess main -b /tmp:/home/tmp -b /dev chrootdir
[[ -d chrootdir/home/tmp ]]
assertEquals 0 $?
[[ -d chrootdir/dev ]]
assertEquals 0 $?
assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nmount(--rbind /tmp chrootdir/home/tmp)\nmount(--rbind /dev chrootdir/dev)\nchroot(chrootdir)")" "$(cat $STDOUTF)"
}
function test_groot_with_command(){
assertCommandSuccess main chrootdir ls -la -h
assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nchroot(chrootdir ls -la -h)")" "$(cat $STDOUTF)"
}
function test_groot_with_bind_and_command(){
assertCommandSuccess main -b /tmp:/home/tmp -b /dev chrootdir ls -la -h
[[ -d chrootdir/home/tmp ]]
assertEquals 0 $?
[[ -d chrootdir/dev ]]
assertEquals 0 $?
assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nmount(--rbind /tmp chrootdir/home/tmp)\nmount(--rbind /dev chrootdir/dev)\nchroot(chrootdir ls -la -h)")" "$(cat $STDOUTF)"
}
function test_groot_with_bind_no_umount(){
assertCommandSuccess main -n chrootdir
assertEquals "$(echo -e "mountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nchroot(chrootdir)")" "$(cat $STDOUTF)"
}
function test_groot_with_chroot_teardown(){
echo -e "1 /home/mychroot/dev\n1 /home/mychroot/proc/fs1\n1 /home/mychroot\n1 /home/mychroot-no/dev\n1 /home/mychroot/dev/shm\n1 /home/mychroot/proc\n" > ./mounts
MOUNTS_FILE=./mounts
CHROOTDIR=/home/mychroot assertCommandSuccess chroot_teardown
assertEquals "$(echo -e "umount(/home/mychroot/proc/fs1)
umount(/home/mychroot/proc)
umount(/home/mychroot/dev/shm)
umount(/home/mychroot/dev)
umount(/home/mychroot)")" "$(cat $STDOUTF)"
}
function test_groot_with_chroot_teardown_with_trailing_slash(){
echo -e "1 /home/mychroot/dev\n1 /home/mychroot/proc/fs1\n1 /home/mychroot\n1 /home/mychroot-no/dev\n1 /home/mychroot/dev/shm\n1 /home/mychroot/proc\n" > ./mounts
MOUNTS_FILE=./mounts
CHROOTDIR=/home/mychroot assertCommandSuccess chroot_teardown
assertEquals "$(echo -e "umount(/home/mychroot/proc/fs1)
umount(/home/mychroot/proc)
umount(/home/mychroot/dev/shm)
umount(/home/mychroot/dev)
umount(/home/mychroot)")" "$(cat $STDOUTF)"
}
source $(dirname $0)/../utils/shunit2

189
tests/unit-tests/test-junest.sh Executable file
View file

@ -0,0 +1,189 @@
#!/bin/bash
source "$(dirname $0)/../utils/utils.sh"
JUNEST_BASE="$(dirname $0)/../.."
source $JUNEST_BASE/bin/junest -h &> /dev/null
# Disable the exiterr
set +e
function oneTimeSetUp(){
setUpUnitTests
}
function setUp(){
function is_env_installed(){
return 0
}
}
## Mock functions ##
function usage(){
echo "usage"
}
function version(){
echo "version"
}
function build_image_env(){
local disable_validation=$1
echo "build_image_env($disable_validation)"
}
function delete_env(){
echo "delete_env"
}
function setup_env_from_file(){
echo "setup_env_from_file($1)"
}
function setup_env(){
echo "setup_env($1)"
}
function run_env_as_fakeroot(){
local backend_args="$1"
shift
echo "run_env_as_fakeroot($backend_args,$@)"
}
function run_env_as_groot(){
echo "run_env_as_groot $@"
}
function run_env_as_chroot(){
echo "run_env_as_chroot $@"
}
function run_env_as_user(){
local backend_args="$1"
shift
echo "run_env_as_user($backend_args,$@)"
}
function run_env_with_namespace(){
local backend_args="$1"
shift
echo "run_env_with_namespace($backend_args,$@)"
}
function test_help(){
assertCommandSuccess main -h
assertEquals "usage" "$(cat $STDOUTF)"
assertCommandSuccess main --help
assertEquals "usage" "$(cat $STDOUTF)"
}
function test_version(){
assertCommandSuccess main -V
assertEquals "version" "$(cat $STDOUTF)"
assertCommandSuccess main --version
assertEquals "version" "$(cat $STDOUTF)"
}
function test_build_image_env(){
assertCommandSuccess main -b
assertEquals "build_image_env(false)" "$(cat $STDOUTF)"
assertCommandSuccess main --build-image
assertEquals "build_image_env(false)" "$(cat $STDOUTF)"
assertCommandSuccess main -b -n
assertEquals "build_image_env(true)" "$(cat $STDOUTF)"
assertCommandSuccess main --build-image --disable-validation
assertEquals "build_image_env(true)" "$(cat $STDOUTF)"
}
function test_delete_env(){
assertCommandSuccess main -d
assertEquals "delete_env" "$(cat $STDOUTF)"
assertCommandSuccess main --delete
assertEquals "delete_env" "$(cat $STDOUTF)"
}
function test_setup_env_from_file(){
is_env_installed(){
return 1
}
assertCommandSuccess main -i myimage
assertEquals "$(echo -e "setup_env_from_file(myimage)\nrun_env_as_user(,)")" "$(cat $STDOUTF)"
assertCommandSuccess main --setup-from-file myimage
assertEquals "$(echo -e "setup_env_from_file(myimage)\nrun_env_as_user(,)")" "$(cat $STDOUTF)"
is_env_installed(){
return 0
}
assertCommandFail main -i myimage
}
function test_setup_env(){
is_env_installed(){
return 1
}
assertCommandSuccess main -a arm
assertEquals "$(echo -e "setup_env(arm)\nrun_env_as_user(,)")" "$(cat $STDOUTF)"
assertCommandSuccess main --arch arm
assertEquals "$(echo -e "setup_env(arm)\nrun_env_as_user(,)")" "$(cat $STDOUTF)"
assertCommandSuccess main
assertEquals "$(echo -e "setup_env()\nrun_env_as_user(,)")" "$(cat $STDOUTF)"
is_env_installed(){
return 0
}
assertCommandFail main -a arm
}
function test_run_env_as_fakeroot(){
assertCommandSuccess main -f
assertEquals "run_env_as_fakeroot(,)" "$(cat $STDOUTF)"
assertCommandSuccess main --fakeroot
assertEquals "run_env_as_fakeroot(,)" "$(cat $STDOUTF)"
assertCommandSuccess main -f -p "-b arg"
assertEquals "run_env_as_fakeroot(-b arg,)" "$(cat $STDOUTF)"
assertCommandSuccess main -f -p "-b arg" -- command -kv
assertEquals "run_env_as_fakeroot(-b arg,command -kv)" "$(cat $STDOUTF)"
assertCommandSuccess main -f command --as
assertEquals "run_env_as_fakeroot(,command --as)" "$(cat $STDOUTF)"
assertCommandFail main -a "myarch" -f command --as
}
function test_run_env_as_user(){
assertCommandSuccess main
assertEquals "run_env_as_user(,)" "$(cat $STDOUTF)"
assertCommandSuccess main -p "-b arg"
assertEquals "run_env_as_user(-b arg,)" "$(cat $STDOUTF)"
assertCommandSuccess main -p "-b arg" -- command -ll
assertEquals "run_env_as_user(-b arg,command -ll)" "$(cat $STDOUTF)"
assertCommandSuccess main command -ls
assertEquals "run_env_as_user(,command -ls)" "$(cat $STDOUTF)"
assertCommandFail main -a "myarch" -- command -ls
}
function test_run_env_as_groot(){
assertCommandSuccess main -g
assertEquals "run_env_as_groot " "$(cat $STDOUTF)"
assertCommandSuccess main -g command
assertEquals "run_env_as_groot command" "$(cat $STDOUTF)"
}
function test_run_env_as_chroot(){
assertCommandSuccess main -r
assertEquals "run_env_as_chroot " "$(cat $STDOUTF)"
assertCommandSuccess main -r command
assertEquals "run_env_as_chroot command" "$(cat $STDOUTF)"
}
function test_run_env_with_namespace(){
assertCommandSuccess main -u -f
assertEquals "run_env_with_namespace(,)" "$(cat $STDOUTF)"
assertCommandSuccess main --namespace --fakeroot
assertEquals "run_env_with_namespace(,)" "$(cat $STDOUTF)"
assertCommandSuccess main -u -f -p "-b arg"
assertEquals "run_env_with_namespace(-b arg,)" "$(cat $STDOUTF)"
assertCommandSuccess main -u -f -p "-b arg" -- command -kv
assertEquals "run_env_with_namespace(-b arg,command -kv)" "$(cat $STDOUTF)"
assertCommandSuccess main -u -f command --as
assertEquals "run_env_with_namespace(,command --as)" "$(cat $STDOUTF)"
}
function test_check_cli(){
assertCommandFail main -b -h
assertCommandFail main -b -c
assertCommandFail main -d -s
assertCommandFail main -n -v
assertCommandFail main -d -r
assertCommandFail main -h -f
assertCommandFail main -v -i fsd
assertCommandFail main -f -r
assertCommandFail main -p args -v
assertCommandFail main -a arch -v
assertCommandFail main -d args
}
source $(dirname $0)/../utils/shunit2

View file

@ -0,0 +1,113 @@
#!/bin/bash
JUNEST_ROOT=$(readlink -f $(dirname $0)/../..)
source "$JUNEST_ROOT/tests/utils/utils.sh"
source "$JUNEST_ROOT/lib/utils/utils.sh"
# Disable the exiterr
set +e
function oneTimeSetUp(){
setUpUnitTests
}
## Mock functions ##
function init_mocks() {
function unshare_cmd(){
echo "unshare $@"
}
}
function setUp(){
cwdSetUp
junestSetUp
# Attempt to source the files under test to revert variable
# overrides (i.e. SH variable)
source "$JUNEST_ROOT/lib/core/common.sh"
source "$JUNEST_ROOT/lib/core/namespace.sh"
set +e
init_mocks
}
function tearDown(){
junestTearDown
cwdTearDown
}
function _test_copy_common_files() {
[[ -e /etc/hosts ]] && assertEquals "$(cat /etc/hosts)" "$(cat ${JUNEST_HOME}/etc/hosts)"
[[ -e /etc/host.conf ]] && assertEquals "$(cat /etc/host.conf)" "$(cat ${JUNEST_HOME}/etc/host.conf)"
[[ -e /etc/nsswitch.conf ]] && assertEquals "$(cat /etc/nsswitch.conf)" "$(cat ${JUNEST_HOME}/etc/nsswitch.conf)"
[[ -e /etc/resolv.conf ]] && assertEquals "$(cat /etc/resolv.conf)" "$(cat ${JUNEST_HOME}/etc/resolv.conf)"
}
function _test_copy_remaining_files() {
[[ -e /etc/hosts.equiv ]] && assertEquals "$(cat /etc/hosts.equiv)" "$(cat ${JUNEST_HOME}/etc/hosts.equiv)"
[[ -e /etc/netgroup ]] && assertEquals "$(cat /etc/netgroup)" "$(cat ${JUNEST_HOME}/etc/netgroup)"
[[ -e /etc/networks ]] && assertEquals "$(cat /etc/networks)" "$(cat ${JUNEST_HOME}/etc/networks)"
[[ -e ${JUNEST_HOME}/etc/passwd ]]
assertEquals 0 $?
[[ -e ${JUNEST_HOME}/etc/group ]]
assertEquals 0 $?
}
function test_is_user_namespace_enabled_no_config_file(){
CONFIG_PROC_FILE="blah"
CONFIG_BOOT_FILE="blah"
assertCommandFailOnStatus $NOT_EXISTING_FILE _is_user_namespace_enabled
}
function test_is_user_namespace_enabled_no_config(){
touch config
gzip config
CONFIG_PROC_FILE="config.gz"
CONFIG_BOOT_FILE="blah"
assertCommandFailOnStatus $NO_CONFIG_FOUND _is_user_namespace_enabled
}
function test_is_user_namespace_enabled_with_config(){
echo "CONFIG_USER_NS=y" > config
gzip config
CONFIG_PROC_FILE="config.gz"
CONFIG_BOOT_FILE="blah"
assertCommandSuccess _is_user_namespace_enabled
}
function test_run_env_with_namespace() {
assertCommandSuccess run_env_with_namespace "" ""
assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login" "$(cat $STDOUTF)"
_test_copy_common_files
_test_copy_remaining_files
}
function test_run_env_with_namespace_with_bindings() {
assertCommandSuccess run_env_with_namespace "-b /usr -b /lib:/tmp/lib" ""
assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev -b /usr -b /lib:/tmp/lib $JUNEST_HOME /bin/sh --login" "$(cat $STDOUTF)"
_test_copy_common_files
_test_copy_remaining_files
}
function test_run_env_with_namespace_with_command() {
assertCommandSuccess run_env_with_namespace "" "ls -la"
assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c \"ls -la\"" "$(cat $STDOUTF)"
_test_copy_common_files
_test_copy_remaining_files
}
function test_run_env_with_namespace_with_bindings_and_command() {
assertCommandSuccess run_env_with_namespace "-b /usr -b /lib:/tmp/lib" "ls -la"
assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev -b /usr -b /lib:/tmp/lib $JUNEST_HOME /bin/sh --login -c \"ls -la\"" "$(cat $STDOUTF)"
_test_copy_common_files
_test_copy_remaining_files
}
source $JUNEST_ROOT/tests/utils/shunit2

View file

@ -29,6 +29,24 @@ function tearDown(){
cwdTearDown
}
function _test_copy_common_files() {
[[ -e /etc/hosts ]] && assertEquals "$(cat /etc/hosts)" "$(cat ${JUNEST_HOME}/etc/hosts)"
[[ -e /etc/host.conf ]] && assertEquals "$(cat /etc/host.conf)" "$(cat ${JUNEST_HOME}/etc/host.conf)"
[[ -e /etc/nsswitch.conf ]] && assertEquals "$(cat /etc/nsswitch.conf)" "$(cat ${JUNEST_HOME}/etc/nsswitch.conf)"
[[ -e /etc/resolv.conf ]] && assertEquals "$(cat /etc/resolv.conf)" "$(cat ${JUNEST_HOME}/etc/resolv.conf)"
}
function _test_copy_remaining_files() {
[[ -e /etc/hosts.equiv ]] && assertEquals "$(cat /etc/hosts.equiv)" "$(cat ${JUNEST_HOME}/etc/hosts.equiv)"
[[ -e /etc/netgroup ]] && assertEquals "$(cat /etc/netgroup)" "$(cat ${JUNEST_HOME}/etc/netgroup)"
[[ -e /etc/networks ]] && assertEquals "$(cat /etc/networks)" "$(cat ${JUNEST_HOME}/etc/networks)"
[[ -e ${JUNEST_HOME}/etc/passwd ]]
assertEquals 0 $?
[[ -e ${JUNEST_HOME}/etc/group ]]
assertEquals 0 $?
}
function test_run_env_as_user(){
_run_env_with_qemu() {
echo $@
@ -40,19 +58,8 @@ function test_run_env_as_user(){
assertCommandSuccess run_env_as_user "-k 3.10"
assertEquals "-b $HOME -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10" "$(cat $STDOUTF)"
[[ -e /etc/hosts ]] && assertEquals "$(cat /etc/hosts)" "$(cat ${JUNEST_HOME}/etc/hosts)"
[[ -e /etc/host.conf ]] && assertEquals "$(cat /etc/host.conf)" "$(cat ${JUNEST_HOME}/etc/host.conf)"
[[ -e /etc/nsswitch.conf ]] && assertEquals "$(cat /etc/nsswitch.conf)" "$(cat ${JUNEST_HOME}/etc/nsswitch.conf)"
[[ -e /etc/resolv.conf ]] && assertEquals "$(cat /etc/resolv.conf)" "$(cat ${JUNEST_HOME}/etc/resolv.conf)"
[[ -e /etc/hosts.equiv ]] && assertEquals "$(cat /etc/hosts.equiv)" "$(cat ${JUNEST_HOME}/etc/hosts.equiv)"
[[ -e /etc/netgroup ]] && assertEquals "$(cat /etc/netgroup)" "$(cat ${JUNEST_HOME}/etc/netgroup)"
[[ -e /etc/passwd ]]
assertEquals 0 $?
[[ -e /etc/group ]]
assertEquals 0 $?
_test_copy_common_files
_test_copy_remaining_files
}
function test_run_env_as_fakeroot(){
@ -66,10 +73,7 @@ function test_run_env_as_fakeroot(){
assertCommandSuccess run_env_as_fakeroot "-k 3.10"
assertEquals "-0 -b ${HOME} -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10" "$(cat $STDOUTF)"
[[ -e /etc/hosts ]] && assertEquals "$(cat /etc/hosts)" "$(cat ${JUNEST_HOME}/etc/hosts)"
[[ -e /etc/host.conf ]] && assertEquals "$(cat /etc/host.conf)" "$(cat ${JUNEST_HOME}/etc/host.conf)"
[[ -e /etc/nsswitch.conf ]] && assertEquals "$(cat /etc/nsswitch.conf)" "$(cat ${JUNEST_HOME}/etc/nsswitch.conf)"
[[ -e /etc/resolv.conf ]] && assertEquals "$(cat /etc/resolv.conf)" "$(cat ${JUNEST_HOME}/etc/resolv.conf)"
_test_copy_common_files
}
function test_run_env_with_quotes(){

View file

@ -79,6 +79,28 @@ function test_ask_wrong_default_answer() {
assertEquals 33 $?
}
function test_check_and_trap_fail() {
trap echo EXIT
trap ls QUIT
assertCommandFailOnStatus 1 check_and_trap 'pwd' EXIT QUIT
}
function test_check_and_trap() {
trap - EXIT QUIT
assertCommandSuccess check_and_trap 'echo' EXIT QUIT
}
function test_check_and_force_trap_fail() {
trap echo EXIT
trap ls QUIT
assertCommandSuccess check_and_force_trap 'echo' EXIT QUIT
}
function test_check_and_force_trap() {
trap - EXIT QUIT
assertCommandSuccess check_and_force_trap 'echo' EXIT QUIT
}
function test_insert_quotes_on_spaces(){
assertCommandSuccess insert_quotes_on_spaces this is "a test"
assertEquals "this is \"a test\"" "$(cat $STDOUTF)"

View file

@ -1,3 +1,4 @@
OLD_CWD=${PWD}
function cwdSetUp(){
ORIGIN_CWD=$(TMPDIR=/tmp mktemp -d -t junest-cwd.XXXXXXXXXX)
cd $ORIGIN_CWD
@ -5,6 +6,7 @@ function cwdSetUp(){
function cwdTearDown(){
rm -rf $ORIGIN_CWD
cd $OLD_CWD
}
function junestSetUp(){
@ -12,15 +14,12 @@ function junestSetUp(){
mkdir -p ${JUNEST_HOME}/etc/junest
echo "JUNEST_ARCH=x86_64" > ${JUNEST_HOME}/etc/junest/info
mkdir -p ${JUNEST_HOME}/etc/ca-certificates
trap - QUIT EXIT ABRT KILL TERM INT
trap "rm -rf ${JUNEST_HOME}" EXIT QUIT ABRT KILL TERM INT
}
function junestTearDown(){
# the CA directories are read only and can be deleted only by changing the mod
[ -d ${JUNEST_HOME}/etc/ca-certificates ] && chmod -R +w ${JUNEST_HOME}/etc/ca-certificates
rm -rf $JUNEST_HOME
trap - QUIT EXIT ABRT KILL TERM INT
unset JUNEST_HOME
}