From 0cf7c624ec816cb50c4c77e497aa6534738865e4 Mon Sep 17 00:00:00 2001 From: Filippo Squillace Date: Thu, 25 Jun 2015 18:49:41 +0000 Subject: [PATCH] Issue #91: Add the architecture option --- README.md | 13 ++++++++++ bin/junest | 17 ++++++++----- lib/core.sh | 41 +++++++++++++++++++++---------- lib/util.sh | 6 +++++ tests/test_cli.sh | 36 +++++++++++++++++---------- tests/test_core.sh | 61 +++++++++++++++++++++++++++++----------------- tests/test_util.sh | 9 +++++++ 7 files changed, 129 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 1181ea1..e145c22 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ The main advantages on using JuNest are: - Isolated environment in which you can install packages without affecting a production system. - Access to a wide range of packages in particular on GNU/Linux distros that may contain limited repositories (such as CentOS and RedHat). - Available for x86\_64, x86 and ARM architectures but you can build your own image from scratch too! +- Run on a different architecture from the host OS via QEMU - All Arch Linux lovers can have their favourite distro everywhere! JuNest follows the [Arch Linux philosophy](https://wiki.archlinux.org/index.php/The_Arch_Way). @@ -101,6 +102,13 @@ Related wiki page: - [How to build a JuNest image using QEMU](https://github.com/fsquillace/junest/wiki/How-to-build-a-JuNest-image-using-QEMU) +## 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 either x86\_64 or x86 architectures: + + $> JUNEST_HOME=~/.junest-arm junest -a arm -- uname -m + armv7l + ## Bind directories ## To bind a host directory to a guest location, you can use proot arguments: @@ -153,6 +161,11 @@ image. 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 +in a different architecture from the host system. They are located in `/opt/qemu` +directory. + Troubleshooting =============== diff --git a/bin/junest b/bin/junest index 28c480f..707dcd8 100755 --- a/bin/junest +++ b/bin/junest @@ -34,6 +34,8 @@ usage() { 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" + echo -e "-a, --arch $NAME architecture to use (x86_64, x86, arm)." + echo -e " Defaults to the host architecture ($ARCH)" 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 "-d, --delete Delete $NAME from ${JUNEST_HOME}" @@ -92,12 +94,12 @@ check_cli(){ then die "You must access to $NAME with either fakeroot or root permissions" fi - if $OPT_PROOT_ARGS + if $OPT_PROOT_ARGS || $OPT_ARCH then - if $OPT_BUILD_IMAGE || $OPT_DELETE || $OPT_HELP || $OPT_SETUP_FROM_FILE || \ + if $OPT_BUILD_IMAGE || $OPT_DELETE || $OPT_HELP || \ $OPT_ROOT || $OPT_VERSION || $OPT_DISABLE_VALIDATION then - die "Invalid syntax: Proot args are not allowed with the other options" + die "Invalid syntax: Proot and arch args are not allowed with the other options" fi fi if [ "$ARGS" != "" ] @@ -120,6 +122,8 @@ function parse_arguments(){ OPT_ROOT=false OPT_PROOT_ARGS=false PROOT_ARGS="" + OPT_ARCH=false + ARCH_ARG="" OPT_BUILD_IMAGE=false OPT_DISABLE_VALIDATION=false OPT_DELETE=false @@ -132,6 +136,7 @@ function parse_arguments(){ -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 ;; -d|--delete) OPT_DELETE=true ; shift ;; @@ -167,18 +172,18 @@ function execute_operation(){ if $OPT_SETUP_FROM_FILE; then setup_env_from_file $IMAGE_FILE else - setup_env + setup_env $ARCH_ARG fi elif $OPT_SETUP_FROM_FILE; then die "Error: The image cannot be installed since $JUNEST_HOME is not empty." fi if $OPT_FAKEROOT; then - run_env_as_fakeroot "${PROOT_ARGS}" "${ARGS[@]}" + run_env_as_fakeroot "${ARCH_ARG}" "${PROOT_ARGS}" "${ARGS[@]}" elif $OPT_ROOT; then run_env_as_root "${ARGS[@]}" else - run_env_as_user "${PROOT_ARGS}" "${ARGS[@]}" + run_env_as_user "${ARCH_ARG}" "${PROOT_ARGS}" "${ARGS[@]}" fi } diff --git a/lib/core.sh b/lib/core.sh index cd3a8a0..9cafbb3 100644 --- a/lib/core.sh +++ b/lib/core.sh @@ -167,12 +167,17 @@ function _setup_env(){ function setup_env(){ + local arch=$ARCH + [ -z $1 ] || arch="$1" + contains_element $arch "${ARCH_LIST[@]}" || \ + die "The architecture is not one of: ${ARCH_LIST[@]}" + local maindir=$(TMPDIR=$JUNEST_TEMPDIR mktemp -d -t ${CMD}.XXXXXXXXXX) _prepare_build_directory info "Downloading ${NAME}..." builtin cd ${maindir} - local imagefile=${CMD}-${ARCH}.tar.gz + local imagefile=${CMD}-${arch}.tar.gz download_cmd ${ENV_REPO}/${imagefile} info "Installing ${NAME}..." @@ -210,7 +215,6 @@ function run_env_as_root(){ } function _run_env_with_proot(){ - local proot_args="$1" shift @@ -222,28 +226,39 @@ function _run_env_with_proot(){ fi } +function _run_env_with_qemu(){ + local proot_args="$2" + if [ "$1" != "" ] && [ "$1" != "$ARCH" ] + then + local qemu_bin="/tmp/qemu-$1-static-$ARCH-$RANDOM" + trap - QUIT EXIT ABRT KILL TERM INT + trap "[ -e ${qemu_bin} ] && rm_cmd -f ${qemu_bin}" EXIT QUIT ABRT KILL TERM INT + + contains_element $1 "${ARCH_LIST[@]}" || \ + die "The architecture is not one of: ${ARCH_LIST[@]}" + [ -e "${JUNEST_HOME}/opt/qemu/qemu-$1-static-$ARCH" ] || \ + die "The JuNest image in ${JUNEST_HOME} is not an $1 architecture" + warn "Emulating $NAME via QEMU..." + [ -e ${qemu_bin} ] || \ + ln_cmd -s ${JUNEST_HOME}/opt/qemu/qemu-$1-static-$ARCH ${qemu_bin} + proot_args="-q ${qemu_bin} $2" + fi + shift 2 + _run_env_with_proot "$proot_args" "${@}" +} function run_env_as_fakeroot(){ (( EUID == 0 )) && \ die "You cannot access with root privileges. Use --root option instead." - - local proot_args="$1" - shift - [ ! -e ${JUNEST_HOME}/etc/mtab ] && ln_cmd -s /proc/self/mounts ${JUNEST_HOME}/etc/mtab - _run_env_with_proot "-S ${JUNEST_HOME} $proot_args" "${@}" + _run_env_with_qemu "$1" "-S ${JUNEST_HOME} $2" "${@:3}" } - function run_env_as_user(){ (( EUID == 0 )) && \ die "You cannot access with root privileges. Use --root option instead." - - local proot_args="$1" - shift - [ -e ${JUNEST_HOME}/etc/mtab ] && rm_cmd -f ${JUNEST_HOME}/etc/mtab - _run_env_with_proot "-R ${JUNEST_HOME} $proot_args" "${@}" + _run_env_with_qemu "$1" "-R ${JUNEST_HOME} $2" "${@:3}" } diff --git a/lib/util.sh b/lib/util.sh index f65ac26..7f45102 100644 --- a/lib/util.sh +++ b/lib/util.sh @@ -85,3 +85,9 @@ function insert_quotes_on_spaces(){ done echo $C } + +contains_element () { + local e + for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done + return 1 +} diff --git a/tests/test_cli.sh b/tests/test_cli.sh index bcf2f0c..f653d5b 100755 --- a/tests/test_cli.sh +++ b/tests/test_cli.sh @@ -27,17 +27,21 @@ function setup_env(){ echo "setup_env" } function run_env_as_fakeroot(){ - local proot_args="$1" + local arch_arg="$1" + local proot_args="$2" shift - echo "run_env_as_fakeroot($proot_args,$@)" + shift + echo "run_env_as_fakeroot($arch_arg,$proot_args,$@)" } function run_env_as_root(){ echo "run_env_as_root $@" } function run_env_as_user(){ - local proot_args="$1" + local arch_arg="$1" + local proot_args="$2" shift - echo "run_env_as_user($proot_args,$@)" + shift + echo "run_env_as_user($arch_arg,$proot_args,$@)" } function wrap_env(){ @@ -72,27 +76,31 @@ function test_delete_env(){ } function test_run_env_as_fakeroot(){ local output=$(wrap_env -f) - assertEquals $output "run_env_as_fakeroot(,)" + assertEquals $output "run_env_as_fakeroot(,,)" local output=$(wrap_env --fakeroot) - assertEquals $output "run_env_as_fakeroot(,)" + assertEquals $output "run_env_as_fakeroot(,,)" local output=$(wrap_env -f -p "-b arg") - assertEquals "${output[@]}" "run_env_as_fakeroot(-b arg,)" + assertEquals "${output[@]}" "run_env_as_fakeroot(,-b arg,)" local output=$(wrap_env -f -p "-b arg" -- command -kv) - assertEquals "${output[@]}" "run_env_as_fakeroot(-b arg,command -kv)" + assertEquals "${output[@]}" "run_env_as_fakeroot(,-b arg,command -kv)" local output=$(wrap_env -f command --as) - assertEquals "${output[@]}" "run_env_as_fakeroot(,command --as)" + assertEquals "${output[@]}" "run_env_as_fakeroot(,,command --as)" + local output=$(wrap_env -a "myarch" -f command --as) + assertEquals "${output[@]}" "run_env_as_fakeroot(myarch,,command --as)" } function test_run_env_as_user(){ local output=$(wrap_env) - assertEquals $output "run_env_as_user(,)" + assertEquals $output "run_env_as_user(,,)" local output=$(wrap_env -p "-b arg") - assertEquals "$output" "run_env_as_user(-b arg,)" + assertEquals "$output" "run_env_as_user(,-b arg,)" local output=$(wrap_env -p "-b arg" -- command -ll) - assertEquals "$output" "run_env_as_user(-b arg,command -ll)" + assertEquals "$output" "run_env_as_user(,-b arg,command -ll)" local output=$(wrap_env command -ls) - assertEquals "$output" "run_env_as_user(,command -ls)" + assertEquals "$output" "run_env_as_user(,,command -ls)" + local output=$(wrap_env -a "myarch" -- command -ls) + assertEquals "$output" "run_env_as_user(myarch,,command -ls)" } function test_run_env_as_root(){ local output=$(wrap_env -r) @@ -117,6 +125,8 @@ function test_check_cli(){ assertEquals $? 1 $(wrap_env -p args -v 2> /dev/null) assertEquals $? 1 + $(wrap_env -a arch -v 2> /dev/null) + assertEquals $? 1 $(wrap_env -d args 2> /dev/null) assertEquals $? 1 } diff --git a/tests/test_core.sh b/tests/test_core.sh index baf17dd..cafaa72 100755 --- a/tests/test_core.sh +++ b/tests/test_core.sh @@ -149,6 +149,9 @@ function test_setup_env(){ setup_env 1> /dev/null assertTrue "[ -e $JUNEST_HOME/file ]" assertTrue "[ -e $JUNEST_HOME/run/lock ]" + + $(setup_env "noarch" 2> /dev/null) + assertEquals 1 $? } @@ -237,21 +240,28 @@ function test_run_env_as_junest_root(){ function test_run_env_as_user(){ install_mini_env - local output=$(run_env_as_user "-k 3.10" "/usr/bin/mkdir" "-v" "/newdir2" | awk -F: '{print $1}') + local output=$(run_env_as_user "" "-k 3.10" "/usr/bin/mkdir" "-v" "/newdir2" | awk -F: '{print $1}') assertEquals "$output" "/usr/bin/mkdir" assertTrue "[ -e $JUNEST_HOME/newdir2 ]" SH=("/usr/bin/mkdir" "-v" "/newdir") - local output=$(run_env_as_user "-k 3.10" | awk -F: '{print $1}') + local output=$(run_env_as_user "" "-k 3.10" | awk -F: '{print $1}') assertEquals "$output" "/usr/bin/mkdir" assertTrue "[ -e $JUNEST_HOME/newdir ]" + + $(run_env_as_user "noarch" "-k 3.10" "mycommand" 2> /dev/null) + assertEquals 1 $? + + local different_arch=(${ARCH_LIST[@]/$ARCH}) + $(run_env_as_user "${different_arch[0]}" "-k 3.10" "mycommand" 2> /dev/null) + assertEquals 1 $? } function test_run_env_as_proot_mtab(){ install_mini_env - $(run_env_as_fakeroot "-k 3.10" "echo") + $(run_env_as_fakeroot "" "-k 3.10" "echo") assertTrue "[ -e $JUNEST_HOME/etc/mtab ]" - $(run_env_as_user "-k 3.10" "echo") + $(run_env_as_user "" "-k 3.10" "echo") assertTrue "[ ! -e $JUNEST_HOME/etc/mtab ]" } @@ -268,32 +278,32 @@ function test_run_env_as_root_mtab(){ function test_run_env_with_quotes(){ install_mini_env - local output=$(run_env_as_user "-k 3.10" "bash" "-c" "/usr/bin/mkdir -v /newdir2" | awk -F: '{print $1}') - assertEquals "$output" "/usr/bin/mkdir" + local output=$(run_env_as_user "" "-k 3.10" "bash" "-c" "/usr/bin/mkdir -v /newdir2" | awk -F: '{print $1}') + assertEquals "/usr/bin/mkdir" "$output" assertTrue "[ -e $JUNEST_HOME/newdir2 ]" } function test_run_env_as_user_proot_args(){ install_mini_env - run_env_as_user "--help" "" &> /dev/null - assertEquals $? 0 + run_env_as_user "" "--help" "" &> /dev/null + assertEquals 0 $? mkdir $JUNEST_TEMPDIR/newdir touch $JUNEST_TEMPDIR/newdir/newfile - run_env_as_user "-b $JUNEST_TEMPDIR/newdir:/newdir -k 3.10" "ls" "-l" "/newdir/newfile" &> /dev/null - assertEquals $? 0 + run_env_as_user "" "-b $JUNEST_TEMPDIR/newdir:/newdir -k 3.10" "ls" "-l" "/newdir/newfile" &> /dev/null + assertEquals 0 $? $(_run_env_with_proot --helps 2> /dev/null) - assertEquals $? 1 + assertEquals 1 $? } function test_run_env_with_proot_compat(){ PROOT_COMPAT="/bin/true" _run_env_with_proot "" "" &> /dev/null - assertEquals $? 0 + assertEquals 0 $? $(PROOT_COMPAT="/bin/false" _run_env_with_proot --helps 2> /dev/null) - assertEquals $? 1 + assertEquals 1 $? } function test_run_env_with_proot_as_root(){ @@ -302,44 +312,51 @@ function test_run_env_with_proot_as_root(){ install_mini_env $(sudo run_env_as_user 2> /dev/null) - assertEquals $? 1 + assertEquals 1 $? $(sudo run_env_as_fakeroot 2> /dev/null) - assertEquals $? 1 + assertEquals 1 $? } function test_run_proot_seccomp(){ PROOT_COMPAT=env local output=$(proot_cmd | grep "^PROOT_NO_SECCOMP") - assertEquals "$output" "" + assertEquals "" "$output" envv(){ env | grep "^PROOT_NO_SECCOMP" } PROOT_COMPAT=envv local output=$(proot_cmd 2> /dev/null | grep "^PROOT_NO_SECCOMP") - assertEquals "$output" "PROOT_NO_SECCOMP=1" + assertEquals "PROOT_NO_SECCOMP=1" "$output" } function test_run_env_as_fakeroot(){ install_mini_env - local output=$(run_env_as_fakeroot "-k 3.10" "id" | awk '{print $1}') - assertEquals "$output" "uid=0(root)" + local output=$(run_env_as_fakeroot "" "-k 3.10" "id" | awk '{print $1}') + assertEquals "uid=0(root)" "$output" + + $(run_env_as_fakeroot "noarch" "-k 3.10" "mycommand" 2> /dev/null) + assertEquals 1 $? + + local different_arch=(${ARCH_LIST[@]/$ARCH}) + $(run_env_as_fakeroot "${different_arch[0]}" "-k 3.10" "mycommand" 2> /dev/null) + assertEquals 1 $? } function test_delete_env(){ install_mini_env echo "N" | delete_env 1> /dev/null is_env_installed - assertEquals $? 0 + assertEquals 0 $? echo "Y" | delete_env 1> /dev/null is_env_installed - assertEquals $? 1 + assertEquals 1 $? } function test_nested_env(){ install_mini_env JUNEST_ENV=1 bash -ic "source $CURRPWD/$(dirname $0)/../lib/core.sh" &> /dev/null - assertEquals $? 1 + assertEquals 1 $? } source $(dirname $0)/shunit2 diff --git a/tests/test_util.sh b/tests/test_util.sh index c10a095..80fd660 100755 --- a/tests/test_util.sh +++ b/tests/test_util.sh @@ -60,4 +60,13 @@ function test_insert_quotes_on_spaces(){ assertEquals "this is \"a test\"" "$actual" } +function test_contains_element(){ + array=("something to search for" "a string" "test2000") + contains_element "a string" "${array[@]}" + assertEquals "$?" "0" + + contains_element "blabla" "${array[@]}" + assertEquals "$?" "1" +} + source $(dirname $0)/shunit2