diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 8efae1f..0000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,2 +0,0 @@ -github: fsquillace -custom: https://github.com/fsquillace/junest/blob/master/README.md#donating diff --git a/.gitignore b/.gitignore index b47a408..1377554 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1 @@ *.swp -*pkg.tar.* -*.tar.gz -*.SRCINFO diff --git a/.travis.yml b/.travis.yml index fab23ba..e43ddc3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,62 +1,24 @@ +language: bash + sudo: required -os: linux - -cache: - directories: - - ~/.ccache - - ~/.pkg-cache - -services: -- docker env: - matrix: - TRAVIS_BASH_VERSION="4.0" before_install: - - ./ci/install-bash.sh "$TRAVIS_BASH_VERSION" - - sudo apt-get update - - sudo apt-get -y install awscli + - ./tests/integ-tests/install-bash.sh "$TRAVIS_BASH_VERSION" install: - PATH=$PWD/bin:$PATH + - junest -- echo "Installing JuNest (\$(uname -m))" + - JUNEST_HOME=~/.junest-arm junest -a arm -- echo "Installing JuNest (\$(uname -m))" + # TODO: Remember to enable x86 tests when fixed + #- JUNEST_HOME=~/.junest-x86 junest -a x86 -- echo "Installing JuNest (\$(uname -m))" script: - ####################### - # Unit Tests - ####################### - bash --version - - bash ./tests/checkstyle/checkstyle.sh - - bash ./tests/unit-tests/unit-tests.sh - - shellcheck bin/junest lib/**/*.sh ci/*.sh tests/**/*.sh - - # ARM with qemu does seem to work properly. Disabling integ tests for ARM for now. - #- export JUNEST_HOME=~/.junest-arm - #- junest setup --arch arm - #- junest proot --fakeroot -- echo "Installing JuNest (\$(uname -m))" - #- junest proot --fakeroot -- ${PWD}/lib/checks/check.sh --skip-aur-tests - #- junest proot -- ${PWD}/lib/checks/check.sh --skip-aur-tests --use-sudo - #- yes | junest setup --delete - - ####################### - # Build and validation - ####################### - - echo "$DOCKER_PASSWORD" | docker login --username "$DOCKER_USERNAME" --password-stdin - - docker run --rm -v "$(pwd):/build" -v ~/.ccache:/home/travis/.ccache -v ~/.pkg-cache:/var/cache/pacman/pkg --privileged archlinux:latest bash /build/ci/build_image.sh - - - "echo pacman pkg cache size: $(du -h ~/.pkg-cache|cut -f1) in $(ls ~/.pkg-cache|wc -l) files" - - ls -l - # Test the newly created JuNest image against Ubuntu host - - export JUNEST_HOME=~/.junest - - junest setup -i junest-x86_64.tar.gz - # TODO The check does not work at the moment: https://app.travis-ci.com/github/fsquillace/junest/builds/271706037 - # Disabling it in order to avoid having stale version of junest images. - - ${PWD}/lib/checks/check_all.sh - - yes | junest setup --delete - - -after_success: - ####################### - # Deploy and validation - ####################### - - ./ci/deploy.sh ./junest-x86_64.tar.gz + - ./tests/unit-tests/unit-tests.sh + - junest --check ./bin/junest + - yes | junest --delete + - JUNEST_HOME=~/.junest-arm junest --check ./bin/junest --skip-root-tests + - yes | JUNEST_HOME=~/.junest-arm junest --delete diff --git a/README.md b/README.md index 1268f8b..c7764fe 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,6 @@ JuNest ====== - -> [!IMPORTANT] -> Starting from Ubuntu 23.10+, [unprivileged user namespaces has been restricted](https://ubuntu.com/blog/ubuntu-23-10-restricted-unprivileged-user-namespaces). -> If using JuNest within Ubuntu, you may need root privileges in order to enable it. -> Alternatively, you can access JuNest using the `proot` mode as described -> [below](#Proot-based). - -The lightweight Arch Linux based distro that runs, without root privileges, on top of any other Linux distro. +The Arch Linux based distro that runs upon any Linux distros without root access.

=4.0)](https://www.gnu.org/software/bash/) - [GNU coreutils](https://www.gnu.org/software/coreutils/) -## Installation from git repository ## +The minimum recommended Linux kernel is 2.6.0+ on x86 32 and 64 bit and ARM architectures. + +## Method one (Recommended) ## Just clone the JuNest repo somewhere (for example in ~/.local/share/junest): -```sh -git clone https://github.com/fsquillace/junest.git ~/.local/share/junest -export PATH=~/.local/share/junest/bin:$PATH -``` - -Optionally you want to use the wrappers to run commands -installed in JuNest directly from host: - -```sh -export PATH="$PATH:~/.junest/usr/bin_wrappers" -``` -Update your `~/.bashrc` or `~/.zshrc` to get always the wrappers available. + git clone git://github.com/fsquillace/junest ~/.local/share/junest + export PATH=~/.local/share/junest/bin:$PATH ### 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/packages/junest-git/). -JuNest will be located in `/opt/junest/` +If you are using an Arch Linux system you can, alternatively, install JuNest from the [AUR repository](https://aur.archlinux.org/): -Quickstart -========== + yaourt -S junest-git + export PATH=/opt/junest/bin:$PATH -Setup environment ------------------ +## Method two ## +Alternatively, another installation method would be to directly download the JuNest image and place it to the default directory ~/.junest: -The first operation required is to install the JuNest environment in the -location of your choice via `JUNEST_HOME` environment variable -(it must contain an absolute path) which by -default is `~/.junest`: - -```sh -junest setup -``` - -The script will download the image from the repository and will place it to the default directory `~/.junest`. - -Access to environment ---------------------- - -JuNest uses the Linux namespaces (aka `ns`) as the default backend program. To access via `ns` just type: - -```sh -junest -``` - -You can use the command `sudo` to acquire fakeroot privileges and -install/remove packages. - -Alternatively, you can access fakeroot privileges without using `sudo` all the -time with the `-f` (or `--fakeroot`) option: - -```sh -junest -f -``` - -Another execution mode is via [Proot](https://wiki.archlinux.org/index.php/Proot): - -```sh -junest proot [-f] -``` - -There are multiple backend programs, each with its own pros/cons. -To know more about the JuNest execution modes depending on the backend program -used, see the [Usage](#usage) section below. - -Run JuNest installed programs directly from host OS ---------------------------------------- - -Programs installed within JuNest can be accessible directly from host machine -without entering into a JuNest session -(namely, no need to call `junest` command first). -For instance, supposing the host OS is an Ubuntu distro you can directly -run `pacman` by simply updating the `PATH` variable: - -```sh -export PATH="$PATH:~/.junest/usr/bin_wrappers" -sudoj pacman -S htop -htop -``` - -By default the wrappers use `ns` mode. To use the `ns --fakeroot` you can use the convenient command helper `sudoj`. -For more control on backend modes you can use the `JUNEST_ARGS` environment variable too. -For instance, if you want to run `iftop` with real root privileges: - -``` -sudoj pacman -S iftop -sudo JUNEST_ARGS="groot" iftop -``` - -Bin wrappers can be always recreated (e.g. in case for some reasons they get -corrupted) with: - -``` -junest create-bin-wrappers -f -``` - -Bin wrappers are automatically generated each time they get installed inside JuNest. -This only works for executables located in `/usr/bin` path. -For executables in other locations (say `/usr/mybinpath`) you can only create -wrappers manually by executing the command: - -``` -junest create-bin-wrappers --bin-path /usr/mybinpath -``` - -Obviously, to get access to the corresponding bin wrappers you will need to -update your `PATH` variable accordingly: - -``` -export PATH="$PATH:~/.junest/usr/mybinpath_wrappers" -``` - -Install packages from AUR -------------------------- - -In `ns` mode, you can easily install package from [AUR](https://aur.archlinux.org/) repository -using the already available [`yay`](https://aur.archlinux.org/packages/yay/) -command. In `proot` mode, JuNest does no longer support the building of AUR packages. - -**Remember** that in order to build packages from AUR, `base-devel` package group is required -first: - -```sh -pacman -S base-devel -``` - -JuNest uses a modified version of `sudo` provided by `junest/sudo-fake`. And the original `core/sudo` -package will be ignored **(and must not be installed)** during the installation of `base-devel`. - -Have fun! ---------- - -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). - -Usage -===== -There are three different ways you can run JuNest depending on the backend program you decide to use. - -Linux namespaces based ----------------------- -The [Linux namespaces](http://man7.org/linux/man-pages/man7/namespaces.7.html) -represents the default backend program for JuNest. -The requirements for having Linux namespaces working are: - -1. Kernel starting from Linux 3.8 allows unprivileged processes to create -user and mount namespaces. -1. The Linux kernel distro must have the user namespace enabled. - -In the last years, the majority of GNU/Linux distros have the user namespace -enabled by default. This means that you do not need to have root privileges to -access to the JuNest environment via this method. -This -[wiki](https://github.com/fsquillace/junest/wiki/Linux-distros-with-user-namespace-enabled-by-default) -provides the state of the user namespace on several GNU/Linux distros. - -In order to run JuNest via Linux namespaces: - -- As normal user - Allow to make basic operations or install/remove packages -with `sudo` command: `junest ns` or `junest` -- As fakeroot - Allow to install/remove packages: `junest ns -f` or `junest -f` - -This mode is based on the fantastic -[`bubblewrap`](https://github.com/containers/bubblewrap) command. - -PRoot based ------------ -[Proot](https://wiki.archlinux.org/index.php/Proot) represents a portable -solution which allows unprivileged users to execute programs inside a sandbox -and works well in most of GNU/Linux distros available. - -In order to run JuNest via Proot: - -- As normal user - Allow to make basic operations: `junest proot` - -- As fakeroot - Allow to install/remove packages: `junest proot -f` - -In `proot` mode, the minimum recommended Linux kernel for the host OS is 2.6.32 on x86 (64 bit) -and ARM architectures. It is still possible to run JuNest on lower -2.6.x host OS kernels but errors may appear, and some applications may -crash. For further information, read the [Troubleshooting](#troubleshooting) -section below. - -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`, a small and portable -version of -[arch-chroot](https://wiki.archlinux.org/index.php/Chroot) -wrapper, that allows to bind mount directories specified by the user, such as -`/proc`, `/sys`, `/dev`, `/tmp`, `/run/user/` 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 groot` - -- As root via `chroot` - Allow to have fully root privileges inside JuNest environment (you need to be root for executing this): `junest root` - -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 | -| --- | ---- | ------------------------ | ------------------------ | ------------------- | ----------- | ------- | ---------- | -| **Linux Namespaces** | NO | NO | YES | YES | Poor | YES | Normal user and `fakeroot` | -| **Proot** | YES | NO | YES | NO | YES | YES | Normal user and `fakeroot` | -| **Chroot** | NO | YES | YES | YES | YES | YES | `root` only | + ARCH= + mkdir ~/.junest + curl https://dl.dropboxusercontent.com/u/42449030/junest/junest-${ARCH}.tar.gz | tar -xz -C ~/.junest + export PATH=~/.junest/opt/junest/bin:$PATH Advanced usage ============== + ## Build image ## You can build a new JuNest image from scratch by running the following command: -```sh -junest build [-n] -``` + junest -b [-n] The script will create a directory containing all the essentials -files in order to make JuNest working properly (such as `pacman` and `proot`). -The option `-n` will skip the final validation tests if they are not needed. +files in order to make JuNest working properly (such as pacman, yaourt 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 and the base-devel packages installed. -To change the build directory just use the `JUNEST_TEMPDIR` (by default /tmp). +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). -After creating the image `junest-x86_64.tar.gz` you can install it by running: +After creating the image junest-x86\_64.tar.gz you can install it by running: -```sh -junest setup -i junest-x86_64.tar.gz -``` + junest -i junest-x86_64.tar.gz For more details, you can also take a look at [junest-builder](https://github.com/fsquillace/junest-builder) @@ -327,36 +125,22 @@ Related wiki page: ## Run JuNest using a different architecture via QEMU ## The following command will download the ARM JuNest image and will run QEMU in -case the host OS runs on `x86_64` architecture: +case the host OS runs on either x86\_64 or x86 architectures: -```sh -$> export JUNEST_HOME=~/.junest-arm -$> junest setup -a arm -$> junest proot -- uname -m -armv7l -``` + $> JUNEST_HOME=~/.junest-arm junest -a arm -- uname -m + armv7l ## Bind directories ## -To bind a host directory to a guest location: +To bind a host directory to a guest location, you can use proot arguments: -```sh -junest -b "--bind /home/user/mydata /mnt/mydata" -``` + junest -p "-b /mnt/mydata:/home/user/mydata" -Or using proot arguments: +Check out the proot options with: -```sh -junest proot -b "-b /mnt/mydata:/home/user/mydata" -``` - -The option `-b` to provide options to the backend program will work with PRoot, Namespace and GRoot backend programs. -Check out the backend program options by passing `--help` option: - -```sh -junest [u|g|p] -b "--help" -``` + junest -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 @@ -366,9 +150,7 @@ and the container can only be executed using root privileges. To boot a JuNest container: -```sh -sudo systemd-nspawn -bD ~/.junest -``` + sudo systemd-nspawn -bD ~/.junest Related wiki page: @@ -377,17 +159,30 @@ Related wiki page: 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 +[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 +mount one of the directories), +JuNest automatically tries to fallback to the classic chroot. + ## Automatic fallback for all the dependent host OS executables ## -JuNest attempts first to run the executables in the host OS located in different -positions (`/usr/bin`, `/bin`, `/usr/sbin` and `/sbin`). +JuNest attempt first to run the executables in the host OS located in different +positions (/usr/bin, /bin, /usr/sbin and /sbin). As a fallback it tries to run the same executable if it is available in the JuNest -environment. +image. ## Automatic building of the JuNest images ## -There is a periodic automation build of the JuNest images for `x86_64` arch -only. -The JuNest image for `arm` architecture may not be always up to date because -the build is performed manually. +The JuNest images are built every week so that you can always get the most +updated package versions. ## Static QEMU binaries ## There are static QEMU binaries included in JuNest image that allows to run JuNest @@ -397,35 +192,18 @@ directory. Troubleshooting =============== -For Arch Linux related FAQs take a look at the [General troubleshooting page](https://wiki.archlinux.org/index.php/General_troubleshooting). - ## Cannot use AUR repository ## -> **Q**: Why do I get the following error when I try to install a package? +> **Q**: Why do I get the following error when I try to install a package with yaourt? Cannot find the gzip binary required for compressing man and info pages. > **A**: JuNest comes with a very basic number of packages. -> In order to install AUR packages you need to install the package group `base-devel` first -> that contains all the essential packages for compiling from source code (such as gcc, make, patch, etc): +> In order to install packages using yaourt you may need to install the package group **base-devel** +> that contains all the essential packages for compiling source code (such as gcc, make, patch, etc): #> pacman -S base-devel -> Remember to not install `core/sudo` as it conflicts with `junest/sudo-fake` package. - -## Can't set user and group as root - -> **Q**: In ns mode when installing package I get the following error: - - warning: warning given when extracting /usr/file... (Can't set user=0/group=0 for - /usr/file...) - -> **A**: This is because as fakeroot is not possible to set the owner/group of -> files as root. The package will still be installed correctly even though this -> message is showed. - -## Could not change the root directory in pacman - ## No servers configured for repository ## > **Q**: Why I cannot install packages? @@ -461,66 +239,26 @@ For Arch Linux related FAQs take a look at the [General troubleshooting page](ht $> pkgfile getop core/util-linux -> Alternatively, you can use directly `pacman` command only. Take a look [here](https://wiki.archlinux.org/index.php/General_troubleshooting#Message:_%22error_while_loading_shared_libraries%22). - ## Kernel too old ## > **Q**: Why do I get the error: "FATAL: kernel too old"? -> **A**: This is because the binaries from the precompiled package are -> compiled for Linux kernel 2.6.32. When JuNest is started without further -> options, it tries to run a shell from the JuNest chroot. The system sees that -> the host OS kernel is too old and refuses to start the shell. +> **A**: This is because the executable from the precompiled package cannot +> properly run if the kernel is old. +> You may need to specify the PRoot *-k* option if the guest rootfs +> requires a newer kernel version: -> The solution is to present a higher "fake" kernel version to the JuNest -> chroot. PRoot offers the *-k* option for this, and JuNest passes this option -> on to PRoot when *-p* is prepended. For example, to fake a kernel version of -> 3.10, issue the following command: + $> junest -p "-k 3.10" - $> junest proot -b "-k 3.10" - -> As Arch Linux ships binaries for kernel version 2.6.32, the above error is -> not unique to the precompiled package from JuNest. It will also appear when -> trying to run binaries that were later installed in the JuNest chroot with -> the `pacman` command. - -> In order to check if an executable inside JuNest chroot is compatible with -> the kernel of the host OS just use the `file` command, for instance: +> In order to check if an executable inside JuNest environment can be compatible +> with the kernel of the host OS just use the *file* command, for instance: $> file ~/.junest/usr/bin/bash ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=ec37e49e7188ff4030052783e61b859113e18ca6, stripped -> The output shows the minimum recommended Linux kernel version. - -## Kernel doesn't support private futexes ## - -> **Q**: Why do I get the warning: "kompat: this kernel doesn't support private -> futexes and PRoot can't emulate them."? - -> **A**: This happens on older host OS kernels when the trick of showing a fake -> kernel version to the JuNest chroot is applied (see above: -> [Kernel too old](#kernel-too-old)). - -> The consequence of showing a fake kernel version to the JuNest chroot is that -> in the background, PRoot needs to translate requests from applications in the -> chroot to the old kernel of the host OS. Some of the newer kernel -> functionality can be emulated, but private futexes cannot be translated. - -> Private Futexes were introduced in Linux kernel 2.6.22. Therefore, the above -> problem likely appears on old Linux systems, for example RHEL5 systems, which -> are based on Linux kernel 2.6.18. Many of the core tools like `which`, `man`, -> or `vim` run without problems while others, especially XOrg-based programs, -> are more likely to show the warning. These are also more likely to crash -> unexpectedly. - -> Currently, there is no (easy) workaround for this. In order to be fully -> compatible with kernels below 2.6.22, both the precompiled package from -> JuNest and all software that is installed later needs to be compiled for this -> kernel. Most likely this can only be achieved by building the needed software -> packages from source, which kind of contradicts JuNest's distro-in-a-distro -> philosophy. +> From the output you can see what is the minimum recommended Linux kernel version. ## SUID permissions ## > **Q**: Why I do not have permissions for ping? @@ -534,7 +272,7 @@ For Arch Linux related FAQs take a look at the [General troubleshooting page](ht > have *suid* permission, you can list the commands from your JuNest environment > with the following command: - $> find /usr/bin -perm /4000 + $> find /usr/bin -perm +4000 ## No characters are visible on a graphic application ## @@ -563,32 +301,6 @@ For Arch Linux related FAQs take a look at the [General troubleshooting page](ht > since JuNest will try to preserve the JuNest environment by assigning ownership > of the files to the real user. -## Unprivileged user namespace disable at kernel compile time or kernel too old ## - -> **Q**: Why do I get this warning when I run JuNest via Linux namespaces? - - $> junest ns - Unprivileged user namespace is disabled at kernel compile time or kernel too old (<3.8). Proceeding anyway... - -> **A**: This means that JuNest detected that the host OS either -> does not have a newer kernel version or the unprivileged user namespace -> is not enabled at kernel compile time. -> 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. - -## Unprivileged user namespace disabled - -> **Q**: Why do I get this warning when I run JuNest via Linux namespaces? - - $> junest ns - Unprivileged user namespace disabled. Root permissions are required to enable it: sudo sysctl kernel.unprivileged_userns_clone=1 - -> **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. If you have root permissions try to enable it, otherwise try to use -> Proot as backend program. - More documentation ================== There are additional tutorials in the @@ -596,21 +308,13 @@ There are additional tutorials in the Contributing ============ -Contributions are welcome! You could help improving JuNest in the following ways: +You could help improving JuNest in the following ways: - [Reporting Bugs](CONTRIBUTING.md#reporting-bugs) - [Suggesting Enhancements](CONTRIBUTING.md#suggesting-enhancements) - [Writing Code](CONTRIBUTING.md#your-first-code-contribution) -Donating -======== -To sustain the project please consider funding by donations through -the [GitHub Sponsors page](https://github.com/sponsors/fsquillace/). +Author +====== +Filippo Squillace -Authors -======= -JuNest was originally created in late 2014 by [Filippo Squillace (feel.sqoox@gmail.com)](https://github.com/fsquillace). - -Here is a list of [**really appreciated contributors**](https://github.com/fsquillace/junest/graphs/contributors)! - -[![](https://sourcerer.io/fame/fsquillace/fsquillace/junest/images/0)](https://sourcerer.io/fame/fsquillace/fsquillace/junest/links/0)[![](https://sourcerer.io/fame/fsquillace/fsquillace/junest/images/1)](https://sourcerer.io/fame/fsquillace/fsquillace/junest/links/1)[![](https://sourcerer.io/fame/fsquillace/fsquillace/junest/images/2)](https://sourcerer.io/fame/fsquillace/fsquillace/junest/links/2)[![](https://sourcerer.io/fame/fsquillace/fsquillace/junest/images/3)](https://sourcerer.io/fame/fsquillace/fsquillace/junest/links/3)[![](https://sourcerer.io/fame/fsquillace/fsquillace/junest/images/4)](https://sourcerer.io/fame/fsquillace/fsquillace/junest/links/4)[![](https://sourcerer.io/fame/fsquillace/fsquillace/junest/images/5)](https://sourcerer.io/fame/fsquillace/fsquillace/junest/links/5)[![](https://sourcerer.io/fame/fsquillace/fsquillace/junest/images/6)](https://sourcerer.io/fame/fsquillace/fsquillace/junest/links/6)[![](https://sourcerer.io/fame/fsquillace/fsquillace/junest/images/7)](https://sourcerer.io/fame/fsquillace/fsquillace/junest/links/7) diff --git a/VERSION b/VERSION index ef13716..349e9d0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.4.10 +5.6.8 diff --git a/bin/jchroot b/bin/jchroot new file mode 100755 index 0000000..8eaa8d4 --- /dev/null +++ b/bin/jchroot @@ -0,0 +1,98 @@ +#!/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.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 < Setup the $NAME image in ${JUNEST_HOME}" + echo -e "-a, --arch $NAME architecture to download (x86_64, x86, arm)." + echo -e " Defaults to the host architecture ($ARCH)" + echo -e "-d, --delete Delete $NAME from ${JUNEST_HOME}" echo - echo -e "Actions and options:" - echo -e " s[etup] Setup $NAME in ${JUNEST_HOME} either from repo or from file" - echo -e " -i, --from-file Setup the $NAME image in ${JUNEST_HOME}" - echo -e " -a, --arch $NAME architecture to download (x86_64, arm)" - echo -e " Defaults to the host architecture ($ARCH)" - echo -e " -d, --delete Delete $NAME from ${JUNEST_HOME}" + 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 Proot arguments (use '$CMD -p \"--help\"' to check out the proot options)" echo - echo -e " n[s] Access via Linux Namespaces using BubbleWrap (Default action)" - echo -e " -f, --fakeroot Run $NAME with fakeroot privileges" - echo -e " --backend-command Bwrap command to use" - echo -e " -b, --backend-args Arguments for bwrap backend program" - echo -e " ($CMD ns -b \"--help\" to check out the bwrap options)" - echo -e " -n, --no-copy-files Do not copy common etc files into $NAME environment" - echo - echo -e " p[root] Access via PRoot" - echo -e " -f, --fakeroot Run $NAME with fakeroot privileges" - echo -e " --backend-command PRoot command to use" - echo -e " -b, --backend-args Arguments for PRoot backend program" - echo -e " ($CMD proot -b \"--help\" to check out the PRoot options)" - echo -e " -n, --no-copy-files Do not copy common etc files into $NAME environment" - echo - echo -e " g[root] Access with root privileges via GRoot" - echo -e " --backend-command GRoot command to use" - echo -e " -b, --backend-args Arguments for GRoot backend program" - echo -e " ($CMD groot -b \"--help\" to check out the GRoot options)" - echo -e " -n, --no-copy-files Do not copy common etc files into $NAME environment" - echo - echo -e " r[oot] Access with root privileges via classic chroot" - echo -e " --backend-command Chroot command to use" - echo -e " -b, --backend-args Arguments for chroot backend program" - echo -e " ($CMD root -b \"--help\" to check out the chroot options)" - echo -e " -n, --no-copy-files Do not copy common etc files into $NAME environment" - echo - echo -e " b[uild] Build a $NAME image (must run in ArchLinux)" - echo -e " -n, --disable-check Disable the $NAME image check" - echo - echo -e " create-bin-wrappers Create a bin wrappers directory according to --bin-path option" - echo -e " Default path is $JUNEST_HOME/usr/bin_wrappers" - echo -e " -f, --force Create the wrapper files even if they already exist" - echo -e " -p, --bin-path The source directory where executable are located in JuNest" - echo -e " Default value is: /usr/bin" + 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" } version() { - echo -e "$NAME $(cat "$JUNEST_BASE"/VERSION)" + echo -e "$NAME $(cat $JUNEST_BASE/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" + fi + fi + if $OPT_DISABLE_VALIDATION + then + if $OPT_DELETE || $OPT_HELP || $OPT_VERSION || $OPT_SETUP_FROM_FILE || \ + $OPT_FAKEROOT || $OPT_ROOT || $OPT_CHECK + then + die "The disable validation option must be used with the build image option only" + fi + fi + 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 + then + die "The $NAME delete option must be used exclusively" + fi + fi + 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 + then + die "The $NAME help option must be used exclusively" + fi + fi + 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 + then + die "The $NAME version option must be used exclusively" + fi + fi + if $OPT_FAKEROOT && $OPT_ROOT + then + die "You must access to $NAME with either fakeroot or root permissions" + fi + if $OPT_PROOT_ARGS || $OPT_ARCH + then + if $OPT_BUILD_IMAGE || $OPT_DELETE || $OPT_HELP || \ + $OPT_ROOT || $OPT_VERSION || $OPT_DISABLE_VALIDATION || $OPT_CHECK + then + die "Invalid syntax: Proot and arch args are not allowed with the other options" + fi + fi + if [ "$ARGS" != "" ] + then + if $OPT_BUILD_IMAGE || $OPT_DELETE || $OPT_HELP || $OPT_SETUP_FROM_FILE || \ + $OPT_VERSION || $OPT_DISABLE_VALIDATION || $OPT_CHECK + then + die "No arguments are needed. For the CLI syntax run: $CMD --help" + fi + fi + + return 0 +} + + function parse_arguments(){ - # Actions - ACT_SETUP=false - ACT_BUILD=false - ACT_CREATE_WRAPPERS=false - ACT_NAMESPACE=false - ACT_PROOT=false - ACT_GROOT=false - ACT_ROOT=false - ACT_HELP=false - ACT_VERSION=false - - case "$1" in - s|setup) ACT_SETUP=true ; shift ;; - b|build) ACT_BUILD=true ; shift ;; - create-bin-wrappers) ACT_CREATE_WRAPPERS=true ; shift ;; - n|ns) ACT_NAMESPACE=true ; shift ;; - p|proot) ACT_PROOT=true ; shift ;; - g|groot) ACT_GROOT=true ; shift ;; - r|root) ACT_ROOT=true ; shift ;; - -h|--help) ACT_HELP=true ; shift ;; - -V|--version) ACT_VERSION=true ; shift ;; - *) ACT_NAMESPACE=true ;; - esac - - if $ACT_SETUP - then - _parse_setup_opts "$@" - elif $ACT_BUILD - then - _parse_build_opts "$@" - elif $ACT_CREATE_WRAPPERS - then - _parse_create_wrappers_opts "$@" - elif $ACT_NAMESPACE - then - _parse_ns_opts "$@" - elif $ACT_PROOT - then - _parse_proot_opts "$@" - elif $ACT_GROOT - then - _parse_root_opts "$@" - elif $ACT_ROOT - then - _parse_root_opts "$@" - fi -} - -function _parse_root_opts() { - # Options: - BACKEND_ARGS="" - OPT_NO_COPY_FILES=false - BACKEND_COMMAND="" - - while [[ -n "$1" ]] - do - case "$1" in - -b|--backend-args) shift ; BACKEND_ARGS=$1; shift ;; - -n|--no-copy-files) OPT_NO_COPY_FILES=true ; shift ;; - --backend-command) shift; BACKEND_COMMAND="$1"; shift ;; - --) shift ; break ;; - -*) die "Invalid option $1" ;; - *) break ;; - esac - done - - ARGS=() - for arg in "$@" - do - ARGS+=("$arg") - done -} - -function _parse_ns_opts() { - # Options: - OPT_FAKEROOT=false - BACKEND_ARGS="" - OPT_NO_COPY_FILES=false - BACKEND_COMMAND="" - - while [[ -n "$1" ]] - do - case "$1" in - -f|--fakeroot) OPT_FAKEROOT=true ; shift ;; - -b|--backend-args) shift ; BACKEND_ARGS=$1; shift ;; - -n|--no-copy-files) OPT_NO_COPY_FILES=true ; shift ;; - --backend-command) shift; BACKEND_COMMAND="$1"; shift ;; - --) shift ; break ;; - -*) die "Invalid option $1" ;; - *) break ;; - esac - done - - ARGS=() - for arg in "$@" - do - ARGS+=("$arg") - done -} - -function _parse_proot_opts() { - # Options: - OPT_FAKEROOT=false - BACKEND_ARGS="" - OPT_NO_COPY_FILES=false - BACKEND_COMMAND="" - - while [[ -n "$1" ]] - do - case "$1" in - -f|--fakeroot) OPT_FAKEROOT=true ; shift ;; - -b|--backend-args) shift ; BACKEND_ARGS=$1; shift ;; - -n|--no-copy-files) OPT_NO_COPY_FILES=true ; shift ;; - --backend-command) shift; BACKEND_COMMAND="$1"; shift ;; - --) shift ; break ;; - -*) die "Invalid option $1" ;; - *) break ;; - esac - done - - ARGS=("$@") -} - -function _parse_build_opts() { - OPT_DISABLE_CHECK=false - while [[ -n "$1" ]] - do - case "$1" in - -n|--disable-check) OPT_DISABLE_CHECK=true ; shift ;; - *) die "Invalid option $1" ;; - esac - done -} - -function _parse_create_wrappers_opts() { - OPT_FORCE=false - OPT_BIN_PATH="" - while [[ -n "$1" ]] - do - case "$1" in - -f|--force) OPT_FORCE=true ; shift ;; - -p|--bin-path) shift ; OPT_BIN_PATH="$1" ; shift ;; - *) die "Invalid option $1" ;; - esac - done -} - -function _parse_setup_opts() { - OPT_FROM_FILE=false + OPT_SETUP_FROM_FILE=false IMAGE_FILE="" + OPT_FAKEROOT=false + OPT_ROOT=false + OPT_PROOT_ARGS=false + PROOT_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 - while [[ -n "$1" ]] + OPT_HELP=false + OPT_VERSION=false + for opt in "$@" do case "$1" in - -i|--from-file) OPT_FROM_FILE=true ; shift ; IMAGE_FILE=$1 ; shift ;; - -a|--arch) shift ; ARCH_ARG=$1; shift ;; + -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 ;; + -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 ;; - *) die "Invalid option $1" ;; + -h|--help) OPT_HELP=true ; shift ;; + -v|--version) OPT_VERSION=true ; shift ;; + --) shift ; break ;; + -*) die "Invalid option $1" ;; + *) break ;; esac done + + ARGS=() + for arg in "$@" + do + ARGS+=("$arg") + done } -function execute_operation() { - $ACT_HELP && usage && return - $ACT_VERSION && version && return +function execute_operation(){ + $OPT_HELP && usage && return + $OPT_VERSION && version && return - if $ACT_BUILD; then - # shellcheck disable=SC2086 - build_image_env $OPT_DISABLE_CHECK + if $OPT_BUILD_IMAGE; then + build_image_env $OPT_DISABLE_VALIDATION $OPT_SKIP_ROOT_TEST + 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 $ACT_SETUP; then - if $OPT_DELETE; then - delete_env - else - if is_env_installed - then - die "Error: The image cannot be installed since $JUNEST_HOME is not empty." - fi - - if $OPT_FROM_FILE; then - setup_env_from_file "$IMAGE_FILE" - else - setup_env "$ARCH_ARG" - fi - create_wrappers - fi - - return - fi - - if ! is_env_installed then - die "Error: The image is still not installed in $JUNEST_HOME. Run this first: $CMD setup" - fi - - if $ACT_CREATE_WRAPPERS; then - # shellcheck disable=SC2086 - create_wrappers $OPT_FORCE "$OPT_BIN_PATH" - exit - fi - - local run_env - if $ACT_NAMESPACE; then - if $OPT_FAKEROOT; then - run_env=run_env_as_bwrap_fakeroot + if $OPT_SETUP_FROM_FILE; then + setup_env_from_file $IMAGE_FILE else - run_env=run_env_as_bwrap_user + setup_env $ARCH_ARG + unset ARCH_ARG fi - elif $ACT_PROOT; then - if $OPT_FAKEROOT; then - run_env=run_env_as_proot_fakeroot - else - run_env=run_env_as_proot_user - fi - elif $ACT_GROOT; then - run_env=run_env_as_groot - elif $ACT_ROOT; then - run_env=run_env_as_chroot + elif $OPT_SETUP_FROM_FILE; then + die "Error: The image cannot be installed since $JUNEST_HOME is not empty." fi - # Call create_wrappers in case new bin files have been created - # shellcheck disable=SC2064 - trap "PATH=$PATH create_wrappers" EXIT QUIT TERM - # shellcheck disable=SC2086 - $run_env "$BACKEND_COMMAND" "${BACKEND_ARGS}" $OPT_NO_COPY_FILES "${ARGS[@]}" + [ -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[@]}" + elif $OPT_ROOT; then + run_env_as_root "${ARGS[@]}" + else + run_env_as_user "${PROOT_ARGS}" "${ARGS[@]}" + fi } -function main() { +function cli() { parse_arguments "$@" + check_cli execute_operation } -main "$@" +cli "$@" # vim: set ts=4 sw=4 noet: diff --git a/bin/sudoj b/bin/sudoj deleted file mode 100755 index aa43e15..0000000 --- a/bin/sudoj +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -# -# This file is part of JuNest (https://github.com/fsquillace/junest). -# - -export PATH="${PATH}:${JUNEST_HOME}/usr/bin_wrappers" - -JUNEST_ARGS="ns --fakeroot" "$@" diff --git a/ci/build_image.sh b/ci/build_image.sh deleted file mode 100755 index f9cda95..0000000 --- a/ci/build_image.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -set -ex - -pacman -Sy --noconfirm sudo - -# Create a travis user -echo "travis ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/travis -chmod 'u=r,g=r,o=' /etc/sudoers.d/travis -groupadd --gid "2000" "travis" -useradd --create-home --uid "2000" --gid "2000" --shell /usr/bin/false "travis" - -# Here do not make any validation (-n) because it will be done later on in the Ubuntu host directly -cd /build -runuser -u travis -- /build/bin/junest build -n diff --git a/ci/deploy.sh b/ci/deploy.sh deleted file mode 100755 index a45d0a1..0000000 --- a/ci/deploy.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash - -set -e - -IMG_PATH=$1 - -set -ux - -MAX_OLD_IMAGES=5 -ENDPOINT="https://8da1bcd84e423c9b013b69fe1e8b4675.r2.cloudflarestorage.com" - -# ARCH can be one of: x86, x86_64, arm -HOST_ARCH=$(uname -m) -if [ "$HOST_ARCH" == "i686" ] || [ "$HOST_ARCH" == "i386" ] -then - ARCH="x86" -elif [ "$HOST_ARCH" == "x86_64" ] -then - ARCH="x86_64" -elif [[ $HOST_ARCH =~ .*(arm).* ]] -then - ARCH="arm" -else - echo "Unknown architecture ${HOST_ARCH}" >&2 - exit 11 -fi - -if [[ "$TRAVIS_BRANCH" == "master" ]] -then - - export AWS_DEFAULT_REGION=auto - # Upload image - # The put is done via a temporary filename in order to prevent outage on the - # production file for a longer period of time. - img_name=$(basename "${IMG_PATH}") - aws s3 --endpoint-url="$ENDPOINT" cp "${IMG_PATH}" s3://junest-repo/junest/ - - DATE=$(date +'%Y-%m-%d-%H-%M-%S') - aws s3 --endpoint-url="$ENDPOINT" cp "${IMG_PATH}" "s3://junest-repo/junest/${img_name}.${DATE}" - - # Cleanup old images - aws s3 --endpoint-url="$ENDPOINT" ls s3://junest-repo/junest/junest-${ARCH}.tar.gz. | awk '{print $4}' | head -n -${MAX_OLD_IMAGES} | xargs -I {} aws s3 --endpoint-url="$ENDPOINT" rm "s3://junest-repo/junest/{}" - - # Test the newly deployed image can be downloaded correctly - junest setup - junest -- echo "Installed JuNest (\$(uname -m))" - yes | junest setup --delete -fi diff --git a/ci/install-bash.sh b/ci/install-bash.sh deleted file mode 100755 index b766123..0000000 --- a/ci/install-bash.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -set -ex - -VERSION=$1 - -cd /tmp -wget "http://ftp.gnu.org/gnu/bash/bash-$VERSION.tar.gz" - -tar -zxf "bash-$VERSION.tar.gz" -cd /tmp/bash-"$VERSION"* -./configure -make -sudo make install diff --git a/lib/checks/check.sh b/lib/checks/check.sh deleted file mode 100755 index 789206e..0000000 --- a/lib/checks/check.sh +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env bash -# shellcheck disable=SC1091 -# -# 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 -ex - - -RUN_ROOT_TESTS=false -SKIP_AUR_TESTS=false -USE_SUDO=false -while [[ -n "$1" ]] -do - case "$1" in - --run-root-tests) RUN_ROOT_TESTS=true ; shift ;; - --skip-aur-tests) SKIP_AUR_TESTS=true ; shift ;; - --use-sudo) USE_SUDO=true ; shift ;; - *) die "Invalid option $1" ;; - esac -done - -set -u - -SUDO="" -[[ -n $USE_SUDO ]] && SUDO="sudo" - -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..." -# The following ensures that the gpg agent gets killed (if exists) -# otherwise it is not possible to exit from the session -trap "[[ -e /etc/pacman.d/gnupg/S.gpg-agent ]] && gpg-connect-agent -S /etc/pacman.d/gnupg/S.gpg-agent killagent /bye" QUIT EXIT ABRT TERM INT - -prepare_archlinux "$SUDO" - -PACMAN_OPTIONS="--noconfirm --disable-download-timeout" -# shellcheck disable=SC2086 -$SUDO pacman $PACMAN_OPTIONS -S grep coreutils -# shellcheck disable=SC2086 -# shellcheck disable=SC2046 -$SUDO pacman $PACMAN_OPTIONS -Syu --ignore sudo base-devel - -info "Checking basic executables work..." -$SUDO pacman -Qi pacman 1> /dev/null -/usr/bin/groot --help 1> /dev/null - -# Test FAKEROOTDONTTRYCHOWN is set to true by default -set +u -if [[ -z $FAKEROOTKEY ]] -then - fakeroot chown root /tmp -else - chown root /tmp -fi -set -u - -repo_package1=tree -echo "Checking ${repo_package1} package from official repo..." -# shellcheck disable=SC2086 -$SUDO pacman $PACMAN_OPTIONS -S ${repo_package1} -tree -L 1 -# shellcheck disable=SC2086 -$SUDO pacman $PACMAN_OPTIONS -Rsn ${repo_package1} - -repo_package2=iftop -info "Checking ${repo_package2} package from official repo..." -# shellcheck disable=SC2086 -$SUDO pacman $PACMAN_OPTIONS -S ${repo_package2} -if $RUN_ROOT_TESTS -then - # Time it out given that sometimes it gets stuck after few seconds. - $SUDO timeout 10 iftop -t -s 5 || true -fi -# shellcheck disable=SC2086 -$SUDO pacman $PACMAN_OPTIONS -Rsn ${repo_package2} - -if ! $SKIP_AUR_TESTS -then - aur_package=tcptraceroute - info "Checking ${aur_package} package from AUR repo..." - yay --noconfirm -S ${aur_package} - # shellcheck disable=SC2086 - $SUDO pacman $PACMAN_OPTIONS -Rsn ${aur_package} -fi - -exit 0 diff --git a/lib/checks/check_all.sh b/lib/checks/check_all.sh deleted file mode 100755 index 13e9237..0000000 --- a/lib/checks/check_all.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash -# Multiple tests against different execution modes - -set -ex - -# 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")")"/../..)}" - -JUNEST_SCRIPT=${JUNEST_SCRIPT:-${JUNEST_BASE}/bin/junest} - -CHECK_SCRIPT=${JUNEST_BASE}/lib/checks/check.sh - -$JUNEST_SCRIPT proot --fakeroot -- "$CHECK_SCRIPT" --skip-aur-tests -$JUNEST_SCRIPT proot -- "$CHECK_SCRIPT" --skip-aur-tests --use-sudo -# Test the backend command option -$JUNEST_SCRIPT proot --backend-command "$JUNEST_HOME/usr/bin/proot-x86_64" -- exit -$JUNEST_SCRIPT ns --fakeroot -- "$CHECK_SCRIPT" --skip-aur-tests -$JUNEST_SCRIPT ns -- "$CHECK_SCRIPT" --use-sudo -# Test the backend command option -$JUNEST_SCRIPT ns --backend-command "$JUNEST_HOME/usr/bin/bwrap" -- exit -sudo -E "$JUNEST_SCRIPT" groot -- "$CHECK_SCRIPT" --run-root-tests --skip-aur-tests - -# Test the wrappers work -"$JUNEST_SCRIPT" create-bin-wrappers --force -"$JUNEST_HOME"/usr/bin_wrappers/pacman --help - -"$JUNEST_SCRIPT" create-bin-wrappers --force --bin-path /usr/bin/core_perl/ -"$JUNEST_HOME"/usr/bin/core_perl_wrappers/shasum --help - -"${JUNEST_BASE}/bin/sudoj" pacman -Syu diff --git a/lib/core.sh b/lib/core.sh new file mode 100644 index 0000000..dbb81d2 --- /dev/null +++ b/lib/core.sh @@ -0,0 +1,634 @@ +#!/usr/bin/env bash +# +# This module contains all core functionalities for JuNest. +# +# Dependencies: +# - lib/utils.sh +# +# vim: ft=sh + +set -e + +NAME='JuNest' +CMD='junest' +DESCRIPTION='The Arch Linux based distro that runs upon any Linux distros without root access' + +NOT_AVAILABLE_ARCH=102 +NOT_EXISTING_FILE=103 +ARCHITECTURE_MISMATCH=104 +ROOT_ACCESS_ERROR=105 +NESTED_ENVIRONMENT=106 +VARIABLE_NOT_SET=107 + +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 + +# The update of the variable PATH ensures that the executables are +# found on different locations +PATH=/usr/bin:/bin:/usr/sbin:/sbin:$PATH + +# The executable uname is essential in order to get the architecture +# of the host system, so a fallback mechanism cannot be used for it. +UNAME=uname + +ARCH_LIST=('x86_64' 'x86' 'arm') +HOST_ARCH=$($UNAME -m) +if [ $HOST_ARCH == "i686" ] || [ $HOST_ARCH == "i386" ] +then + ARCH="x86" + LD_LIB="${JUNEST_HOME}/lib/ld-linux.so.2" +elif [ $HOST_ARCH == "x86_64" ] +then + ARCH="x86_64" + LD_LIB="${JUNEST_HOME}/lib64/ld-linux-x86-64.so.2" +elif [[ $HOST_ARCH =~ .*(arm).* ]] +then + ARCH="arm" + LD_LIB="${JUNEST_HOME}/lib/ld-linux-armhf.so.3" +else + die "Unknown architecture ${ARCH}" +fi + +PROOT_LINK=http://static.proot.me/ +MAIN_REPO=https://dl.dropboxusercontent.com/u/42449030 +ENV_REPO=${MAIN_REPO}/${CMD} +DEFAULT_MIRROR='https://mirrors.kernel.org/archlinux/$repo/os/$arch' + +ORIGIN_WD=$(pwd) + +################################ EXECUTABLES ################################## +# This section contains all the executables needed for JuNest to run properly. +# They are based on a fallback mechanism that tries to use the executable in +# different locations in the host OS. + +# List of executables that are run inside JuNest: +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" +WGET="wget --no-check-certificate" +CURL="curl -L -J -O -k" +TAR=tar +CHOWN="chown" +LN=ln +RM=rm +MKDIR=mkdir +GETENT=getent +CP=cp + +LD_EXEC="$LD_LIB --library-path ${JUNEST_HOME}/usr/lib:${JUNEST_HOME}/lib" + +# The following functions attempt first to run the executable in the host OS. +# As a last hope they try to run the same executable available in the JuNest +# image. + +function ln_cmd(){ + $LN $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/$LN $@ +} + +function getent_cmd(){ + $GETENT $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/$GETENT $@ +} + +function cp_cmd(){ + $CP $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/$CP $@ +} + +function rm_cmd(){ + $RM $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/$RM $@ +} + +function chown_cmd(){ + $CHOWN $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/$CHOWN $@ +} + +function mkdir_cmd(){ + $MKDIR $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/$MKDIR $@ +} + +function proot_cmd(){ + local proot_args="$1" + shift + if ${PROOT} ${proot_args} "${SH[@]}" "-c" ":" + then + ${PROOT} ${proot_args} "${@}" + elif PROOT_NO_SECCOMP=1 ${PROOT} ${proot_args} "${SH[@]}" "-c" ":" + 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\"" + fi +} + +function download_cmd(){ + $WGET $@ || $CURL $@ +} + +function chroot_cmd(){ + $CHROOT "$@" || $CLASSIC_CHROOT "$@" || $LD_EXEC ${JUNEST_HOME}/usr/bin/chroot "$@" +} + +################################# MAIN FUNCTIONS ############################## + +####################################### +# Check if the JuNest system is installed in JUNEST_HOME. +# +# Globals: +# JUNEST_HOME (RO) : Contains the JuNest home directory. +# Arguments: +# None +# Returns: +# 0 : If JuNest is installed +# 1 : If JuNest is not installed +# Output: +# None +####################################### +function is_env_installed(){ + [ -d "$JUNEST_HOME" ] && [ "$(ls -A $JUNEST_HOME)" ] && return 0 + return 1 +} + + +function _cleanup_build_directory(){ + local maindir=$1 + check_not_null "$maindir" + builtin cd $ORIGIN_WD + trap - QUIT EXIT ABRT KILL TERM INT + rm_cmd -fr "$maindir" +} + + +function _prepare_build_directory(){ + local maindir=$1 + check_not_null "$maindir" + trap - QUIT EXIT ABRT KILL TERM INT + trap "rm_cmd -rf ${maindir}; die \"Error occurred when installing ${NAME}\"" EXIT QUIT ABRT KILL TERM INT +} + + +function _setup_env(){ + local imagepath=$1 + check_not_null "$imagepath" + + is_env_installed && die "Error: ${NAME} has been already installed in $JUNEST_HOME" + + mkdir_cmd -p "${JUNEST_HOME}" + $TAR -zxpf ${imagepath} -C ${JUNEST_HOME} + info "The default mirror URL is ${DEFAULT_MIRROR}." + info "Remember to refresh the package databases from the server:" + info " pacman -Syy" + info "${NAME} installed successfully" +} + + +####################################### +# Setup JuNest. +# +# Globals: +# JUNEST_HOME (RO) : The JuNest home directory in which JuNest needs +# to be installed. +# ARCH (RO) : The host architecture. +# JUNEST_TEMPDIR (RO) : The JuNest temporary directory for building +# the JuNest system from the image. +# ENV_REPO (RO) : URL of the site containing JuNest images. +# NAME (RO) : The JuNest name. +# DEFAULT_MIRROR (RO) : Arch Linux URL mirror. +# Arguments: +# arch ($1?) : The JuNest architecture image to download. +# Defaults to the host architecture +# Returns: +# $NOT_AVAILABLE_ARCH : If the architecture is not one of the available ones. +# Output: +# None +####################################### +function setup_env(){ + local arch=${1:-$ARCH} + contains_element $arch "${ARCH_LIST[@]}" || \ + die_on_status $NOT_AVAILABLE_ARCH "The architecture is not one of: ${ARCH_LIST[@]}" + + local maindir=$(TMPDIR=$JUNEST_TEMPDIR mktemp -d -t ${CMD}.XXXXXXXXXX) + _prepare_build_directory $maindir + + info "Downloading ${NAME}..." + builtin cd ${maindir} + local imagefile=${CMD}-${arch}.tar.gz + download_cmd ${ENV_REPO}/${imagefile} + + info "Installing ${NAME}..." + _setup_env ${maindir}/${imagefile} + + _cleanup_build_directory ${maindir} +} + +####################################### +# Setup JuNest from file. +# +# Globals: +# JUNEST_HOME (RO) : The JuNest home directory in which JuNest needs +# to be installed. +# NAME (RO) : The JuNest name. +# DEFAULT_MIRROR (RO) : Arch Linux URL mirror. +# Arguments: +# imagefile ($1) : The JuNest image file. +# Returns: +# $NOT_EXISTING_FILE : If the image file does not exist. +# Output: +# None +####################################### +function setup_env_from_file(){ + local imagefile=$1 + check_not_null "$imagefile" + [ ! -e ${imagefile} ] && die_on_status $NOT_EXISTING_FILE "Error: The ${NAME} image file ${imagefile} does not exist" + + info "Installing ${NAME} from ${imagefile}..." + _setup_env ${imagefile} +} + +####################################### +# 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" + + local uid=$UID + # SUDO_USER is more reliable compared to SUDO_UID + [ -z $SUDO_USER ] || uid=$SUDO_USER:$SUDO_GID + + local main_cmd="${SH[@]}" + [ "$1" != "" ] && main_cmd="$(insert_quotes_on_spaces "$@")" + + # With chown the ownership of the files is assigned to the real user + 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}" +} + +function _run_env_with_proot(){ + local proot_args="$1" + shift + + if [ "$1" != "" ] + then + JUNEST_ENV=1 proot_cmd "${proot_args}" "${SH[@]}" "-c" "$(insert_quotes_on_spaces "${@}")" + else + JUNEST_ENV=1 proot_cmd "${proot_args}" "${SH[@]}" + fi +} + +function _run_env_with_qemu(){ + local proot_args="$1" + source ${JUNEST_HOME}/etc/junest/info + + if [ "$JUNEST_ARCH" != "$ARCH" ] + then + local qemu_bin="qemu-$JUNEST_ARCH-static-$ARCH" + local qemu_symlink="/tmp/${qemu_bin}-$RANDOM" + trap - QUIT EXIT ABRT KILL TERM INT + trap "[ -e ${qemu_symlink} ] && rm_cmd -f ${qemu_symlink}" EXIT QUIT ABRT KILL TERM INT + + warn "Emulating $NAME via QEMU..." + [ -e ${qemu_symlink} ] || \ + ln_cmd -s ${JUNEST_HOME}/opt/qemu/${qemu_bin} ${qemu_symlink} + proot_args="-q ${qemu_symlink} $proot_args" + fi + shift + _run_env_with_proot "$proot_args" "${@}" +} + +####################################### +# 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. +# Arguments: +# cmd ($@?) : 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. +# 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." + + _run_env_with_qemu "-S ${JUNEST_HOME} $1" "${@:2}" +} + +####################################### +# Run JuNest as normal user. +# +# Globals: +# 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. +# Default command is defined by SH variable. +# Returns: +# $ROOT_ACCESS_ERROR : If the user is the real root. +# Output: +# - : The command output. +####################################### +function run_env_as_user(){ + (( EUID == 0 )) && \ + die_on_status $ROOT_ACCESS_ERROR "You cannot access with root privileges. Use --root option instead." + + _build_passwd_and_group + _provide_bindings_as_user + local bindings="$RESULT" + unset RESULT + _run_env_with_qemu "$bindings -r ${JUNEST_HOME} $1" "${@:2}" +} + +####################################### +# Provide the proot binding options for the normal user. +# The list of bindings can be found in `proot --help`. This function excludes +# /etc/mtab file so that it will not give conflicts with the related +# symlink in the image. +# +# Globals: +# HOME (RO) : The home directory. +# RESULT (WO) : Contains the binding options. +# Arguments: +# None +# Returns: +# None +# Output: +# None +####################################### +function _provide_bindings_as_user(){ + RESULT="" + local re='(.*):.*' + for bind in "/etc/host.conf" "/etc/hosts" "/etc/hosts.equiv" "/etc/netgroup" "/etc/networks" "${JUNEST_HOME}/etc/junest/passwd:/etc/passwd" "${JUNEST_HOME}/etc/junest/group:/etc/group" "/etc/nsswitch.conf" "/etc/resolv.conf" "/etc/localtime" "/dev" "/sys" "/proc" "/tmp" "$HOME" + do + if [[ $bind =~ $re ]] + then + [ -e "${BASH_REMATCH[1]}" ] && RESULT="-b $bind $RESULT" + else + [ -e "$bind" ] && RESULT="-b $bind $RESULT" + fi + done +} + +####################################### +# Build passwd and group files using getent command. +# If getent fails the function fallbacks by copying the content from /etc/passwd +# and /etc/group. +# +# The generated passwd and group will be stored in $JUNEST_HOME/etc/junest. +# +# Globals: +# JUNEST_HOME (RO) : The JuNest home directory. +# Arguments: +# None +# Returns: +# None +# Output: +# None +####################################### +function _build_passwd_and_group(){ + if ! getent_cmd passwd > ${JUNEST_HOME}/etc/junest/passwd + then + warn "getent command failed or does not exist. Binding directly from /etc/passwd." + cp_cmd /etc/passwd ${JUNEST_HOME}/etc/junest/passwd + fi + if ! getent_cmd group > ${JUNEST_HOME}/etc/junest/group + then + warn "getent command failed or does not exist. Binding directly from /etc/group." + cp_cmd /etc/group ${JUNEST_HOME}/etc/junest/group + fi +} + +####################################### +# Remove an existing JuNest system. +# +# Globals: +# JUNEST_HOME (RO) : The JuNest home directory to remove. +# Arguments: +# None +# Returns: +# None +# Output: +# None +####################################### +function delete_env(){ + ! ask "Are you sure to delete ${NAME} located in ${JUNEST_HOME}" "N" && return + if mountpoint -q ${JUNEST_HOME} + then + info "There are mounted directories inside ${JUNEST_HOME}" + if ! umount --force ${JUNEST_HOME} + then + error "Cannot umount directories in ${JUNEST_HOME}" + die "Try to delete ${NAME} using root permissions" + fi + fi + # the CA directories are read only and can be deleted only by changing the mod + chmod -R +w ${JUNEST_HOME}/etc/ca-certificates + if rm_cmd -rf ${JUNEST_HOME}/* + then + info "${NAME} deleted in ${JUNEST_HOME}" + else + error "Error: Cannot delete ${NAME} in ${JUNEST_HOME}" + fi +} + + +function _check_package(){ + if ! pacman -Qq $1 > /dev/null + then + die "Package $1 must be installed" + fi +} + +function _install_from_aur(){ + local maindir=$1 + local pkgname=$2 + local installname=$3 + mkdir -p ${maindir}/packages/${pkgname} + builtin cd ${maindir}/packages/${pkgname} + $CURL "https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=${pkgname}" + [ -z "${installname}" ] || $CURL "https://aur.archlinux.org/cgit/aur.git/plain/${installname}?h=${pkgname}" + makepkg -sfc + sudo pacman --noconfirm --root ${maindir}/root -U ${pkgname}*.pkg.tar.xz +} + +function build_image_env(){ + umask 022 + + # The function must runs on ArchLinux with non-root privileges. + (( EUID == 0 )) && \ + die "You cannot build with root privileges." + + _check_package arch-install-scripts + _check_package gcc + _check_package package-query + _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 + trap - QUIT EXIT ABRT KILL TERM INT + trap "sudo rm -rf ${maindir}; die \"Error occurred when installing ${NAME}\"" EXIT QUIT ABRT KILL TERM INT + info "Installing pacman and its dependencies..." + # The archlinux-keyring and libunistring are due to missing dependencies declaration in ARM archlinux + # All the essential executables (ln, mkdir, chown, etc) are in coreutils + # yaourt requires sed + sudo pacstrap -G -M -d ${maindir}/root pacman coreutils libunistring archlinux-keyring sed + sudo bash -c "echo 'Server = $DEFAULT_MIRROR' >> ${maindir}/root/etc/pacman.d/mirrorlist" + mkdir -p ${maindir}/root/run/lock + + info "Install ${NAME} script..." + sudo pacman --noconfirm --root ${maindir}/root -S git + _install_from_aur ${maindir} "${CMD}-git" "${CMD}.install" + sudo pacman --noconfirm --root ${maindir}/root -Rsn git + + info "Generating the locales..." + # 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 bash -c "echo 'LANG=\"en_US.UTF-8\"' >> ${maindir}/root/etc/locale.conf" + + info "Generating the metadata info..." + sudo mkdir ${maindir}/root/etc/${CMD} + sudo bash -c "echo 'JUNEST_ARCH=$ARCH' > ${maindir}/root/etc/${CMD}/info" + + info "Installing compatibility binaries proot" + sudo mkdir -p ${maindir}/root/opt/proot + builtin cd ${maindir}/root/opt/proot + for arch in ${ARCH_LIST[@]} + do + info "Downloading $PROOT_LINK/proot-$arch ..." + sudo $CURL $PROOT_LINK/proot-$arch + sudo chmod +x proot-$arch + done + + info "Installing qemu static binaries" + sudo mkdir -p ${maindir}/root/opt/qemu + builtin cd ${maindir}/root/opt/qemu + for arch in ${ARCH_LIST[@]} + do + if [ "$arch" != "$ARCH" ] + then + info "Downloading qemu-$ARCH-static-$arch ..." + sudo $CURL ${MAIN_REPO}/qemu/$ARCH/qemu-$ARCH-static-$arch + sudo chmod +x qemu-$ARCH-static-$arch + fi + done + + # AUR packages requires non-root user to be compiled. proot fakes the user to 10 + info "Compiling and installing yaourt..." + _install_from_aur ${maindir} "package-query" + + _install_from_aur ${maindir} "yaourt" + # Apply patches for yaourt and makepkg + sudo mkdir -p ${maindir}/root/opt/yaourt/bin/ + sudo cp ${maindir}/root/usr/bin/yaourt ${maindir}/root/opt/yaourt/bin/ + sudo sed -i -e 's/"--asroot"//' ${maindir}/root/opt/yaourt/bin/yaourt + sudo cp ${maindir}/root/usr/bin/makepkg ${maindir}/root/opt/yaourt/bin/ + sudo sed -i -e 's/EUID\s==\s0/false/' ${maindir}/root/opt/yaourt/bin/makepkg + sudo bash -c "echo 'export PATH=/opt/yaourt/bin:\$PATH' > ${maindir}/root/etc/profile.d/${CMD}.sh" + sudo chmod +x ${maindir}/root/etc/profile.d/${CMD}.sh + + info "Setting up the pacman keyring (this might take a while!)..." + sudo ${maindir}/root/opt/junest/bin/jchroot ${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/* + + mkdir -p ${maindir}/output + builtin cd ${maindir}/output + local imagefile="${CMD}-${ARCH}.tar.gz" + info "Compressing image to ${imagefile}..." + sudo $TAR -zcpf ${imagefile} -C ${maindir}/root . + + if ! $disable_validation + 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 + fi + + sudo cp ${maindir}/output/${imagefile} ${ORIGIN_WD} + + builtin cd ${ORIGIN_WD} + 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 yaourt -V 1> /dev/null + JUNEST_HOME=${testdir} ${cmd} -- yaourt -V 1> /dev/null + JUNEST_HOME=${testdir} ${cmd} -f -- yaourt -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 yaourt_package=tcptraceroute + info "Installing ${yaourt_package} package from AUR repo using proot..." + JUNEST_HOME=${testdir} ${cmd} -f -- yaourt -A --noconfirm -S ${yaourt_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 + +} diff --git a/lib/core/build.sh b/lib/core/build.sh deleted file mode 100644 index 65a9474..0000000 --- a/lib/core/build.sh +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/env bash -# -# This module contains all build functionalities for JuNest. -# -# Dependencies: -# - lib/utils/utils.sh -# - lib/core/common.sh -# -# vim: ft=sh - -function _install_pkg(){ - # This function allows to install packages from AUR. - # At the moment is not used. - local maindir=$1 - local pkgbuilddir=$2 - # Generate a working directory because sources will be downloaded to there - working_dir=$(TMPDIR=/tmp mktemp -d -t junest-wd.XXXXXXXXXX) - cp -R "$pkgbuilddir"/* "$working_dir" - builtin cd "${working_dir}" || return 1 - makepkg -sfcd - makepkg --printsrcinfo > "${pkgbuilddir}"/.SRCINFO - sudo pacman --noconfirm --root "${maindir}"/root -U ./*.pkg.tar.* -} - -function _prepare() { - # ArchLinux System initialization - prepare_archlinux - # curl is used to download pacman.conf file - sudo pacman -S --noconfirm git arch-install-scripts haveged curl -} - -function build_image_env(){ - set -x - umask 022 - - # The function must runs on ArchLinux with non-root privileges. - # This is because installing AUR packages can be done by normal users only. - (( EUID == 0 )) && \ - die "You cannot build with root privileges." - - _prepare - - local disable_validation=$1 - - local maindir - maindir=$(TMPDIR=$JUNEST_TEMPDIR mktemp -d -t "${CMD}".XXXXXXXXXX) - sudo mkdir -p "${maindir}"/root - trap - QUIT EXIT ABRT TERM INT - # shellcheck disable=SC2064 - trap "sudo rm -rf ${maindir}; die \"Error occurred when installing ${NAME}\"" EXIT QUIT ABRT TERM INT - info "Installing pacman and its dependencies..." - # All the essential executables (ln, mkdir, chown, etc) are in coreutils - # bwrap command belongs to bubblewrap - sudo pacstrap -G -M "${maindir}"/root pacman coreutils bubblewrap - - if [[ ${ARCH} != "arm" ]] - then - # x86_64 does not have any mirror set by default... - sudo bash -c "echo 'Server = $DEFAULT_MIRROR' >> ${maindir}/root/etc/pacman.d/mirrorlist" - fi - sudo mkdir -p "${maindir}"/root/run/lock - - # For some reasons, pacstrap does not create the pacman.conf file, - # I could not reproduce the issue locally though: - # https://app.travis-ci.com/github/fsquillace/junest/builds/268216346 - [[ -e "${maindir}"/root/etc/pacman.conf ]] || sudo curl "https://gitlab.archlinux.org/archlinux/packaging/packages/pacman/-/raw/main/pacman.conf" -o "${maindir}/root/etc/pacman.conf" - - # Pacman/pacstrap bug: https://gitlab.archlinux.org/archlinux/packaging/packages/arch-install-scripts/-/issues/3 - sudo sed -i '/^DownloadUser = alpm$/d' "${maindir}"/root/etc/pacman.conf - - sudo tee -a "${maindir}"/root/etc/pacman.conf < ${maindir}/root/etc/${CMD}/info" - # Related to: https://github.com/fsquillace/junest/issues/305 - sudo bash -c "echo 'export FAKEROOTDONTTRYCHOWN=true' > ${maindir}/root/etc/profile.d/junest.sh" - - info "Generating the locales..." - # sed command is required for locale-gen but it is required by fakeroot - # and cannot be removed - # localedef (called by locale-gen) requires gzip but it is supposed to be - # already installed as systemd already depends on it - sudo pacman --noconfirm --root "${maindir}"/root -S sed gzip - 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/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!)..." - if [[ $(uname -m) == *"arm"* ]] - then - sudo pacman -S --noconfirm --root "${maindir}"/root archlinuxarm-keyring - else - sudo pacman -S --noconfirm --root "${maindir}"/root archlinux-keyring - fi - sudo mount --bind "${maindir}"/root "${maindir}"/root - sudo arch-chroot "${maindir}"/root bash -c ' - set -e - pacman-key --init; - for keyring_file in /usr/share/pacman/keyrings/*.gpg; - do - keyring=$(basename $keyring_file | cut -f 1 -d "."); - pacman-key --populate $keyring; - done;' - sudo umount "${maindir}"/root - - sudo rm "${maindir}"/root/var/cache/pacman/pkg/* - # This is needed on system with busybox tar command. - # If the file does not have write permission, the tar command to extract files fails. - sudo chmod -R u+rw "${maindir}"/root/ - - mkdir -p "${maindir}"/output - builtin cd "${maindir}"/output || return 1 - local imagefile="${CMD}-${ARCH}.tar.gz" - info "Compressing image to ${imagefile}..." - sudo "$TAR" -zcpf "${imagefile}" -C "${maindir}"/root . - - if ! $disable_validation - then - mkdir -p "${maindir}"/root_test - $TAR -zxpf "${imagefile}" -C "${maindir}/root_test" - JUNEST_HOME="${maindir}/root_test" "${JUNEST_BASE}"/lib/checks/check_all.sh - fi - - sudo cp "${maindir}"/output/"${imagefile}" "${ORIGIN_WD}" - - builtin cd "${ORIGIN_WD}" || return 1 - trap - QUIT EXIT ABRT KILL TERM INT - sudo rm -fr "$maindir" - - set +x -} diff --git a/lib/core/chroot.sh b/lib/core/chroot.sh deleted file mode 100644 index c2237a9..0000000 --- a/lib/core/chroot.sh +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env bash -# -# This module contains all chroot functionalities for JuNest. -# -# Dependencies: -# - lib/utils/utils.sh -# - lib/core/common.sh -# -# vim: ft=sh - -function _run_env_as_xroot(){ - local cmd=$1 - local backend_args="$2" - local no_copy_files="$3" - shift 3 - - check_same_arch - - local uid=$UID - # SUDO_USER is more reliable compared to SUDO_UID - [[ -z $SUDO_USER ]] || uid=$SUDO_USER:$SUDO_GID - - local args=() - [[ "$1" != "" ]] && args=("-c" "$(insert_quotes_on_spaces "${@}")") - - # With chown the ownership of the files is assigned to the real user - trap - QUIT EXIT ABRT KILL TERM INT - # shellcheck disable=SC2064 - trap "[ -z $uid ] || chown_cmd -R ${uid} ${JUNEST_HOME};" EXIT QUIT ABRT TERM INT - - if ! $no_copy_files - then - copy_common_files - fi - - # shellcheck disable=SC2086 - JUNEST_ENV=1 $cmd $backend_args "$JUNEST_HOME" "${DEFAULT_SH[@]}" "${args[@]}" -} - -####################################### -# 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. -# DEFAULT_SH (RO) : Contains the default command to run in JuNest. -# Arguments: -# backend_args ($1) : The arguments to pass to backend program -# no_copy_files ($2?) : If false it will copy some files in /etc -# from host to JuNest environment. -# cmd ($3-?) : The command to run inside JuNest environment. -# Default command is defined by DEFAULT_SH variable. -# Returns: -# $ARCHITECTURE_MISMATCH : If host and JuNest architecture are different. -# Output: -# - : The command output. -####################################### -function run_env_as_groot(){ - check_nested_env - - local backend_command="${1:-$GROOT}" - local backend_args="$2" - local no_copy_files="$3" - shift 3 - - provide_common_bindings - local bindings=${RESULT} - unset RESULT - - _run_env_as_xroot "$backend_command $bindings" "$backend_args" "$no_copy_files" "$@" -} - -####################################### -# 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. -# DEFAULT_SH (RO) : Contains the default command to run in JuNest. -# Arguments: -# backend_args ($1) : The arguments to pass to backend program -# no_copy_files ($2?) : If false it will copy some files in /etc -# from host to JuNest environment. -# cmd ($3-?) : The command to run inside JuNest environment. -# Default command is defined by DEFAULT_SH variable. -# Returns: -# $ARCHITECTURE_MISMATCH : If host and JuNest architecture are different. -# Output: -# - : The command output. -####################################### -function run_env_as_chroot(){ - check_nested_env - - local backend_command="${1:-chroot_cmd}" - local backend_args="$2" - local no_copy_files="$3" - shift 3 - - _run_env_as_xroot "$backend_command" "$backend_args" "$no_copy_files" "$@" -} diff --git a/lib/core/common.sh b/lib/core/common.sh deleted file mode 100644 index df79bec..0000000 --- a/lib/core/common.sh +++ /dev/null @@ -1,335 +0,0 @@ -#!/usr/bin/env bash -# shellcheck disable=SC2034 -# shellcheck disable=SC1091 -# -# This module contains all common functionalities for JuNest. -# -# Dependencies: -# - lib/utils/utils.sh -# -# vim: ft=sh - -NAME='JuNest' -CMD='junest' -DESCRIPTION='The Arch Linux based distro that runs upon any Linux distros without root access' - -NOT_AVAILABLE_ARCH=102 -NOT_EXISTING_FILE=103 -ARCHITECTURE_MISMATCH=104 -ROOT_ACCESS_ERROR=105 -NESTED_ENVIRONMENT=106 -VARIABLE_NOT_SET=107 -NO_CONFIG_FOUND=108 -UNPRIVILEGED_USERNS_DISABLED=109 - -JUNEST_HOME=${JUNEST_HOME:-~/.${CMD}} -JUNEST_TEMPDIR=${JUNEST_TEMPDIR:-/tmp} - -# The update of the variable PATH ensures that the executables are -# found on different locations -PATH=/usr/bin:/bin:/usr/local/bin:/usr/sbin:/sbin:${HOME}/.local/bin:"$PATH" - -# The executable uname is essential in order to get the architecture -# of the host system, so a fallback mechanism cannot be used for it. -UNAME="uname" - -ARCH_LIST=('x86_64' 'x86' 'arm') -HOST_ARCH=$($UNAME -m) -# To check all available architectures look here: -# https://wiki.archlinux.org/index.php/PKGBUILD#arch -if [[ $HOST_ARCH == "i686" ]] || [[ $HOST_ARCH == "i386" ]] -then - ARCH="x86" - LD_LIB="${JUNEST_HOME}/lib/ld-linux.so.2" -elif [[ $HOST_ARCH == "x86_64" ]] -then - ARCH="x86_64" - LD_LIB="${JUNEST_HOME}/lib64/ld-linux-x86-64.so.2" -elif [[ $HOST_ARCH =~ .*(arm).* ]] || [[ $HOST_ARCH == "aarch64" ]] -then - ARCH="arm" - LD_LIB="${JUNEST_HOME}/lib/ld-linux-armhf.so.3" -else - die "Unknown architecture ${HOST_ARCH}" -fi - -MAIN_REPO=https://link.storjshare.io/s/jvb5tgarnjtt565fffa44spvyuga/junest-repo -MAIN_REPO=https://pub-a2af2344e8554f6c807bc3db355ae622.r2.dev -ENV_REPO=${MAIN_REPO}/${CMD} -# shellcheck disable=SC2016 -DEFAULT_MIRROR='https://mirror.rackspace.com/archlinux/$repo/os/$arch' - -ORIGIN_WD=$(pwd) - -################## EXECUTABLES ################ - -# This section contains all the executables needed for JuNest to run properly. -# They are based on a fallback mechanism that tries to use the executable in -# different locations in the host OS. - -# List of executables that are run inside JuNest: -DEFAULT_SH=("/bin/sh" "--login") - -# List of executables that are run in the host OS: -BWRAP="${JUNEST_HOME}/usr/bin/bwrap" -PROOT="${JUNEST_HOME}/usr/bin/proot-${ARCH}" -GROOT="${JUNEST_HOME}/usr/bin/groot" -CLASSIC_CHROOT=chroot -WGET="wget --content-disposition --no-check-certificate" -CURL="curl -L -J -O -k" -TAR="tar" -CHOWN="chown" -LN="ln" -RM="rm" -MKDIR="mkdir" -GETENT="getent" -CP="cp" -ID="id" -# 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" - -# The following functions attempt first to run the executable in the host OS. -# As a last hope they try to run the same executable available in the JuNest -# image. - -function ln_cmd(){ - $LN "$@" || $LD_EXEC "${JUNEST_HOME}"/usr/bin/$LN "$@" -} - -function getent_cmd(){ - $GETENT "$@" || $LD_EXEC "${JUNEST_HOME}"/usr/bin/$GETENT "$@" -} - -function cp_cmd(){ - $CP "$@" || $LD_EXEC "${JUNEST_HOME}"/usr/bin/$CP "$@" -} - -function rm_cmd(){ - $RM "$@" || $LD_EXEC "${JUNEST_HOME}"/usr/bin/$RM "$@" -} - -function chown_cmd(){ - $CHOWN "$@" || $LD_EXEC "${JUNEST_HOME}"/usr/bin/$CHOWN "$@" -} - -function mkdir_cmd(){ - $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 DEFAULT_SH arguments (i.e. --login) as - # they are not supported by dash. - if $LD_EXEC "${JUNEST_HOME}"/usr/bin/$UNSHARE --user "${DEFAULT_SH[0]}" "-c" ":" - then - $LD_EXEC "${JUNEST_HOME}"/usr/bin/$UNSHARE "${@}" - elif $UNSHARE --user "${DEFAULT_SH[0]}" "-c" ":" - then - $UNSHARE "$@" - else - die "Error: Something went wrong while executing unshare command. Exiting" - fi -} - -function bwrap_cmd(){ - if $LD_EXEC "$BWRAP" --dev-bind / / "${DEFAULT_SH[0]}" "-c" ":" - then - $LD_EXEC "$BWRAP" "${@}" - else - die "Error: Something went wrong while executing bwrap command. Exiting" - fi -} - -function proot_cmd(){ - local proot_args="$1" - shift - # shellcheck disable=SC2086 - if ${PROOT} ${proot_args} "${DEFAULT_SH[@]}" "-c" ":" - then - # shellcheck disable=SC2086 - ${PROOT} ${proot_args} "${@}" - elif PROOT_NO_SECCOMP=1 ${PROOT} ${proot_args} "${DEFAULT_SH[@]}" "-c" ":" - then - warn "Warn: Proot is not properly working. Disabling SECCOMP and expect the application to run slowly in particular when it uses syscalls intensively." - warn "Try to use Linux namespace instead as it is more reliable: junest ns" - PROOT_NO_SECCOMP=1 ${PROOT} ${proot_args} "${@}" - else - die "Error: Something went wrong with proot command. Exiting" - fi -} - -############## 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 [[ -n $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 -# /etc/mtab file so that it will not give conflicts with the related -# symlink in the image. -# -# Globals: -# HOME (RO) : The home directory. -# RESULT (WO) : Contains the binding options. -# Arguments: -# None -# Returns: -# None -# Output: -# None -####################################### -function provide_common_bindings(){ - RESULT="" - local re='(.*):.*' - for bind in "/dev" "/sys" "/proc" "/tmp" "$HOME" "/run/user/$($ID -u)" - do - if [[ $bind =~ $re ]] - then - [ -e "${BASH_REMATCH[1]}" ] && RESULT="-b $bind $RESULT" - else - [ -e "$bind" ] && RESULT="-b $bind $RESULT" - fi - done - return 0 -} - -####################################### -# Build passwd and group files using getent command. -# If getent fails the function fallbacks by copying the content from /etc/passwd -# and /etc/group. -# -# The generated passwd and group will be stored in $JUNEST_HOME/etc/junest. -# -# Globals: -# JUNEST_HOME (RO) : The JuNest home directory. -# Arguments: -# None -# Returns: -# None -# Output: -# None -####################################### -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 - # a more reliable and faster system call (getpwnam(3)). - if ! getent_cmd passwd > "${JUNEST_HOME}"/etc/passwd || \ - ! 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 - 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 - fi - return 0 -} - -function copy_file() { - local file="${1}" - # -f option ensure to remove destination file if it cannot be opened - # https://github.com/fsquillace/junest/issues/284 - [[ -r "$file" ]] && cp_cmd -f "$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 - return 0 -} - -function prepare_archlinux() { - local sudo=${1:-sudo} - local pacman_options="--noconfirm --disable-download-timeout" - - # shellcheck disable=SC2086 - $sudo pacman $pacman_options -Syy - - $sudo pacman-key --init - - if [[ $(uname -m) == *"arm"* ]] - then - # shellcheck disable=SC2086 - $sudo pacman $pacman_options -S archlinuxarm-keyring - $sudo pacman-key --populate archlinuxarm - else - # shellcheck disable=SC2086 - $sudo pacman $pacman_options -S archlinux-keyring - $sudo pacman-key --populate archlinux - fi - - # shellcheck disable=SC2086 - $sudo pacman $pacman_options -Su -} diff --git a/lib/core/namespace.sh b/lib/core/namespace.sh deleted file mode 100644 index 70763bd..0000000 --- a/lib/core/namespace.sh +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/env bash -# -# This module contains functionalities for accessing to JuNest via bubblewrap. -# -# https://github.com/containers/bubblewrap -# -# Dependencies: -# - lib/utils/utils.sh -# - lib/core/common.sh -# -# vim: ft=sh - -# shellcheck disable=SC2027 -COMMON_BWRAP_OPTION="--bind "$JUNEST_HOME" / --bind "$HOME" "$HOME" --bind /tmp /tmp --bind /sys /sys --bind /proc /proc --dev-bind-try /dev /dev --bind-try "/run/user/$($ID -u)" "/run/user/$($ID -u)" --unshare-user-try" -CONFIG_PROC_FILE="/proc/config.gz" -CONFIG_BOOT_FILE="/boot/config-$($UNAME -r)" -PROC_USERNS_CLONE_FILE="/proc/sys/kernel/unprivileged_userns_clone" -PROC_USERNS_FILE="/proc/$$/ns/user" - -function _is_user_namespace_enabled() { - if [[ -L $PROC_USERNS_FILE ]] - then - return 0 - fi - - if [[ -e $PROC_USERNS_CLONE_FILE ]] - then - # `-q` option in zgrep may cause a gzip: stdout: Broken pipe - # Use redirect to /dev/null instead - if zgrep_cmd "1" "$PROC_USERNS_CLONE_FILE" > /dev/null - then - return 0 - fi - fi - - 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 - - # `-q` option in zgrep may cause a gzip: stdout: Broken pipe - # Use redirect to /dev/null instead - if ! zgrep_cmd "CONFIG_USER_NS=y" "$config_file" > /dev/null - then - return "$NO_CONFIG_FOUND" - fi - - return "$UNPRIVILEGED_USERNS_DISABLED" -} - -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 "Unprivileged user namespace is disabled at kernel compile time or kernel too old (<3.8). Proceeding anyway..." ;; - "$UNPRIVILEGED_USERNS_DISABLED") warn "Unprivileged user namespace disabled. Root permissions are required to enable it: sudo sysctl kernel.unprivileged_userns_clone=1" ;; - esac - set -e -} - - -####################################### -# Run JuNest as fakeroot via bwrap -# -# Globals: -# JUNEST_HOME (RO) : The JuNest home directory. -# DEFAULT_SH (RO) : Contains the default command to run in JuNest. -# BWRAP (RO): : The location of the bwrap binary. -# Arguments: -# backend_args ($1) : The arguments to pass to bwrap -# no_copy_files ($2?) : If false it will copy some files in /etc -# from host to JuNest environment. -# cmd ($3-?) : The command to run inside JuNest environment. -# Default command is defined by DEFAULT_SH variable. -# Returns: -# $ARCHITECTURE_MISMATCH : If host and JuNest architecture are different. -# $ROOT_ACCESS_ERROR : If the user is the real root. -# Output: -# - : The command output. -####################################### -function run_env_as_bwrap_fakeroot(){ - check_nested_env - - local backend_command="${1:-$BWRAP}" - local backend_args="$2" - local no_copy_files="$3" - shift 3 - - _check_user_namespace - - check_same_arch - - if ! $no_copy_files - then - copy_common_files - fi - - local args=() - [[ "$1" != "" ]] && args=("-c" "$(insert_quotes_on_spaces "${@}")") - - # Fix PATH to /usr/bin to make sudo working and avoid polluting with host related bin paths - # shellcheck disable=SC2086 - PATH="/usr/bin" BWRAP="${backend_command}" JUNEST_ENV=1 bwrap_cmd $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 $backend_args sudo "${DEFAULT_SH[@]}" "${args[@]}" -} - - -####################################### -# Run JuNest as normal user via bwrap. -# -# Globals: -# JUNEST_HOME (RO) : The JuNest home directory. -# DEFAULT_SH (RO) : Contains the default command to run in JuNest. -# BWRAP (RO): : The location of the bwrap binary. -# Arguments: -# backend_args ($1) : The arguments to pass to bwrap -# no_copy_files ($2?) : If false it will copy some files in /etc -# from host to JuNest environment. -# cmd ($3-?) : The command to run inside JuNest environment. -# Default command is defined by DEFAULT_SH variable. -# Returns: -# $ARCHITECTURE_MISMATCH : If host and JuNest architecture are different. -# Output: -# - : The command output. -####################################### -function run_env_as_bwrap_user() { - check_nested_env - - local backend_command="${1:-$BWRAP}" - local backend_args="$2" - local no_copy_files="$3" - shift 3 - - _check_user_namespace - - check_same_arch - - if ! $no_copy_files - then - 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 - fi - - local args=() - [[ "$1" != "" ]] && args=("-c" "$(insert_quotes_on_spaces "${@}")") - - # Resets PATH to avoid polluting with host related bin paths - # shellcheck disable=SC2086 - PATH='' BWRAP="${backend_command}" JUNEST_ENV=1 bwrap_cmd $COMMON_BWRAP_OPTION $backend_args "${DEFAULT_SH[@]}" "${args[@]}" -} - - - - diff --git a/lib/core/proot.sh b/lib/core/proot.sh deleted file mode 100644 index b6c1c8f..0000000 --- a/lib/core/proot.sh +++ /dev/null @@ -1,138 +0,0 @@ -#!/usr/bin/env bash -# shellcheck disable=SC1091 -# -# This module contains all proot functionalities for JuNest. -# -# Dependencies: -# - lib/utils/utils.sh -# - lib/core/common.sh -# -# vim: ft=sh - -function _run_env_with_proot(){ - local backend_command="${1:-$PROOT}" - local backend_args="$2" - shift 2 - - local args=() - [[ "$1" != "" ]] && args=("-c" "$(insert_quotes_on_spaces "${@}")") - - # Resets PATH to avoid polluting with host related bin paths - PATH='' PROOT="${backend_command}" JUNEST_ENV=1 proot_cmd "${backend_args}" "${DEFAULT_SH[@]}" "${args[@]}" -} - -function _run_env_with_qemu(){ - local backend_command="$1" - local backend_args="$2" - shift 2 - - source "${JUNEST_HOME}"/etc/junest/info - - if [ "$JUNEST_ARCH" != "$ARCH" ] - then - local qemu_bin="qemu-$JUNEST_ARCH-static-$ARCH" - local qemu_symlink="/tmp/${qemu_bin}-$RANDOM" - trap - QUIT EXIT ABRT KILL TERM INT - # shellcheck disable=SC2064 - trap "[ -e ${qemu_symlink} ] && rm_cmd -f ${qemu_symlink}" EXIT QUIT ABRT TERM INT - - warn "Emulating $NAME via QEMU..." - [[ -e ${qemu_symlink} ]] || \ - ln_cmd -s "${JUNEST_HOME}/bin/${qemu_bin}" "${qemu_symlink}" - backend_args="-q ${qemu_symlink} $backend_args" - fi - - _run_env_with_proot "${backend_command}" "$backend_args" "${@}" -} - -####################################### -# Run JuNest as fakeroot. -# -# Globals: -# JUNEST_HOME (RO) : The JuNest home directory. -# EUID (RO) : The user ID. -# DEFAULT_SH (RO) : Contains the default command to run in JuNest. -# Arguments: -# backend_args ($1) : The arguments to pass to proot -# no_copy_files ($2?) : If false it will copy some files in /etc -# from host to JuNest environment. -# cmd ($3-?) : The command to run inside JuNest environment. -# Default command is defined by DEFAULT_SH variable. -# Returns: -# $ROOT_ACCESS_ERROR : If the user is the real root. -# Output: -# - : The command output. -####################################### -function run_env_as_proot_fakeroot(){ - (( EUID == 0 )) && \ - die_on_status "$ROOT_ACCESS_ERROR" "You cannot access with root privileges. Use --groot option instead." - check_nested_env - - local backend_command="$1" - local backend_args="$2" - local no_copy_files="$3" - shift 3 - - if ! $no_copy_files - then - copy_common_files - fi - - 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 "$backend_command" "-0 ${bindings} -r ${JUNEST_HOME} $backend_args" "$@" -} - -####################################### -# Run JuNest as normal user. -# -# Globals: -# JUNEST_HOME (RO) : The JuNest home directory. -# EUID (RO) : The user ID. -# DEFAULT_SH (RO) : Contains the default command to run in JuNest. -# Arguments: -# backend_args ($1) : The arguments to pass to proot -# no_copy_files ($2?) : If false it will copy some files in /etc -# from host to JuNest environment. -# cmd ($3-?) : The command to run inside JuNest environment. -# Default command is defined by DEFAULT_SH variable. -# Returns: -# $ROOT_ACCESS_ERROR : If the user is the real root. -# Output: -# - : The command output. -####################################### -function run_env_as_proot_user(){ - (( EUID == 0 )) && \ - die_on_status "$ROOT_ACCESS_ERROR" "You cannot access with root privileges. Use --groot option instead." - check_nested_env - - local backend_command="$1" - local backend_args="$2" - local no_copy_files="$3" - shift 3 - - if ! $no_copy_files - then - # 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 - # No need for localtime as it is setup during the image build - #copy_file /etc/localtime - copy_passwd_and_group - fi - - provide_common_bindings - local bindings=${RESULT} - unset RESULT - - _run_env_with_qemu "$backend_command" "${bindings} -r ${JUNEST_HOME} $backend_args" "$@" -} diff --git a/lib/core/setup.sh b/lib/core/setup.sh deleted file mode 100644 index 58c6122..0000000 --- a/lib/core/setup.sh +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env bash -# -# This module contains all setup functionalities for JuNest. -# -# Dependencies: -# - lib/utils/utils.sh -# - lib/core/common.sh -# -# vim: ft=sh - -####################################### -# Check if the JuNest system is installed in JUNEST_HOME. -# -# Globals: -# JUNEST_HOME (RO) : Contains the JuNest home directory. -# Arguments: -# None -# Returns: -# 0 : If JuNest is installed -# 1 : If JuNest is not installed -# Output: -# None -####################################### -function is_env_installed(){ - [[ -d "$JUNEST_HOME" ]] && [[ "$(ls -A "$JUNEST_HOME")" ]] && return 0 - return 1 -} - - -function _cleanup_build_directory(){ - local maindir=$1 - check_not_null "$maindir" - builtin cd "$ORIGIN_WD" || return 1 - trap - QUIT EXIT ABRT KILL TERM INT - rm_cmd -fr "$maindir" -} - - -function _prepare_build_directory(){ - local maindir=$1 - check_not_null "$maindir" - trap - QUIT EXIT ABRT KILL TERM INT - # shellcheck disable=SC2064 - trap "rm_cmd -rf ${maindir}; die \"Error occurred when installing ${NAME}\"" EXIT QUIT ABRT TERM INT -} - - -function _setup_env(){ - local imagepath=$1 - check_not_null "$imagepath" - - is_env_installed && die "Error: ${NAME} has been already installed in $JUNEST_HOME" - - mkdir_cmd -p "${JUNEST_HOME}" - $TAR -zxpf "${imagepath}" -C "${JUNEST_HOME}" - info "${NAME} installed successfully!" - echo - info "Default mirror URL set to: ${DEFAULT_MIRROR}" - info "You can change the pacman mirror URL in /etc/pacman.d/mirrorlist according to your location:" - info " \$EDITOR ${JUNEST_HOME}/etc/pacman.d/mirrorlist" - echo - info "Remember to refresh the package databases from the server:" - info " pacman -Syy" - echo - info "To install packages from AUR follow the wiki here:" - info "https://github.com/fsquillace/junest#install-packages-from-aur" -} - - -####################################### -# Setup JuNest. -# -# Globals: -# JUNEST_HOME (RO) : The JuNest home directory in which JuNest needs -# to be installed. -# ARCH (RO) : The host architecture. -# JUNEST_TEMPDIR (RO) : The JuNest temporary directory for building -# the JuNest system from the image. -# ENV_REPO (RO) : URL of the site containing JuNest images. -# NAME (RO) : The JuNest name. -# Arguments: -# arch ($1?) : The JuNest architecture image to download. -# Defaults to the host architecture -# Returns: -# $NOT_AVAILABLE_ARCH : If the architecture is not one of the available ones. -# Output: -# None -####################################### -function setup_env(){ - local arch=${1:-$ARCH} - contains_element "$arch" "${ARCH_LIST[@]}" || \ - die_on_status "$NOT_AVAILABLE_ARCH" "The architecture is not one of: ${ARCH_LIST[*]}" - - local maindir - maindir=$(TMPDIR=$JUNEST_TEMPDIR mktemp -d -t "${CMD}".XXXXXXXXXX) - _prepare_build_directory "$maindir" - - info "Downloading ${NAME}..." - builtin cd "${maindir}" || return 1 - local imagefile=${CMD}-${arch}.tar.gz - download_cmd "${ENV_REPO}/${imagefile}" - - info "Installing ${NAME}..." - _setup_env "${maindir}/${imagefile}" - - _cleanup_build_directory "${maindir}" -} - -####################################### -# Setup JuNest from file. -# -# Globals: -# JUNEST_HOME (RO) : The JuNest home directory in which JuNest needs -# to be installed. -# NAME (RO) : The JuNest name. -# Arguments: -# imagefile ($1) : The JuNest image file. -# Returns: -# $NOT_EXISTING_FILE : If the image file does not exist. -# Output: -# None -####################################### -function setup_env_from_file(){ - local imagefile=$1 - check_not_null "$imagefile" - [[ ! -e ${imagefile} ]] && die_on_status "$NOT_EXISTING_FILE" "Error: The ${NAME} image file ${imagefile} does not exist" - - info "Installing ${NAME} from ${imagefile}..." - _setup_env "${imagefile}" -} - -####################################### -# Remove an existing JuNest system. -# -# Globals: -# JUNEST_HOME (RO) : The JuNest home directory to remove. -# Arguments: -# None -# Returns: -# None -# Output: -# None -####################################### -function delete_env(){ - ! ask "Are you sure to delete ${NAME} located in ${JUNEST_HOME}" "N" && return - if mountpoint -q "${JUNEST_HOME}" - then - info "There are mounted directories inside ${JUNEST_HOME}" - if ! umount --force "${JUNEST_HOME}" - then - error "Cannot umount directories in ${JUNEST_HOME}" - die "Try to delete ${NAME} using root permissions" - fi - fi - # the CA directories are read only and can be deleted only by changing the mod - chmod -R +w "${JUNEST_HOME}"/etc/ca-certificates - if rm_cmd -rf "${JUNEST_HOME}" - then - info "${NAME} deleted in ${JUNEST_HOME}" - else - error "Error: Cannot delete ${NAME} in ${JUNEST_HOME}" - fi -} - diff --git a/lib/core/wrappers.sh b/lib/core/wrappers.sh deleted file mode 100644 index 1fe955c..0000000 --- a/lib/core/wrappers.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env bash -# -# Dependencies: -# None -# -# vim: ft=sh - -####################################### -# Create bin wrappers -# -# Globals: -# JUNEST_HOME (RO) : The JuNest home directory. -# Arguments: -# force ($1?) : Create bin wrappers even if the bin file exists. -# Defaults to false. -# Returns: -# None -# Output: -# None -####################################### -function create_wrappers() { - local force=${1:-false} - local bin_path=${2:-/usr/bin} - bin_path=${bin_path%/} - mkdir -p "${JUNEST_HOME}${bin_path}_wrappers" - # Arguments inside a variable (i.e. `JUNEST_ARGS`) separated by quotes - # are not recognized normally unless using `eval`. More info here: - # https://github.com/fsquillace/junest/issues/262 - # https://github.com/fsquillace/junest/pull/287 - cat < "${JUNEST_HOME}/usr/bin/junest_wrapper" -#!/usr/bin/env bash - -eval "junest_args_array=(\${JUNEST_ARGS:-ns})" -junest "\${junest_args_array[@]}" -- \$(basename \${0}) "\$@" -EOF - chmod +x "${JUNEST_HOME}/usr/bin/junest_wrapper" - - cd "${JUNEST_HOME}${bin_path}" || return 1 - for file in * - do - [[ -d $file ]] && continue - # Symlinks outside junest appear as broken even though they are correct - # within a junest session. The following do not skip broken symlinks: - [[ -x $file || -L $file ]] || continue - if [[ -e ${JUNEST_HOME}${bin_path}_wrappers/$file ]] && ! $force - then - continue - fi - rm -f "${JUNEST_HOME}${bin_path}_wrappers/$file" - ln -s "${JUNEST_HOME}/usr/bin/junest_wrapper" "${JUNEST_HOME}${bin_path}_wrappers/$file" - done - - # Remove wrappers no longer needed - cd "${JUNEST_HOME}${bin_path}_wrappers" || return 1 - for file in * - do - [[ -e ${JUNEST_HOME}${bin_path}/$file || -L ${JUNEST_HOME}${bin_path}/$file ]] || rm -f "$file" - done - -} diff --git a/lib/utils/utils.sh b/lib/utils.sh similarity index 84% rename from lib/utils/utils.sh rename to lib/utils.sh index 5659568..840fbe9 100644 --- a/lib/utils/utils.sh +++ b/lib/utils.sh @@ -50,7 +50,7 @@ function echoerr() { # Message printed to stderr. ####################################### function die() { - error "$@" + error $@ exit 1 } @@ -70,8 +70,8 @@ function die() { function die_on_status() { status=$1 shift - error "$@" - exit "$status" + error $@ + exit $status } ####################################### @@ -87,7 +87,7 @@ function die_on_status() { # Message printed to stderr. ####################################### function error() { - echoerr -e "\033[1;31m$*\033[0m" + echoerr -e "\033[1;31m$@\033[0m" } ####################################### @@ -104,7 +104,7 @@ function error() { ####################################### function warn() { # $@: msg (mandatory) - str: Message to print - echoerr -e "\033[1;33m$*\033[0m" + echoerr -e "\033[1;33m$@\033[0m" } ####################################### @@ -120,7 +120,7 @@ function warn() { # Message printed to stdout. ####################################### function info(){ - echo -e "\033[1;36m$*\033[0m" + echo -e "\033[1;36m$@\033[0m" } ####################################### @@ -142,12 +142,12 @@ function info(){ function ask(){ local question=$1 local default_answer=$2 - check_not_null "$question" + check_not_null $question - if [ -n "$default_answer" ] + if [ ! -z "$default_answer" ] then local answers="Y y N n" - [[ "$answers" =~ $default_answer ]] || { error "The default answer: $default_answer is wrong."; return $WRONG_ANSWER; } + [[ "$answers" =~ "$default_answer" ]] || { error "The default answer: $default_answer is wrong."; return $WRONG_ANSWER; } fi local default="Y" @@ -156,13 +156,12 @@ function ask(){ local other="n" [ "$default" == "N" ] && other="y" - local prompt - prompt=$(info "$question (${default}/${other})> ") + local prompt=$(info "$question (${default}/${other})> ") local res="none" while [ "$res" != "Y" ] && [ "$res" != "N" ] && [ "$res" != "" ]; do - read -r -p "$prompt" res + read -p "$prompt" res res=$(echo "$res" | tr '[:lower:]' '[:upper:]') done @@ -175,27 +174,18 @@ function insert_quotes_on_spaces(){ # It inserts quotes between arguments. # Useful to preserve quotes on command # to be used inside sh -c/bash -c - local C="" + C='' whitespace="[[:space:]]" for i in "$@" do if [[ $i =~ $whitespace ]] then - temp_C="\"$i\"" + C="$C \"$i\"" else - temp_C="$i" + C="$C $i" fi - - # Handle edge case when C is empty to avoid adding an extra space - if [[ -z $C ]] - then - C="$temp_C" - else - C="$C $temp_C" - fi - done - echo "$C" + echo $C } contains_element () { diff --git a/tests/checkstyle/checkstyle.sh b/tests/checkstyle/checkstyle.sh deleted file mode 100755 index 4f71965..0000000 --- a/tests/checkstyle/checkstyle.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash -# shellcheck disable=SC1091 - -source "$(dirname "$0")/../utils/utils.sh" - -# Disable the exiterr -set +e - -function oneTimeSetUp(){ - setUpUnitTests -} - -function test_check_no_tabs(){ - assertCommandFailOnStatus 1 grep -R "$(printf '\t')" "$(dirname "$0")"/../../bin/* - assertEquals "" "$(cat "$STDOUTF")" - assertEquals "" "$(cat "$STDERRF")" - assertCommandFailOnStatus 1 grep -R "$(printf '\t')" "$(dirname "$0")"/../../lib/* - assertEquals "" "$(cat "$STDOUTF")" - assertEquals "" "$(cat "$STDERRF")" -} - -source "$(dirname "$0")"/../utils/shunit2 diff --git a/tests/integ-tests/install-bash.sh b/tests/integ-tests/install-bash.sh new file mode 100755 index 0000000..575c9c5 --- /dev/null +++ b/tests/integ-tests/install-bash.sh @@ -0,0 +1,13 @@ +#!/bin/sh +set -ex + +VERSION=$1 + +cd /tmp +wget http://ftp.gnu.org/gnu/bash/bash-$VERSION.tar.gz + +tar -zxf bash-$VERSION.tar.gz +cd /tmp/bash-$VERSION* +./configure +make +sudo make install diff --git a/tests/utils/shunit2 b/tests/unit-tests/shunit2 similarity index 99% rename from tests/utils/shunit2 rename to tests/unit-tests/shunit2 index e4c719c..d6e7503 100644 --- a/tests/utils/shunit2 +++ b/tests/unit-tests/shunit2 @@ -987,7 +987,7 @@ _shunit_extractTestFunctions() # extract the lines with test function names, strip of anything besides the # function name, and output everything on a single line. _shunit_regex_='^[ ]*(function )*test[A-Za-z0-9_]* *\(\)' - grep -E "${_shunit_regex_}" "${_shunit_script_}" \ + egrep "${_shunit_regex_}" "${_shunit_script_}" \ |sed 's/^[^A-Za-z0-9_]*//;s/^function //;s/\([A-Za-z0-9_]*\).*/\1/g' \ |xargs diff --git a/tests/unit-tests/test-chroot.sh b/tests/unit-tests/test-chroot.sh deleted file mode 100755 index 3739c58..0000000 --- a/tests/unit-tests/test-chroot.sh +++ /dev/null @@ -1,125 +0,0 @@ -#!/bin/bash -# shellcheck disable=SC1091 - -JUNEST_ROOT=$(readlink -f "$(dirname "$0")"/../..) - -source "$JUNEST_ROOT/tests/utils/utils.sh" - -source "$JUNEST_ROOT/lib/utils/utils.sh" -source "$JUNEST_ROOT/lib/core/common.sh" -source "$JUNEST_ROOT/lib/core/chroot.sh" - -# Disable the exiterr -set +e - -function oneTimeSetUp(){ - setUpUnitTests -} - -function setUp(){ - cwdSetUp - junestSetUp - init_mocks -} - -function tearDown(){ - junestTearDown - cwdTearDown -} - -function init_mocks() { - chroot_cmd() { - # shellcheck disable=SC2317 - [ "$JUNEST_ENV" != "1" ] && return 1 - # shellcheck disable=SC2317 - echo "chroot_cmd $*" - } - # shellcheck disable=SC2034 - GROOT=chroot_cmd - mychroot() { - # shellcheck disable=SC2317 - echo mychroot "$*" - } -} - -function test_run_env_as_groot_cmd(){ - assertCommandSuccess run_env_as_groot "" "" "false" pwd - assertEquals "chroot_cmd -b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c pwd" "$(cat "$STDOUTF")" -} - -function test_run_env_as_groot_no_cmd(){ - assertCommandSuccess run_env_as_groot "" "" "false" "" - assertEquals "chroot_cmd -b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login" "$(cat "$STDOUTF")" -} - -function test_run_env_as_groot_with_backend_command(){ - assertCommandSuccess run_env_as_groot "mychroot" "" "false" "" - assertEquals "mychroot -b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login" "$(cat "$STDOUTF")" -} - -function test_run_env_as_groot_no_copy(){ - assertCommandSuccess run_env_as_groot "" "" "true" pwd - assertEquals "chroot_cmd -b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c pwd" "$(cat "$STDOUTF")" - - [[ ! -e ${JUNEST_HOME}/etc/hosts ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/host.conf ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/nsswitch.conf ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/resolv.conf ]] - assertEquals 0 $? -} - -function test_run_env_as_groot_nested_env(){ - JUNEST_ENV=1 - assertCommandFailOnStatus 106 run_env_as_groot "" "" "false" "" - unset JUNEST_ENV -} - -function test_run_env_as_groot_cmd_with_backend_args(){ - assertCommandSuccess run_env_as_groot "" "-n -b /home/blah" "false" pwd - assertEquals "chroot_cmd -b /run/user/$(id -u) -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 "" "" "false" 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 "" "" "false" "" - assertEquals "chroot_cmd $JUNEST_HOME /bin/sh --login" "$(cat "$STDOUTF")" -} - -function test_run_env_as_chroot_with_backend_command(){ - assertCommandSuccess run_env_as_chroot "mychroot" "" "false" "" - assertEquals "mychroot $JUNEST_HOME /bin/sh --login" "$(cat "$STDOUTF")" -} - -function test_run_env_as_chroot_no_copy(){ - assertCommandSuccess run_env_as_chroot "" "" "true" pwd - assertEquals "chroot_cmd $JUNEST_HOME /bin/sh --login -c pwd" "$(cat "$STDOUTF")" - - [[ ! -e ${JUNEST_HOME}/etc/hosts ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/host.conf ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/nsswitch.conf ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/resolv.conf ]] - assertEquals 0 $? -} - -function test_run_env_as_choot_nested_env(){ - JUNEST_ENV=1 - assertCommandFailOnStatus 106 run_env_as_chroot "" "" "false" "" - unset JUNEST_ENV -} - -function test_run_env_as_chroot_cmd_with_backend_args(){ - assertCommandSuccess run_env_as_chroot "" "-n -b /home/blah" "false" pwd - assertEquals "chroot_cmd -n -b /home/blah $JUNEST_HOME /bin/sh --login -c pwd" "$(cat "$STDOUTF")" -} - -source "$JUNEST_ROOT"/tests/utils/shunit2 diff --git a/tests/unit-tests/test-cli.sh b/tests/unit-tests/test-cli.sh new file mode 100755 index 0000000..5dd2513 --- /dev/null +++ b/tests/unit-tests/test-cli.sh @@ -0,0 +1,181 @@ +#!/bin/bash +source "$(dirname $0)/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)/shunit2 diff --git a/tests/unit-tests/test-common.sh b/tests/unit-tests/test-common.sh deleted file mode 100755 index 89c1a1e..0000000 --- a/tests/unit-tests/test-common.sh +++ /dev/null @@ -1,242 +0,0 @@ -#!/bin/bash -# shellcheck disable=SC1091 - -JUNEST_ROOT=$(readlink -f "$(dirname "$0")"/../..) - -source "$JUNEST_ROOT/tests/utils/utils.sh" - -source "$JUNEST_ROOT/lib/utils/utils.sh" -source "$JUNEST_ROOT/lib/core/common.sh" - -# Disable the exiterr -set +e - -function oneTimeSetUp(){ - setUpUnitTests - junestSetUp -} - -function oneTimeTearDown(){ - junestTearDown -} - -function setUp(){ - ld_exec_mock() { - # shellcheck disable=SC2317 - echo "ld_exec $*" - } - # shellcheck disable=SC2317 - ld_exec_mock_false() { - echo "ld_exec $*" - return 1 - } - # shellcheck disable=SC2034 - LD_EXEC=ld_exec_mock - - unshare_mock() { - # shellcheck disable=SC2317 - echo "unshare $*" - } - # shellcheck disable=SC2034 - UNSHARE=unshare_mock - - # shellcheck disable=SC2317 - bwrap_mock() { - echo "bwrap $*" - } - # shellcheck disable=SC2034 - BWRAP=bwrap_mock - -} - -function test_ln(){ - LN="echo" assertCommandSuccess ln_cmd -s ln_file new_file - assertEquals "-s ln_file new_file" "$(cat "$STDOUTF")" - - LN=false assertCommandSuccess ln_cmd -s ln_file new_file - assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false -s ln_file new_file" "$(cat "$STDOUTF")" - - LN=false LD_EXEC=false assertCommandFail ln_cmd -} - -function test_getent(){ - GETENT="echo" assertCommandSuccess getent_cmd passwd - assertEquals "passwd" "$(cat "$STDOUTF")" - - GETENT="false" assertCommandSuccess getent_cmd passwd - assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false passwd" "$(cat "$STDOUTF")" - - GETENT=false LD_EXEC=false assertCommandFail getent_cmd -} - -function test_cp(){ - CP="echo" assertCommandSuccess cp_cmd passwd - assertEquals "passwd" "$(cat "$STDOUTF")" - - CP=false assertCommandSuccess cp_cmd passwd - assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false passwd" "$(cat "$STDOUTF")" - - CP=false LD_EXEC=false assertCommandFail cp_cmd -} - -function test_download(){ - WGET=/bin/true - CURL=/bin/false - assertCommandSuccess download_cmd - - # shellcheck disable=SC2034 - WGET=/bin/false - # shellcheck disable=SC2034 - CURL=/bin/true - assertCommandSuccess download_cmd - - WGET=/bin/false CURL=/bin/false assertCommandFail download_cmd -} - -function test_rm(){ - RM="echo" assertCommandSuccess rm_cmd rm_file - assertEquals "rm_file" "$(cat "$STDOUTF")" - - RM="false" assertCommandSuccess rm_cmd rm_file - assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false rm_file" "$(cat "$STDOUTF")" - - RM=false LD_EXEC=false assertCommandFail rm_cmd rm_file -} - -function test_chown(){ - local id - id=$(id -u) - - CHOWN="echo" assertCommandSuccess chown_cmd "$id" chown_file - assertEquals "$id chown_file" "$(cat "$STDOUTF")" - - CHOWN="false" assertCommandSuccess chown_cmd "$id" chown_file - assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false $id chown_file" "$(cat "$STDOUTF")" - - CHOWN=false LD_EXEC=false assertCommandFail chown_cmd "$id" chown_file -} - -function test_mkdir(){ - MKDIR="echo" assertCommandSuccess mkdir_cmd -p new_dir/new_dir - assertEquals "-p new_dir/new_dir" "$(cat "$STDOUTF")" - - MKDIR=false assertCommandSuccess mkdir_cmd -p new_dir/new_dir - assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false -p new_dir/new_dir" "$(cat "$STDOUTF")" - - 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_bwrap(){ - assertCommandSuccess bwrap_cmd new_program - assertEquals "$(echo -e "ld_exec $BWRAP --dev-bind / / /bin/sh -c :\nld_exec $BWRAP new_program")" "$(cat "$STDOUTF")" - - BWRAP=false LD_EXEC=false assertCommandFail bwrap_cmd new_program -} - -function test_chroot(){ - 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")" - - CLASSIC_CHROOT=false LD_EXEC=false assertCommandFail chroot_cmd root -} - -function test_proot_cmd_compat(){ - PROOT="/bin/true" assertCommandSuccess proot_cmd "" "" - - PROOT="/bin/false" assertCommandFail proot_cmd --helps -} - -function test_proot_cmd_seccomp(){ - envv(){ - # shellcheck disable=SC2317 - env - } - PROOT=envv - assertCommandSuccess proot_cmd cmd - assertEquals "" "$(grep "^PROOT_NO_SECCOMP" "$STDOUTF")" - - envv(){ - # shellcheck disable=SC2317 - env | grep "^PROOT_NO_SECCOMP" - } - # shellcheck disable=SC2034 - PROOT=envv - assertCommandSuccess proot_cmd cmd - # The variable PROOT_NO_SECCOMP will be produced - # twice due to the fallback mechanism - assertEquals "PROOT_NO_SECCOMP=1 -PROOT_NO_SECCOMP=1" "$(grep "^PROOT_NO_SECCOMP" "$STDOUTF")" -} - -function test_copy_passwd_and_group(){ - getent_cmd_mock() { - # shellcheck disable=SC2317 - echo "$*" - } - 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)" -} - -function test_copy_passwd_and_group_fallback(){ - cp_cmd_mock() { - # shellcheck disable=SC2317 - echo "$*" - } - CP=cp_cmd_mock GETENT=false LD_EXEC=false assertCommandSuccess copy_passwd_and_group - assertEquals "$(echo -e "-f /etc/passwd $JUNEST_HOME//etc/passwd\n-f /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 -} - -function test_nested_env(){ - JUNEST_ENV=1 assertCommandFailOnStatus 106 check_nested_env -} - -function test_nested_env_not_set_variable(){ - 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 diff --git a/tests/unit-tests/test-core.sh b/tests/unit-tests/test-core.sh new file mode 100755 index 0000000..a009510 --- /dev/null +++ b/tests/unit-tests/test-core.sh @@ -0,0 +1,351 @@ +#!/bin/bash + +JUNEST_ROOT=$(readlink -f $(dirname $0)/../..) + +source "$JUNEST_ROOT/tests/unit-tests/utils.sh" + +# Disable the exiterr +set +e + +function oneTimeSetUp(){ + SKIP_ROOT_TESTS=${SKIP_ROOT_TESTS:-0} + setUpUnitTests +} + +function setUp(){ + ORIGIN_CWD=$(TMPDIR=/tmp mktemp -d -t junest-cwd.XXXXXXXXXX) + cd $ORIGIN_CWD + JUNEST_HOME=$(TMPDIR=/tmp mktemp -d -t junest-home.XXXXXXXXXX) + mkdir -p ${JUNEST_HOME}/etc/junest + echo "JUNEST_ARCH=x86_64" > ${JUNEST_HOME}/etc/junest/info + mkdir -p ${JUNEST_HOME}/etc/ca-certificates + JUNEST_TEMPDIR=$(TMPDIR=/tmp mktemp -d -t junest-temp.XXXXXXXXXX) + source "$JUNEST_ROOT/lib/utils.sh" + source "$JUNEST_ROOT/lib/core.sh" + + set +e + + trap - QUIT EXIT ABRT KILL TERM INT + trap "rm -rf ${JUNEST_HOME}; rm -rf ${JUNEST_TEMPDIR}" EXIT QUIT ABRT KILL TERM INT + + ld_exec() { + echo "ld_exec $@" + } + LD_EXEC=ld_exec +} + + +function tearDown(){ + # 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 + rm -rf $JUNEST_TEMPDIR + rm -rf $ORIGIN_CWD + trap - QUIT EXIT ABRT KILL TERM INT +} + + +function test_ln(){ + LN=echo assertCommandSuccess ln_cmd -s ln_file new_file + assertEquals "-s ln_file new_file" "$(cat $STDOUTF)" + + LN=false assertCommandSuccess ln_cmd -s ln_file new_file + assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false -s ln_file new_file" "$(cat $STDOUTF)" + + LN=false LD_EXEC=false assertCommandFail ln_cmd +} + +function test_getent(){ + GETENT=echo assertCommandSuccess getent_cmd passwd + assertEquals "passwd" "$(cat $STDOUTF)" + + GETENT=false assertCommandSuccess getent_cmd passwd + assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false passwd" "$(cat $STDOUTF)" + + GETENT=false LD_EXEC=false assertCommandFail getent_cmd +} + +function test_cp(){ + CP=echo assertCommandSuccess cp_cmd passwd + assertEquals "passwd" "$(cat $STDOUTF)" + + CP=false assertCommandSuccess cp_cmd passwd + assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false passwd" "$(cat $STDOUTF)" + + CP=false LD_EXEC=false assertCommandFail cp_cmd +} + +function test_download(){ + WGET=/bin/true + CURL=/bin/false + assertCommandSuccess download_cmd + + WGET=/bin/false + CURL=/bin/true + assertCommandSuccess download_cmd + + WGET=/bin/false CURL=/bin/false assertCommandFail download_cmd +} + +function test_rm(){ + RM=echo assertCommandSuccess rm_cmd rm_file + assertEquals "rm_file" "$(cat $STDOUTF)" + + RM=false assertCommandSuccess rm_cmd rm_file + assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false rm_file" "$(cat $STDOUTF)" + + RM=false LD_EXEC=false assertCommandFail rm_cmd rm_file +} + +function test_chown(){ + local id=$(id -u) + + CHOWN=echo assertCommandSuccess chown_cmd $id chown_file + assertEquals "$id chown_file" "$(cat $STDOUTF)" + + CHOWN=false assertCommandSuccess chown_cmd $id chown_file + assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false $id chown_file" "$(cat $STDOUTF)" + + CHOWN=false LD_EXEC=false assertCommandFail chown_cmd $id chown_file +} + +function test_mkdir(){ + MKDIR=echo assertCommandSuccess mkdir_cmd -p new_dir/new_dir + assertEquals "-p new_dir/new_dir" "$(cat $STDOUTF)" + + MKDIR=false assertCommandSuccess mkdir_cmd -p new_dir/new_dir + assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false -p new_dir/new_dir" "$(cat $STDOUTF)" + + MKDIR=false LD_EXEC=false assertCommandFail mkdir_cmd -p new_dir/new_dir +} + +function test_chroot(){ + CHROOT=echo assertCommandSuccess chroot_cmd root + assertEquals "root" "$(cat $STDOUTF)" + + CHROOT=false CLASSIC_CHROOT=echo assertCommandSuccess chroot_cmd root + assertEquals "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 +} + +function test_proot_cmd_compat(){ + PROOT="/bin/true" assertCommandSuccess proot_cmd "" "" + + PROOT="/bin/false" assertCommandFail proot_cmd --helps +} + +function test_proot_cmd_seccomp(){ + envv(){ + env + } + PROOT=envv + assertCommandSuccess proot_cmd cmd + assertEquals "" "$(cat $STDOUTF | grep "^PROOT_NO_SECCOMP")" + + envv(){ + env | grep "^PROOT_NO_SECCOMP" + } + PROOT=envv + local output=$(proot_cmd | grep "^PROOT_NO_SECCOMP") + assertCommandSuccess proot_cmd cmd + # The variable PROOT_NO_SECCOMP will be produced + # twice due to the fallback mechanism + assertEquals "PROOT_NO_SECCOMP=1 +PROOT_NO_SECCOMP=1" "$(cat $STDOUTF | grep "^PROOT_NO_SECCOMP")" +} + +function test_is_env_installed(){ + rm -rf $JUNEST_HOME/* + assertCommandFail is_env_installed + touch $JUNEST_HOME/just_file + assertCommandSuccess is_env_installed +} + +function test_setup_env(){ + rm -rf $JUNEST_HOME/* + wget_mock(){ + # Proof that the setup is happening + # inside $JUNEST_TEMPDIR + local cwd=${PWD#${JUNEST_TEMPDIR}} + local parent_dir=${PWD%${cwd}} + assertEquals "$JUNEST_TEMPDIR" "${parent_dir}" + touch file + tar -czvf ${CMD}-${ARCH}.tar.gz file + } + WGET=wget_mock + setup_env 1> /dev/null + assertTrue "[ -e $JUNEST_HOME/file ]" + + assertCommandFailOnStatus 102 setup_env "noarch" +} + + +function test_setup_env_from_file(){ + rm -rf $JUNEST_HOME/* + touch file + tar -czvf ${CMD}-${ARCH}.tar.gz file 1> /dev/null + assertCommandSuccess setup_env_from_file ${CMD}-${ARCH}.tar.gz + assertTrue "[ -e $JUNEST_HOME/file ]" +} + +function test_setup_env_from_file_not_existing_file(){ + assertCommandFailOnStatus 103 setup_env_from_file noexist.tar.gz +} + +function test_setup_env_from_file_with_absolute_path(){ + rm -rf $JUNEST_HOME/* + touch file + tar -czvf ${CMD}-${ARCH}.tar.gz file 1> /dev/null + assertCommandSuccess setup_env_from_file ${ORIGIN_WD}/${CMD}-${ARCH}.tar.gz + assertTrue "[ -e $JUNEST_HOME/file ]" +} + +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() { + chroot_cmd() { + [ "$JUNEST_ENV" != "1" ] && return 1 + echo $@ + } + + assertCommandSuccess run_env_as_root $@ +} + +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_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_user(){ + _run_env_with_qemu() { + echo $@ + } + assertCommandSuccess run_env_as_user "-k 3.10" "/usr/bin/mkdir" "-v" "/newdir2" + _provide_bindings_as_user + assertEquals "${RESULT}-r ${JUNEST_HOME} -k 3.10 /usr/bin/mkdir -v /newdir2" "$(cat $STDOUTF)" + + SH=("/usr/bin/echo") + assertCommandSuccess run_env_as_user "-k 3.10" + _provide_bindings_as_user + assertEquals "${RESULT}-r ${JUNEST_HOME} -k 3.10" "$(cat $STDOUTF)" +} + +function test_provide_bindings_as_user_no_junest_home(){ + _provide_bindings_as_user + echo "$RESULT" | grep -q "$JUNEST_HOME/etc/junest/passwd" + assertEquals 1 $? + echo "$RESULT" | grep -q "$JUNEST_HOME/etc/junest/group" + assertEquals 1 $? +} + +function test_provide_bindings_as_user(){ + touch $JUNEST_HOME/etc/junest/passwd + touch $JUNEST_HOME/etc/junest/group + _provide_bindings_as_user + echo "$RESULT" | grep -q "$JUNEST_HOME/etc/junest/passwd" + assertEquals 0 $? + echo "$RESULT" | grep -q "$JUNEST_HOME/etc/junest/group" + assertEquals 0 $? +} + +function test_build_passwd_and_group(){ + getent_cmd_mock() { + echo $@ + } + GETENT=getent_cmd_mock assertCommandSuccess _build_passwd_and_group + assertEquals "passwd" "$(cat $JUNEST_HOME/etc/junest/passwd)" + assertEquals "group" "$(cat $JUNEST_HOME/etc/junest/group)" +} + +function test_build_passwd_and_group_fallback(){ + cp_cmd_mock() { + echo $@ + } + CP=cp_cmd_mock GETENT=false LD_EXEC=false assertCommandSuccess _build_passwd_and_group + assertEquals "$(echo -e "/etc/passwd $JUNEST_HOME/etc/junest/passwd\n/etc/group $JUNEST_HOME/etc/junest/group")" "$(cat $STDOUTF)" +} + +function test_build_passwd_and_group_failure(){ + CP=false GETENT=false LD_EXEC=false assertCommandFailOnStatus 1 _build_passwd_and_group +} + +function test_run_env_as_fakeroot(){ + _run_env_with_qemu() { + echo $@ + } + assertCommandSuccess run_env_as_fakeroot "-k 3.10" "/usr/bin/mkdir" "-v" "/newdir2" + assertEquals "-S ${JUNEST_HOME} -k 3.10 /usr/bin/mkdir -v /newdir2" "$(cat $STDOUTF)" + + SH=("/usr/bin/echo") + assertCommandSuccess run_env_as_fakeroot "-k 3.10" + assertEquals "-S ${JUNEST_HOME} -k 3.10" "$(cat $STDOUTF)" +} + +function test_run_env_with_quotes(){ + _run_env_with_qemu() { + echo $@ + } + assertCommandSuccess run_env_as_user "-k 3.10" "bash" "-c" "/usr/bin/mkdir -v /newdir2" + _provide_bindings_as_user + assertEquals "${RESULT}-r ${JUNEST_HOME} -k 3.10 bash -c /usr/bin/mkdir -v /newdir2" "$(cat $STDOUTF)" +} + +function test_run_env_with_proot_args(){ + proot_cmd() { + [ "$JUNEST_ENV" != "1" ] && return 1 + echo $@ + } + + assertCommandSuccess _run_env_with_proot --help + assertEquals "--help /bin/sh --login" "$(cat $STDOUTF)" + + assertCommandSuccess _run_env_with_proot --help mycommand + assertEquals "--help /bin/sh --login -c mycommand" "$(cat $STDOUTF)" + + assertCommandFail _run_env_with_proot +} + +function test_delete_env(){ + echo "N" | delete_env 1> /dev/null + assertCommandSuccess is_env_installed + echo "Y" | delete_env 1> /dev/null + assertCommandFail is_env_installed +} + +function test_nested_env(){ + JUNEST_ENV=1 assertCommandFailOnStatus 106 bash -ic "source $JUNEST_ROOT/lib/utils.sh; source $JUNEST_ROOT/lib/core.sh" +} + +function test_nested_env_not_set_variable(){ + JUNEST_ENV=aaa assertCommandFailOnStatus 107 bash -ic "source $JUNEST_ROOT/lib/utils.sh; source $JUNEST_ROOT/lib/core.sh" +} + +function test_qemu() { + echo "JUNEST_ARCH=arm" > ${JUNEST_HOME}/etc/junest/info + rm_cmd() { + echo $@ + } + ln_cmd() { + echo $@ + } + _run_env_with_proot() { + echo $@ + } + + RANDOM=100 ARCH=x86_64 assertCommandSuccess _run_env_with_qemu "" + assertEquals "$(echo -e "-s $JUNEST_HOME/opt/qemu/qemu-arm-static-x86_64 /tmp/qemu-arm-static-x86_64-100\n-q /tmp/qemu-arm-static-x86_64-100")" "$(cat $STDOUTF)" +} + +source $JUNEST_ROOT/tests/unit-tests/shunit2 diff --git a/tests/unit-tests/test-junest.sh b/tests/unit-tests/test-junest.sh deleted file mode 100755 index 07a92b1..0000000 --- a/tests/unit-tests/test-junest.sh +++ /dev/null @@ -1,383 +0,0 @@ -#!/bin/bash -# shellcheck disable=SC1091 - -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(){ - ## Mock functions ## - # shellcheck disable=SC2317 - function usage(){ - echo "usage" - } - # shellcheck disable=SC2317 - function version(){ - echo "version" - } - # shellcheck disable=SC2317 - function build_image_env(){ - local disable_check=$1 - echo "build_image_env($disable_check)" - } - # shellcheck disable=SC2317 - function delete_env(){ - echo "delete_env" - } - # shellcheck disable=SC2317 - function setup_env_from_file(){ - echo "setup_env_from_file($1)" - } - # shellcheck disable=SC2317 - function setup_env(){ - echo "setup_env($1)" - } - # shellcheck disable=SC2317 - function run_env_as_proot_fakeroot(){ - local backend_command="$1" - local backend_args="$2" - local no_copy_files="$3" - shift 3 - echo "run_env_as_proot_fakeroot($backend_command,$backend_args,$no_copy_files,$*)" - } - # shellcheck disable=SC2317 - function run_env_as_groot(){ - local backend_command="$1" - local backend_args="$2" - local no_copy_files="$3" - shift 3 - echo "run_env_as_groot($backend_command,$backend_args,$no_copy_files,$*)" - } - # shellcheck disable=SC2317 - function run_env_as_chroot(){ - local backend_command="$1" - local backend_args="$2" - local no_copy_files="$3" - shift 3 - echo "run_env_as_chroot($backend_command,$backend_args,$no_copy_files,$*)" - } - # shellcheck disable=SC2317 - function run_env_as_proot_user(){ - local backend_command="$1" - local backend_args="$2" - local no_copy_files="$3" - shift 3 - echo "run_env_as_proot_user($backend_command,$backend_args,$no_copy_files,$*)" - } - # shellcheck disable=SC2317 - function run_env_as_bwrap_fakeroot(){ - local backend_command="$1" - local backend_args="$2" - local no_copy_files="$3" - shift 3 - echo "run_env_as_bwrap_fakeroot($backend_command,$backend_args,$no_copy_files,$*)" - } - # shellcheck disable=SC2317 - function run_env_as_bwrap_user(){ - local backend_command="$1" - local backend_args="$2" - local no_copy_files="$3" - shift 3 - echo "run_env_as_bwrap_user($backend_command,$backend_args,$no_copy_files,$*)" - } - # shellcheck disable=SC2317 - function is_env_installed(){ - return 0 - } - # shellcheck disable=SC2317 - function create_wrappers(){ - : - } -} - -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 - assertEquals "build_image_env(false)" "$(cat "$STDOUTF")" - assertCommandSuccess main b -n - assertEquals "build_image_env(true)" "$(cat "$STDOUTF")" - assertCommandSuccess main build --disable-check - assertEquals "build_image_env(true)" "$(cat "$STDOUTF")" -} - -function test_create_wrappers(){ - # shellcheck disable=SC2317 - function create_wrappers(){ - local force=$1 - echo "create_wrappers($force)" - } - assertCommandSuccess main create-bin-wrappers - assertEquals "create_wrappers(false)" "$(cat "$STDOUTF")" - - assertCommandSuccess main create-bin-wrappers --force - assertEquals "create_wrappers(true)" "$(cat "$STDOUTF")" -} - -function test_delete_env(){ - assertCommandSuccess main s -d - assertEquals "delete_env" "$(cat "$STDOUTF")" - assertCommandSuccess main setup --delete - assertEquals "delete_env" "$(cat "$STDOUTF")" -} -function test_setup_env_from_file(){ - # shellcheck disable=SC2317 - is_env_installed(){ - return 1 - } - assertCommandSuccess main s -i myimage - assertEquals "setup_env_from_file(myimage)" "$(cat "$STDOUTF")" - assertCommandSuccess main setup --from-file myimage - assertEquals "setup_env_from_file(myimage)" "$(cat "$STDOUTF")" - - # shellcheck disable=SC2317 - is_env_installed(){ - return 0 - } - assertCommandFail main setup -i myimage -} - -function test_setup_env(){ - # shellcheck disable=SC2317 - is_env_installed(){ - return 1 - } - assertCommandSuccess main s - assertEquals "setup_env()" "$(cat "$STDOUTF")" - assertCommandSuccess main setup - assertEquals "setup_env()" "$(cat "$STDOUTF")" - assertCommandSuccess main s -a arm - assertEquals "setup_env(arm)" "$(cat "$STDOUTF")" - assertCommandSuccess main setup --arch arm - assertEquals "setup_env(arm)" "$(cat "$STDOUTF")" - - # shellcheck disable=SC2317 - is_env_installed(){ - return 0 - } - assertCommandFail main setup -a arm -} - -function test_run_env_as_proot_fakeroot(){ - assertCommandSuccess main p -f - assertEquals "run_env_as_proot_fakeroot(,,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main proot --fakeroot - assertEquals "run_env_as_proot_fakeroot(,,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main p -f -n - assertEquals "run_env_as_proot_fakeroot(,,true,)" "$(cat "$STDOUTF")" - - assertCommandSuccess main p -f --backend-command blah - assertEquals "run_env_as_proot_fakeroot(blah,,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main proot -f --backend-command blah - assertEquals "run_env_as_proot_fakeroot(blah,,false,)" "$(cat "$STDOUTF")" - - assertCommandSuccess main proot -f -b "-b arg" - assertEquals "run_env_as_proot_fakeroot(,-b arg,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main proot -f -b "-b arg" -- command -kv - assertEquals "run_env_as_proot_fakeroot(,-b arg,false,command -kv)" "$(cat "$STDOUTF")" - assertCommandSuccess main proot -f command --as - assertEquals "run_env_as_proot_fakeroot(,,false,command --as)" "$(cat "$STDOUTF")" - assertCommandSuccess main proot -f -- command --as - assertEquals "run_env_as_proot_fakeroot(,,false,command --as)" "$(cat "$STDOUTF")" - - # shellcheck disable=SC2317 - is_env_installed(){ - return 1 - } - assertCommandFail main proot -f -} - -function test_run_env_as_user(){ - assertCommandSuccess main proot - assertEquals "run_env_as_proot_user(,,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main p -n - assertEquals "run_env_as_proot_user(,,true,)" "$(cat "$STDOUTF")" - - assertCommandSuccess main p --backend-command blah - assertEquals "run_env_as_proot_user(blah,,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main proot --backend-command blah - assertEquals "run_env_as_proot_user(blah,,false,)" "$(cat "$STDOUTF")" - - assertCommandSuccess main proot -b "-b arg" - assertEquals "run_env_as_proot_user(,-b arg,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main proot -b "-b arg" -- command -ll - assertEquals "run_env_as_proot_user(,-b arg,false,command -ll)" "$(cat "$STDOUTF")" - assertCommandSuccess main proot command -ls - assertEquals "run_env_as_proot_user(,,false,command -ls)" "$(cat "$STDOUTF")" - assertCommandSuccess main proot -- command -ls - assertEquals "run_env_as_proot_user(,,false,command -ls)" "$(cat "$STDOUTF")" - - # shellcheck disable=SC2317 - is_env_installed(){ - return 1 - } - assertCommandFail main proot -} - -function test_run_env_as_groot(){ - assertCommandSuccess main g - assertEquals "run_env_as_groot(,,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main g -n - assertEquals "run_env_as_groot(,,true,)" "$(cat "$STDOUTF")" - assertCommandSuccess main g -b "-b arg" - assertEquals "run_env_as_groot(,-b arg,false,)" "$(cat "$STDOUTF")" - - assertCommandSuccess main g --backend-command blah - assertEquals "run_env_as_groot(blah,,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main groot --backend-command blah - assertEquals "run_env_as_groot(blah,,false,)" "$(cat "$STDOUTF")" - - assertCommandSuccess main groot command - assertEquals "run_env_as_groot(,,false,command)" "$(cat "$STDOUTF")" - assertCommandSuccess main groot -- command - assertEquals "run_env_as_groot(,,false,command)" "$(cat "$STDOUTF")" - - # shellcheck disable=SC2317 - is_env_installed(){ - return 1 - } - assertCommandFail main groot -} - -function test_run_env_as_chroot(){ - assertCommandSuccess main r - assertEquals "run_env_as_chroot(,,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main r -b "-b arg" - assertEquals "run_env_as_chroot(,-b arg,false,)" "$(cat "$STDOUTF")" - - assertCommandSuccess main r --backend-command blah - assertEquals "run_env_as_chroot(blah,,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main root --backend-command blah - assertEquals "run_env_as_chroot(blah,,false,)" "$(cat "$STDOUTF")" - - assertCommandSuccess main root command - assertEquals "run_env_as_chroot(,,false,command)" "$(cat "$STDOUTF")" - assertCommandSuccess main root -- command - assertEquals "run_env_as_chroot(,,false,command)" "$(cat "$STDOUTF")" - - # shellcheck disable=SC2317 - is_env_installed(){ - return 1 - } - assertCommandFail main root -f -} - -function test_run_env_as_bwrap_fakeroot(){ - assertCommandSuccess main n -f - assertEquals "run_env_as_bwrap_fakeroot(,,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main ns -f - assertEquals "run_env_as_bwrap_fakeroot(,,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main ns -n -f - assertEquals "run_env_as_bwrap_fakeroot(,,true,)" "$(cat "$STDOUTF")" - - assertCommandSuccess main ns -f -b "-b arg" - assertEquals "run_env_as_bwrap_fakeroot(,-b arg,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main ns -f -b "-b arg" -- command -kv - assertEquals "run_env_as_bwrap_fakeroot(,-b arg,false,command -kv)" "$(cat "$STDOUTF")" - assertCommandSuccess main ns -f command --as - assertEquals "run_env_as_bwrap_fakeroot(,,false,command --as)" "$(cat "$STDOUTF")" - assertCommandSuccess main ns -f -- command --as - assertEquals "run_env_as_bwrap_fakeroot(,,false,command --as)" "$(cat "$STDOUTF")" - - assertCommandSuccess main ns -f --backend-command blah - assertEquals "run_env_as_bwrap_fakeroot(blah,,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main -f --backend-command blah - assertEquals "run_env_as_bwrap_fakeroot(blah,,false,)" "$(cat "$STDOUTF")" - - assertCommandSuccess main -f - assertEquals "run_env_as_bwrap_fakeroot(,,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main -f - assertEquals "run_env_as_bwrap_fakeroot(,,false,)" "$(cat "$STDOUTF")" - - assertCommandSuccess main -f -b "-b arg" - assertEquals "run_env_as_bwrap_fakeroot(,-b arg,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main -f -b "-b arg" -- command -kv - assertEquals "run_env_as_bwrap_fakeroot(,-b arg,false,command -kv)" "$(cat "$STDOUTF")" - assertCommandSuccess main -f command --as - assertEquals "run_env_as_bwrap_fakeroot(,,false,command --as)" "$(cat "$STDOUTF")" - assertCommandSuccess main -f -- command --as - assertEquals "run_env_as_bwrap_fakeroot(,,false,command --as)" "$(cat "$STDOUTF")" - - # shellcheck disable=SC2317 - is_env_installed(){ - return 1 - } - assertCommandFail main ns -f -} - -function test_run_env_as_bwrap_user(){ - assertCommandSuccess main n - assertEquals "run_env_as_bwrap_user(,,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main ns - assertEquals "run_env_as_bwrap_user(,,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main ns -n - assertEquals "run_env_as_bwrap_user(,,true,)" "$(cat "$STDOUTF")" - - assertCommandSuccess main ns -b "-b arg" - assertEquals "run_env_as_bwrap_user(,-b arg,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main ns -b "-b arg" -- command -kv - assertEquals "run_env_as_bwrap_user(,-b arg,false,command -kv)" "$(cat "$STDOUTF")" - assertCommandSuccess main ns command --as - assertEquals "run_env_as_bwrap_user(,,false,command --as)" "$(cat "$STDOUTF")" - assertCommandSuccess main ns -- command --as - assertEquals "run_env_as_bwrap_user(,,false,command --as)" "$(cat "$STDOUTF")" - - assertCommandSuccess main ns --backend-command blah - assertEquals "run_env_as_bwrap_user(blah,,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main --backend-command blah - assertEquals "run_env_as_bwrap_user(blah,,false,)" "$(cat "$STDOUTF")" - - assertCommandSuccess main - assertEquals "run_env_as_bwrap_user(,,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main - assertEquals "run_env_as_bwrap_user(,,false,)" "$(cat "$STDOUTF")" - - assertCommandSuccess main -b "-b arg" - assertEquals "run_env_as_bwrap_user(,-b arg,false,)" "$(cat "$STDOUTF")" - assertCommandSuccess main -b "-b arg" -- command -kv - assertEquals "run_env_as_bwrap_user(,-b arg,false,command -kv)" "$(cat "$STDOUTF")" - assertCommandSuccess main command --as - assertEquals "run_env_as_bwrap_user(,,false,command --as)" "$(cat "$STDOUTF")" - assertCommandSuccess main -- command --as - assertEquals "run_env_as_bwrap_user(,,false,command --as)" "$(cat "$STDOUTF")" - - # shellcheck disable=SC2317 - is_env_installed(){ - return 1 - } - assertCommandFail main ns -} - -function test_invalid_option(){ - assertCommandFail main --no-option - assertCommandFail main n --no-option - assertCommandFail main g --no-option - assertCommandFail main r --no-option - - assertCommandFail main p --no-option - - assertCommandFail main b --no-option - assertCommandFail main s --no-option -} - -source "$(dirname "$0")"/../utils/shunit2 diff --git a/tests/unit-tests/test-namespace.sh b/tests/unit-tests/test-namespace.sh deleted file mode 100755 index 7a845aa..0000000 --- a/tests/unit-tests/test-namespace.sh +++ /dev/null @@ -1,241 +0,0 @@ -#!/bin/bash -# shellcheck disable=SC1091 - -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() { - # shellcheck disable=SC2317 - function bwrap_cmd(){ - echo "$BWRAP $*" - } -} - -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(){ - PROC_USERNS_FILE="blah" - PROC_USERNS_CLONE_FILE="blah" - CONFIG_PROC_FILE="blah" - CONFIG_BOOT_FILE="blah" - assertCommandFailOnStatus "$NOT_EXISTING_FILE" _is_user_namespace_enabled -} - -function test_is_user_namespace_enabled_no_config(){ - PROC_USERNS_FILE="blah" - PROC_USERNS_CLONE_FILE="blah" - touch config - gzip config - # shellcheck disable=SC2034 - CONFIG_PROC_FILE="config.gz" - # shellcheck disable=SC2034 - CONFIG_BOOT_FILE="blah" - assertCommandFailOnStatus "$NO_CONFIG_FOUND" _is_user_namespace_enabled -} - -function test_is_user_namespace_enabled_with_userns_clone_file_disabled(){ - PROC_USERNS_FILE="blah" - PROC_USERNS_CLONE_FILE="unprivileged_userns_clone" - echo "0" > $PROC_USERNS_CLONE_FILE - assertCommandFailOnStatus "$UNPRIVILEGED_USERNS_DISABLED" _is_user_namespace_enabled -} - -function test_is_user_namespace_enabled_with_userns_clone_file_enabled(){ - PROC_USERNS_CLONE_FILE="unprivileged_userns_clone" - echo "1" > $PROC_USERNS_CLONE_FILE - assertCommandSuccess _is_user_namespace_enabled -} - -function test_is_user_namespace_enabled_with_proc_userns_file_existing(){ - PROC_USERNS_FILE="user" - ln -s . $PROC_USERNS_FILE - PROC_USERNS_CLONE_FILE="blah" - assertCommandSuccess _is_user_namespace_enabled -} - -function test_run_env_as_bwrap_fakeroot() { - assertCommandSuccess run_env_as_bwrap_fakeroot "" "" "false" - assertEquals "$BWRAP $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 sudo /bin/sh --login" "$(cat "$STDOUTF")" - - _test_copy_common_files -} - -function test_run_env_as_bwrap_fakeroot_with_backend_command() { - assertCommandSuccess run_env_as_bwrap_fakeroot "mybwrap" "" "false" - assertEquals "mybwrap $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 sudo /bin/sh --login" "$(cat "$STDOUTF")" - - _test_copy_common_files -} - -function test_run_env_as_bwrap_user() { - assertCommandSuccess run_env_as_bwrap_user "" "" "false" - assertEquals "$BWRAP $COMMON_BWRAP_OPTION /bin/sh --login" "$(cat "$STDOUTF")" - - _test_copy_common_files - _test_copy_remaining_files -} - -function test_run_env_as_bwrap_user_with_backend_command() { - assertCommandSuccess run_env_as_bwrap_user "mybwrap" "" "false" - assertEquals "mybwrap $COMMON_BWRAP_OPTION /bin/sh --login" "$(cat "$STDOUTF")" - - _test_copy_common_files - _test_copy_remaining_files -} - -function test_run_env_as_bwrap_fakeroot_no_copy() { - assertCommandSuccess run_env_as_bwrap_fakeroot "" "" "true" "" - assertEquals "$BWRAP $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 sudo /bin/sh --login" "$(cat "$STDOUTF")" - - [[ ! -e ${JUNEST_HOME}/etc/hosts ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/host.conf ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/nsswitch.conf ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/resolv.conf ]] - assertEquals 0 $? - - [[ ! -e ${JUNEST_HOME}/etc/hosts.equiv ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/netgroup ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/networks ]] - assertEquals 0 $? - - [[ ! -e ${JUNEST_HOME}/etc/passwd ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/group ]] - assertEquals 0 $? -} - -function test_run_env_as_bwrap_user_no_copy() { - assertCommandSuccess run_env_as_bwrap_user "" "" "true" "" - assertEquals "$BWRAP $COMMON_BWRAP_OPTION /bin/sh --login" "$(cat "$STDOUTF")" - - [[ ! -e ${JUNEST_HOME}/etc/hosts ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/host.conf ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/nsswitch.conf ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/resolv.conf ]] - assertEquals 0 $? - - [[ ! -e ${JUNEST_HOME}/etc/hosts.equiv ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/netgroup ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/networks ]] - assertEquals 0 $? - - [[ ! -e ${JUNEST_HOME}/etc/passwd ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/group ]] - assertEquals 0 $? -} - -function test_run_env_as_bwrap_fakeroot_with_backend_args() { - assertCommandSuccess run_env_as_bwrap_fakeroot "" "--bind /usr /usr" "false" - assertEquals "$BWRAP $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 --bind /usr /usr sudo /bin/sh --login" "$(cat "$STDOUTF")" - - _test_copy_common_files -} - -function test_run_env_as_bwrap_user_with_backend_args() { - assertCommandSuccess run_env_as_bwrap_user "" "--bind /usr /usr" "false" - assertEquals "$BWRAP $COMMON_BWRAP_OPTION --bind /usr /usr /bin/sh --login" "$(cat "$STDOUTF")" - - _test_copy_common_files - _test_copy_remaining_files -} - -function test_run_env_as_bwrap_fakeroot_with_command() { - assertCommandSuccess run_env_as_bwrap_fakeroot "" "" "false" "ls -la" - assertEquals "$BWRAP $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 sudo /bin/sh --login -c \"ls -la\"" "$(cat "$STDOUTF")" - - _test_copy_common_files -} - -function test_run_env_as_bwrap_user_with_command() { - assertCommandSuccess run_env_as_bwrap_user "" "" "false" "ls -la" - assertEquals "$BWRAP $COMMON_BWRAP_OPTION /bin/sh --login -c \"ls -la\"" "$(cat "$STDOUTF")" - - _test_copy_common_files - _test_copy_remaining_files -} - -function test_run_env_as_bwrap_fakeroot_with_backend_args_and_command() { - assertCommandSuccess run_env_as_bwrap_fakeroot "" "--bind /usr /usr" "false" "ls -la" - assertEquals "$BWRAP $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 --bind /usr /usr sudo /bin/sh --login -c \"ls -la\"" "$(cat "$STDOUTF")" - - _test_copy_common_files -} - -function test_run_env_as_bwrap_user_with_backend_args_and_command() { - assertCommandSuccess run_env_as_bwrap_user "" "--bind /usr /usr" "false" "ls -la" - assertEquals "$BWRAP $COMMON_BWRAP_OPTION --bind /usr /usr /bin/sh --login -c \"ls -la\"" "$(cat "$STDOUTF")" - - _test_copy_common_files - _test_copy_remaining_files -} - -function test_run_env_as_bwrap_fakeroot_nested_env(){ - JUNEST_ENV=1 - assertCommandFailOnStatus 106 run_env_as_bwrap_fakeroot "" "" "false" "" - unset JUNEST_ENV -} - -function test_run_env_as_bwrap_user_nested_env(){ - # shellcheck disable=SC2034 - JUNEST_ENV=1 - assertCommandFailOnStatus 106 run_env_as_bwrap_user "" "" "false" "" - unset JUNEST_ENV -} - -source "$JUNEST_ROOT"/tests/utils/shunit2 diff --git a/tests/unit-tests/test-proot.sh b/tests/unit-tests/test-proot.sh deleted file mode 100755 index 0f4f11a..0000000 --- a/tests/unit-tests/test-proot.sh +++ /dev/null @@ -1,220 +0,0 @@ -#!/bin/bash -# shellcheck disable=SC1091 - -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 -} - -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/proot.sh" - set +e -} - -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_run_env_as_proot_user(){ - # shellcheck disable=SC2317 - _run_env_with_qemu() { - # shellcheck disable=SC2086 - # shellcheck disable=SC2048 - echo $* - } - assertCommandSuccess run_env_as_proot_user "" "-k 3.10" "false" "/usr/bin/mkdir" "-v" "/newdir2" - assertEquals "-b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10 /usr/bin/mkdir -v /newdir2" "$(cat "$STDOUTF")" - - SH=("/usr/bin/echo") - assertCommandSuccess run_env_as_proot_user "" "-k 3.10" "false" - assertEquals "-b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10" "$(cat "$STDOUTF")" - - _test_copy_common_files - _test_copy_remaining_files -} - -function test_run_env_as_proot_user_with_backend_command(){ - # shellcheck disable=SC2317 - _run_env_with_qemu() { - # shellcheck disable=SC2086 - # shellcheck disable=SC2048 - echo $* - } - assertCommandSuccess run_env_as_proot_user "myproot" "-k 3.10" "false" "/usr/bin/mkdir" "-v" "/newdir2" - assertEquals "myproot -b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10 /usr/bin/mkdir -v /newdir2" "$(cat "$STDOUTF")" - - SH=("/usr/bin/echo") - assertCommandSuccess run_env_as_proot_user "myproot" "-k 3.10" "false" - assertEquals "myproot -b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10" "$(cat "$STDOUTF")" - - _test_copy_common_files - _test_copy_remaining_files -} - -function test_run_env_as_proot_user_no_copy(){ - # shellcheck disable=SC2317 - _run_env_with_qemu() { - # shellcheck disable=SC2086 - # shellcheck disable=SC2048 - echo $* - } - assertCommandSuccess run_env_as_proot_user "" "-k 3.10" "true" "/usr/bin/mkdir" "-v" "/newdir2" - assertEquals "-b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10 /usr/bin/mkdir -v /newdir2" "$(cat "$STDOUTF")" - - [[ ! -e ${JUNEST_HOME}/etc/hosts ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/host.conf ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/nsswitch.conf ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/resolv.conf ]] - assertEquals 0 $? - - [[ ! -e ${JUNEST_HOME}/etc/hosts.equiv ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/netgroup ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/networks ]] - assertEquals 0 $? - - [[ ! -e ${JUNEST_HOME}/etc/passwd ]] - assertEquals 0 $? - [[ ! -e ${JUNEST_HOME}/etc/group ]] - assertEquals 0 $? -} - -function test_run_env_as_proot_user_nested_env(){ - JUNEST_ENV=1 - assertCommandFailOnStatus 106 run_env_as_proot_user "" "" "false" - unset JUNEST_ENV -} - -function test_run_env_as_proot_fakeroot(){ - # shellcheck disable=SC2317 - _run_env_with_qemu() { - # shellcheck disable=SC2086 - # shellcheck disable=SC2048 - echo $* - } - assertCommandSuccess run_env_as_proot_fakeroot "" "-k 3.10" "false" "/usr/bin/mkdir" "-v" "/newdir2" - assertEquals "-0 -b /run/user/$(id -u) -b ${HOME} -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10 /usr/bin/mkdir -v /newdir2" "$(cat "$STDOUTF")" - - SH=("/usr/bin/echo") - assertCommandSuccess run_env_as_proot_fakeroot "" "-k 3.10" "false" - assertEquals "-0 -b /run/user/$(id -u) -b ${HOME} -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10" "$(cat "$STDOUTF")" - - _test_copy_common_files -} - -function test_run_env_as_proot_fakeroot_with_backend_command(){ - # shellcheck disable=SC2317 - _run_env_with_qemu() { - # shellcheck disable=SC2086 - # shellcheck disable=SC2048 - echo $* - } - assertCommandSuccess run_env_as_proot_fakeroot "myproot" "-k 3.10" "false" "/usr/bin/mkdir" "-v" "/newdir2" - assertEquals "myproot -0 -b /run/user/$(id -u) -b ${HOME} -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10 /usr/bin/mkdir -v /newdir2" "$(cat "$STDOUTF")" - - # shellcheck disable=SC2034 - SH=("/usr/bin/echo") - assertCommandSuccess run_env_as_proot_fakeroot "myproot" "-k 3.10" "false" - assertEquals "myproot -0 -b /run/user/$(id -u) -b ${HOME} -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10" "$(cat "$STDOUTF")" - - _test_copy_common_files -} - -function test_run_env_as_proot_fakeroot_nested_env(){ - JUNEST_ENV=1 - assertCommandFailOnStatus 106 run_env_as_proot_fakeroot "" "" "false" "" - unset JUNEST_ENV -} - -function test_run_env_with_quotes(){ - # shellcheck disable=SC2317 - _run_env_with_qemu() { - # shellcheck disable=SC2086 - # shellcheck disable=SC2048 - echo $* - } - assertCommandSuccess run_env_as_proot_user "" "-k 3.10" "false" "bash" "-c" "/usr/bin/mkdir -v /newdir2" - assertEquals "-b /run/user/$(id -u) -b ${HOME} -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10 bash -c /usr/bin/mkdir -v /newdir2" "$(cat "$STDOUTF")" -} - -function test_run_env_with_proot_args(){ - # shellcheck disable=SC2317 - proot_cmd() { - [ "$JUNEST_ENV" != "1" ] && return 1 - # shellcheck disable=SC2086 - # shellcheck disable=SC2048 - echo $* - } - - assertCommandSuccess _run_env_with_proot "" "--help" - assertEquals "--help /bin/sh --login" "$(cat "$STDOUTF")" - - assertCommandSuccess _run_env_with_proot "" "--help" mycommand - assertEquals "--help /bin/sh --login -c mycommand" "$(cat "$STDOUTF")" - - assertCommandFail _run_env_with_proot -} - -function test_qemu() { - echo "JUNEST_ARCH=arm" > "${JUNEST_HOME}"/etc/junest/info - # shellcheck disable=SC2317 - rm_cmd() { - # shellcheck disable=SC2086 - # shellcheck disable=SC2048 - echo $* - } - # shellcheck disable=SC2317 - ln_cmd() { - # shellcheck disable=SC2086 - # shellcheck disable=SC2048 - echo $* - } - # shellcheck disable=SC2317 - _run_env_with_proot() { - # shellcheck disable=SC2086 - # shellcheck disable=SC2048 - echo $* - } - - RANDOM=100 ARCH=x86_64 assertCommandSuccess _run_env_with_qemu "" "" - assertEquals "$(echo -e "-s $JUNEST_HOME/bin/qemu-arm-static-x86_64 /tmp/qemu-arm-static-x86_64-100\n-q /tmp/qemu-arm-static-x86_64-100")" "$(cat "$STDOUTF")" -} - -source "$JUNEST_ROOT"/tests/utils/shunit2 diff --git a/tests/unit-tests/test-setup.sh b/tests/unit-tests/test-setup.sh deleted file mode 100755 index de2df75..0000000 --- a/tests/unit-tests/test-setup.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/bin/bash -# shellcheck disable=SC1091 - -JUNEST_ROOT=$(readlink -f "$(dirname "$0")"/../..) - -source "$JUNEST_ROOT/tests/utils/utils.sh" - -source "$JUNEST_ROOT/lib/utils/utils.sh" -source "$JUNEST_ROOT/lib/core/common.sh" -source "$JUNEST_ROOT/lib/core/setup.sh" - -# Disable the exiterr -set +e - -function oneTimeSetUp(){ - setUpUnitTests -} - -function setUp(){ - cwdSetUp - junestSetUp -} - -function tearDown(){ - junestTearDown - cwdTearDown -} - -function test_is_env_installed(){ - rm -rf "${JUNEST_HOME:?}"/* - assertCommandFail is_env_installed - touch "$JUNEST_HOME"/just_file - assertCommandSuccess is_env_installed -} - -function test_setup_env(){ - rm -rf "${JUNEST_HOME:?}"/* - # shellcheck disable=SC2317 - wget_mock(){ - # Proof that the setup is happening - # inside $JUNEST_TEMPDIR - local cwd=${PWD#"${JUNEST_TEMPDIR}"} - local parent_dir=${PWD%"${cwd}"} - assertEquals "$JUNEST_TEMPDIR" "${parent_dir}" - touch file - tar -czvf "${CMD}-${ARCH}".tar.gz file - } - # shellcheck disable=SC2034 - WGET=wget_mock - # shellcheck disable=SC2119 - setup_env 1> /dev/null - assertTrue "[ -e $JUNEST_HOME/file ]" - - assertCommandFailOnStatus 102 setup_env "noarch" -} - - -function test_setup_env_from_file(){ - rm -rf "${JUNEST_HOME:?}"/* - touch file - tar -czvf "${CMD}-${ARCH}".tar.gz file 1> /dev/null - assertCommandSuccess setup_env_from_file "${CMD}-${ARCH}.tar.gz" - assertTrue "[ -e $JUNEST_HOME/file ]" -} - -function test_setup_env_from_file_not_existing_file(){ - assertCommandFailOnStatus 103 setup_env_from_file noexist.tar.gz -} - -function test_setup_env_from_file_with_absolute_path(){ - rm -rf "${JUNEST_HOME:?}"/* - touch file - tar -czf "${CMD}-${ARCH}".tar.gz file - assertCommandSuccess setup_env_from_file "${CMD}-${ARCH}.tar.gz" - assertTrue "[ -e $JUNEST_HOME/file ]" -} - -function test_delete_env(){ - echo "N" | delete_env 1> /dev/null - assertCommandSuccess is_env_installed - echo "Y" | delete_env 1> /dev/null - assertCommandFail is_env_installed -} - -source "$JUNEST_ROOT"/tests/utils/shunit2 diff --git a/tests/unit-tests/test-utils.sh b/tests/unit-tests/test-utils.sh index 03e602a..5816d11 100755 --- a/tests/unit-tests/test-utils.sh +++ b/tests/unit-tests/test-utils.sh @@ -1,13 +1,10 @@ #!/bin/bash -# shellcheck disable=SC1091 - -source "$(dirname "$0")/../utils/utils.sh" +source "$(dirname $0)/utils.sh" unset HOME -export HOME -HOME=$(TMPDIR=/tmp mktemp -d -t pearl-user-home.XXXXXXX) +export HOME=$(TMPDIR=/tmp mktemp -d -t pearl-user-home.XXXXXXX) -source "$(dirname "$0")/../../lib/utils/utils.sh" +source "$(dirname $0)/../../lib/utils.sh" # Disable the exiterr set +e @@ -23,42 +20,37 @@ function test_check_not_null(){ function test_echoerr(){ assertCommandSuccess echoerr "Test" - assertEquals "Test" "$(cat "$STDERRF")" + assertEquals "Test" "$(cat $STDERRF)" } function test_error(){ assertCommandSuccess error "Test" - local expected - expected=$(echo -e "\033[1;31mTest\033[0m") - assertEquals "$expected" "$(cat "$STDERRF")" + local expected=$(echo -e "\033[1;31mTest\033[0m") + assertEquals "$expected" "$(cat $STDERRF)" } function test_warn(){ assertCommandSuccess warn "Test" - local expected - expected=$(echo -e "\033[1;33mTest\033[0m") - assertEquals "$expected" "$(cat "$STDERRF")" + local expected=$(echo -e "\033[1;33mTest\033[0m") + assertEquals "$expected" "$(cat $STDERRF)" } function test_info(){ assertCommandSuccess info "Test" - local expected - expected=$(echo -e "\033[1;36mTest\033[0m") - assertEquals "$expected" "$(cat "$STDOUTF")" + local expected=$(echo -e "\033[1;36mTest\033[0m") + assertEquals "$expected" "$(cat $STDOUTF)" } function test_die(){ assertCommandFail die "Test" - local expected - expected=$(echo -e "\033[1;31mTest\033[0m") - assertEquals "$expected" "$(cat "$STDERRF")" + local expected=$(echo -e "\033[1;31mTest\033[0m") + assertEquals "$expected" "$(cat $STDERRF)" } function test_die_on_status(){ assertCommandFailOnStatus 222 die_on_status 222 "Test" - local expected - expected=$(echo -e "\033[1;31mTest\033[0m") - assertEquals "$expected" "$(cat "$STDERRF")" + local expected=$(echo -e "\033[1;31mTest\033[0m") + assertEquals "$expected" "$(cat $STDERRF)" } function test_ask_null_question(){ @@ -89,10 +81,10 @@ function test_ask_wrong_default_answer() { function test_insert_quotes_on_spaces(){ assertCommandSuccess insert_quotes_on_spaces this is "a test" - assertEquals "this is \"a test\"" "$(cat "$STDOUTF")" + assertEquals "this is \"a test\"" "$(cat $STDOUTF)" assertCommandSuccess insert_quotes_on_spaces this is 'a test' - assertEquals "this is \"a test\"" "$(cat "$STDOUTF")" + assertEquals "this is \"a test\"" "$(cat $STDOUTF)" } function test_contains_element(){ @@ -102,4 +94,4 @@ function test_contains_element(){ assertCommandFailOnStatus 1 contains_element "blabla" "${array[@]}" } -source "$(dirname "$0")"/../utils/shunit2 +source $(dirname $0)/shunit2 diff --git a/tests/unit-tests/test-wrappers.sh b/tests/unit-tests/test-wrappers.sh deleted file mode 100755 index ee9776f..0000000 --- a/tests/unit-tests/test-wrappers.sh +++ /dev/null @@ -1,139 +0,0 @@ -#!/bin/bash -# shellcheck disable=SC1091 - -source "$(dirname "$0")/../utils/utils.sh" - -source "$(dirname "$0")/../../lib/core/wrappers.sh" - -# Disable the exiterr -set +e - -function oneTimeSetUp(){ - setUpUnitTests -} - -function setUp(){ - junestSetUp -} - -function tearDown(){ - junestTearDown -} - -function test_create_wrappers_empty_bin(){ - assertCommandSuccess create_wrappers - assertEquals "" "$(cat "$STDOUTF")" - assertTrue "bin_wrappers does not exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]" -} - -function test_create_wrappers_not_executable_file(){ - touch "$JUNEST_HOME"/usr/bin/myfile - assertCommandSuccess create_wrappers - assertEquals "" "$(cat "$STDOUTF")" - assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]" - assertTrue "myfile wrapper should not exist" "[ ! -x $JUNEST_HOME/usr/bin_wrappers/myfile ]" -} - -function test_create_wrappers_directory(){ - mkdir -p "$JUNEST_HOME"/usr/bin/mydir - assertCommandSuccess create_wrappers - assertEquals "" "$(cat "$STDOUTF")" - assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]" - assertTrue "mydir wrapper should not exist" "[ ! -e $JUNEST_HOME/usr/bin_wrappers/mydir ]" -} - -function test_create_wrappers_broken_link(){ - ln -s /opt/myapp/bin/cmd "$JUNEST_HOME"/usr/bin/cmd - assertCommandSuccess create_wrappers - assertEquals "" "$(cat "$STDOUTF")" - assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]" - assertTrue "cmd wrapper should exist" "[ -x $JUNEST_HOME/usr/bin_wrappers/cmd ]" -} - -function test_create_wrappers_executable_file(){ - touch "$JUNEST_HOME"/usr/bin/myfile - chmod +x "$JUNEST_HOME"/usr/bin/myfile - assertCommandSuccess create_wrappers - assertEquals "" "$(cat "$STDOUTF")" - assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]" - assertTrue "myfile wrapper should exist" "[ -x $JUNEST_HOME/usr/bin_wrappers/myfile ]" -} - -function test_create_wrappers_verify_content(){ - # Test for: - # https://github.com/fsquillace/junest/issues/262 - # https://github.com/fsquillace/junest/issues/292 - touch "$JUNEST_HOME"/usr/bin/myfile - chmod +x "$JUNEST_HOME"/usr/bin/myfile - export JUNEST_ARGS="ns --fakeroot -b '--bind /run /run2'" - assertCommandSuccess create_wrappers - assertEquals "" "$(cat "$STDOUTF")" - - # Mock junest command to capture the actual output generated from myfile script - # shellcheck disable=SC2317 - junest(){ - for arg in "$@" - do - echo "$arg" - done - } - assertEquals "ns ---fakeroot --b ---bind /run /run2 --- -test-wrappers.sh -pacman --Rsn -neovim -new package" "$(source "$JUNEST_HOME"/usr/bin_wrappers/myfile pacman -Rsn neovim 'new package')" -} - -function test_create_wrappers_already_exist(){ - touch "$JUNEST_HOME"/usr/bin/myfile - chmod +x "$JUNEST_HOME"/usr/bin/myfile - mkdir -p "$JUNEST_HOME"/usr/bin_wrappers - echo "original" > "$JUNEST_HOME"/usr/bin_wrappers/myfile - chmod +x "$JUNEST_HOME"/usr/bin_wrappers/myfile - assertCommandSuccess create_wrappers false - assertEquals "" "$(cat "$STDOUTF")" - assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]" - assertTrue "myfile wrapper should exist" "[ -x $JUNEST_HOME/usr/bin_wrappers/myfile ]" - assertEquals "original" "$(cat "$JUNEST_HOME"/usr/bin_wrappers/myfile)" -} - -function test_create_wrappers_forced_already_exist(){ - echo "new" > "$JUNEST_HOME"/usr/bin/myfile - chmod +x "$JUNEST_HOME"/usr/bin/myfile - mkdir -p "$JUNEST_HOME"/usr/bin_wrappers - echo "original" > "$JUNEST_HOME"/usr/bin_wrappers/myfile - chmod +x "$JUNEST_HOME"/usr/bin_wrappers/myfile - assertCommandSuccess create_wrappers true - assertEquals "" "$(cat "$STDOUTF")" - assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]" - assertTrue "myfile wrapper should exist" "[ -x $JUNEST_HOME/usr/bin_wrappers/myfile ]" - assertNotEquals "original" "$(cat "$JUNEST_HOME"/usr/bin_wrappers/myfile)" -} - -function test_create_wrappers_executable_no_longer_exist(){ - mkdir -p "$JUNEST_HOME"/usr/bin_wrappers - touch "$JUNEST_HOME"/usr/bin_wrappers/myfile - chmod +x "$JUNEST_HOME"/usr/bin_wrappers/myfile - assertCommandSuccess create_wrappers - assertEquals "" "$(cat "$STDOUTF")" - assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]" - assertTrue "myfile wrapper should not exist" "[ ! -x $JUNEST_HOME/usr/bin_wrappers/myfile ]" -} - -function test_create_wrappers_custom_bin_path(){ - mkdir -p "$JUNEST_HOME"/usr/mybindir - touch "$JUNEST_HOME"/usr/mybindir/myfile - chmod +x "$JUNEST_HOME"/usr/mybindir/myfile - assertCommandSuccess create_wrappers false /usr/mybindir/ - assertEquals "" "$(cat "$STDOUTF")" - assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/mybindir_wrappers ]" - assertTrue "myfile wrapper should exist" "[ -x $JUNEST_HOME/usr/mybindir_wrappers/myfile ]" -} - - -source "$(dirname "$0")"/../utils/shunit2 diff --git a/tests/unit-tests/unit-tests.sh b/tests/unit-tests/unit-tests.sh index e90ed22..acf1bba 100755 --- a/tests/unit-tests/unit-tests.sh +++ b/tests/unit-tests/unit-tests.sh @@ -1,7 +1,6 @@ #!/bin/bash tests_succeded=true -# shellcheck disable=SC2010 -for tst in $(ls "$(dirname "$0")"/test* | grep -v "$(basename "$0")") +for tst in $(ls $(dirname $0)/test* | grep -v $(basename $0)) do $tst || tests_succeded=false done diff --git a/tests/unit-tests/utils.sh b/tests/unit-tests/utils.sh new file mode 100644 index 0000000..b12420e --- /dev/null +++ b/tests/unit-tests/utils.sh @@ -0,0 +1,32 @@ + +function setUpUnitTests(){ + OUTPUT_DIR="${SHUNIT_TMPDIR}/output" + mkdir "${OUTPUT_DIR}" + STDOUTF="${OUTPUT_DIR}/stdout" + STDERRF="${OUTPUT_DIR}/stderr" +} + +function assertCommandSuccess(){ + $(set -e + "$@" > $STDOUTF 2> $STDERRF + ) + assertTrue "The command $1 did not return 0 exit status" $? +} + +function assertCommandFail(){ + $(set -e + "$@" > $STDOUTF 2> $STDERRF + ) + assertFalse "The command $1 returned 0 exit status" $? +} + +# $1: expected exit status +# $2-: The command under test +function assertCommandFailOnStatus(){ + local status=$1 + shift + $(set -e + "$@" > $STDOUTF 2> $STDERRF + ) + assertEquals $status $? +} diff --git a/tests/utils/utils.sh b/tests/utils/utils.sh deleted file mode 100644 index ab37bd9..0000000 --- a/tests/utils/utils.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bash - -OLD_CWD=${PWD} -function cwdSetUp(){ - ORIGIN_CWD=$(TMPDIR=/tmp mktemp -d -t junest-cwd.XXXXXXXXXX) - cd "$ORIGIN_CWD" || return 1 -} - -function cwdTearDown(){ - rm -rf "$ORIGIN_CWD" - cd "$OLD_CWD" || return 1 -} - -function junestSetUp(){ - JUNEST_HOME=$(TMPDIR=/tmp mktemp -d -t junest-home.XXXXXXXXXX) - mkdir -p "${JUNEST_HOME}/usr/bin" - mkdir -p "${JUNEST_HOME}/etc/junest" - echo "JUNEST_ARCH=x86_64" > "${JUNEST_HOME}/etc/junest/info" - mkdir -p "${JUNEST_HOME}/etc/ca-certificates" -} - -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" - unset JUNEST_HOME -} - -function setUpUnitTests(){ - OUTPUT_DIR="${SHUNIT_TMPDIR}/output" - mkdir "${OUTPUT_DIR}" - STDOUTF="${OUTPUT_DIR}/stdout" - STDERRF="${OUTPUT_DIR}/stderr" -} - -function assertCommandSuccess(){ - # shellcheck disable=SC2091 - $(set -e - "$@" > "$STDOUTF" 2> "$STDERRF" - ) - assertTrue "The command $1 did not return 0 exit status" $? -} - -function assertCommandFail(){ - # shellcheck disable=SC2091 - $(set -e - "$@" > "$STDOUTF" 2> "$STDERRF" - ) - assertFalse "The command $1 returned 0 exit status" $? -} - -# $1: expected exit status -# $2-: The command under test -function assertCommandFailOnStatus(){ - local status=$1 - shift - # shellcheck disable=SC2091 - $(set -e - "$@" > "$STDOUTF" 2> "$STDERRF" - ) - assertEquals "$status" $? -}