diff --git a/bin/juju b/bin/juju index 8f12cdc..c00da88 100755 --- a/bin/juju +++ b/bin/juju @@ -105,79 +105,74 @@ check_cli(){ } -################################### -### MAIN PROGRAM ### -################################### +function parse_arguments(){ + TEMP=$(getopt -o drp:fbi:hv --long delete,root,proot-args:,fakeroot,build-image,setup-from-file:,help,version -n 'juju' -- "$@") + eval set -- "$TEMP" -TEMP=`getopt -o drp:fbi:hv --long delete,root,proot-args:,fakeroot,build-image,setup-from-file:,help,version -n 'juju' -- "$@"` + OPT_SETUP_FROM_FILE=false + IMAGE_FILE="" + OPT_FAKEROOT=false + OPT_ROOT=false + OPT_PROOT_ARGS=false + PROOT_ARGS="" + OPT_BUILD_IMAGE=false + OPT_DELETE=false + OPT_HELP=false + OPT_VERSION=false + while true ; do + case "$1" in + -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 ;; + -b|--build-image) OPT_BUILD_IMAGE=true ; shift ;; + -d|--delete) OPT_DELETE=true ; shift ;; + -h|--help) OPT_HELP=true ; shift ;; + -v|--version) OPT_VERSION=true ; shift ;; + --) shift ; break ;; + *) die "Internal error!" ;; + esac + done -if [ $? != 0 ] ; then error "Error on parsing the command line. Try juju -h." ; exit ; fi + ARGS=() + for arg do + ARGS+=($arg) + done +} -# Note the quotes around `$TEMP': they are essential! -eval set -- "$TEMP" +function execute_operation(){ + $OPT_HELP && usage && return + $OPT_VERSION && version && return -OPT_SETUP_FROM_FILE=false -IMAGE_FILE="" -OPT_FAKEROOT=false -OPT_ROOT=false -OPT_PROOT_ARGS=false -PROOT_ARGS="" -OPT_BUILD_IMAGE=false -OPT_DELETE=false -OPT_HELP=false -OPT_VERSION=false -while true ; do - case "$1" in - -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 ;; - -b|--build-image) OPT_BUILD_IMAGE=true ; shift ;; - -d|--delete) OPT_DELETE=true ; shift ;; - -h|--help) OPT_HELP=true ; shift ;; - -v|--version) OPT_VERSION=true ; shift ;; - --) shift ; break ;; - *) error "Internal error!" ; exit 1 ;; - esac -done + if $OPT_BUILD_IMAGE; then + build_image_juju + return + elif $OPT_DELETE; then + delete_juju + return + fi -ARGS=() -for arg do - ARGS+=($arg) -done + if ! is_juju_installed + then + if $OPT_SETUP_FROM_FILE; then + setup_from_file_juju $IMAGE_FILE + else + setup_juju + fi + elif $OPT_SETUP_FROM_FILE; then + die "Error: The image cannot be installed since $JUJU_HOME is not empty." + fi -check_cli || exit 1 - -################ DEFINE ACTION ######################## - -$OPT_HELP && usage && exit -$OPT_VERSION && version && exit - -if $OPT_BUILD_IMAGE; then - build_image_juju - exit -elif $OPT_DELETE; then - delete_juju - exit -fi - -if ! is_juju_installed -then - if $OPT_SETUP_FROM_FILE; then - setup_from_file_juju $IMAGE_FILE - else - setup_juju - fi -elif $OPT_SETUP_FROM_FILE; then - die "Error: The image cannot be installed since $JUJU_HOME is not empty." -fi - -if $OPT_FAKEROOT; then - run_juju_as_fakeroot "${PROOT_ARGS}" ${ARGS[@]} -elif $OPT_ROOT; then - run_juju_as_root ${ARGS[@]} -else - run_juju_as_user "${PROOT_ARGS}" ${ARGS[@]} -fi + if $OPT_FAKEROOT; then + run_juju_as_fakeroot "${PROOT_ARGS}" ${ARGS[@]} + elif $OPT_ROOT; then + run_juju_as_root ${ARGS[@]} + else + run_juju_as_user "${PROOT_ARGS}" ${ARGS[@]} + fi +} +parse_arguments $@ +check_cli +execute_operation # vim: set ts=4 sw=4 noet: diff --git a/lib/core.sh b/lib/core.sh index 631931d..c6cc042 100644 --- a/lib/core.sh +++ b/lib/core.sh @@ -75,6 +75,10 @@ then else die "The variable JUJU_ENV is not properly set" fi + +CHROOT=${JUJU_HOME}/usr/bin/arch-chroot +TRUE=${JUJU_HOME}/usr/bin/true + ################################# MAIN FUNCTIONS ############################## function is_juju_installed(){ @@ -160,12 +164,12 @@ function run_juju_as_root(){ [ "$JUJU_ENV" == "1" ] && die "Error: The operation is not allowed inside JuJu environment" mkdir -p ${JUJU_HOME}/${HOME} - ${JUJU_HOME}/usr/bin/arch-chroot $JUJU_HOME /usr/bin/bash -c "mkdir -p /run/lock && $(_define_chroot_args "$@")" + ${CHROOT} $JUJU_HOME /usr/bin/bash -c "mkdir -p /run/lock && $(_define_chroot_args "$@")" } function _run_juju_with_proot(){ - if ${PROOT} ${JUJU_HOME}/usr/bin/true &> /dev/null + if ${PROOT} ${TRUE} &> /dev/null then JUJU_ENV=1 ${PROOT} $@ else diff --git a/lib/util.sh b/lib/util.sh index 15fd48c..dc88506 100644 --- a/lib/util.sh +++ b/lib/util.sh @@ -17,7 +17,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -echoerr() { echo "$@" 1>&2; } +function echoerr(){ + echo "$@" 1>&2 +} function die(){ # $@: msg (mandatory) - str: Message to print error $@ diff --git a/tests/test_all.sh b/tests/test_all.sh new file mode 100755 index 0000000..684e6fd --- /dev/null +++ b/tests/test_all.sh @@ -0,0 +1,5 @@ + +for tst in $(ls $(dirname $0)/test_* | grep -v $(basename $0)) +do + $tst +done diff --git a/tests/test_core.sh b/tests/test_core.sh new file mode 100755 index 0000000..5c85925 --- /dev/null +++ b/tests/test_core.sh @@ -0,0 +1,163 @@ +#!/bin/bash +# To test this module you need to run it inside a Archlinux system. Also, the following packages needs to be installed: +# bash +# pacman +# arch-install-scripts +# proot +# coreutils +# grep +# sudo +# gawk + +source "$(dirname $0)/utils.sh" +CURRPWD=$PWD +JUJU_HOME="" + +function set_up(){ + cd $CURRPWD + JUJU_HOME=$(TMPDIR=/tmp mktemp -d -t jujuhome.XXXXXXXXXX) + source "$(dirname $0)/../lib/core.sh" + ORIGIN_WD=$(TMPDIR=/tmp mktemp -d -t jujuowd.XXXXXXXXXX) + cd $ORIGIN_WD + JUJU_TEMPDIR=$(TMPDIR=/tmp mktemp -d -t jujutemp.XXXXXXXXXX) + + trap - QUIT EXIT ABRT KILL TERM INT + trap "rm -rf ${JUJU_HOME}; rm -rf ${ORIGIN_WD}; rm -rf ${JUJU_TEMPDIR}" EXIT QUIT ABRT KILL TERM INT +} + + +function tear_down(){ + rm -rf $JUJU_HOME + rm -rf $ORIGIN_WD + rm -rf $JUJU_TEMPDIR + trap - QUIT EXIT ABRT KILL TERM INT +} + +function test_is_juju_installed(){ + is_juju_installed + is_equal $? 1 || return 1 + touch $JUJU_HOME/just_file + is_juju_installed + is_equal $? 0 || return 1 +} + + +function test_setup_juju(){ + wget_mock(){ + # Proof that the setup is happening + # inside $JUJU_TEMPDIR + local cwd=${PWD#${JUJU_TEMPDIR}} + local parent_dir=${PWD%${cwd}} + is_equal $JUJU_TEMPDIR ${parent_dir} || return 1 + touch file + tar -czvf juju-${ARCH}.tar.gz file + } + WGET=wget_mock + setup_juju 1> /dev/null + [ -e $JUJU_HOME/file ] || return 1 + [ -e $JUJU_HOME/run/lock ] || return 1 + + export -f setup_juju + JUJU_ENV=1 bash -ic "setup_juju" &> /dev/null + is_equal $? 1 || return 1 +} + + +function test_setup_from_file_juju(){ + touch file + tar -czvf juju-${ARCH}.tar.gz file 1> /dev/null + setup_from_file_juju juju-${ARCH}.tar.gz 1> /dev/null + [ -e $JUJU_HOME/file ] || return 1 + [ -e $JUJU_HOME/run/lock ] || return 1 + + export -f setup_from_file_juju + bash -ic "setup_from_file_juju noexist.tar.gz" &> /dev/null + is_equal $? 1 || return 1 + + export -f setup_from_file_juju + JUJU_ENV=1 bash -ic "setup_from_file_juju" &> /dev/null + is_equal $? 1 || return 1 +} + + +function test_run_juju_as_root(){ + install_mini_juju + CHROOT="sudo $CHROOT" + SH="type -t type" + local output=$(run_juju_as_root) + is_equal $output "builtin" || return 1 + local output=$(run_juju_as_root pwd) + is_equal $output "/" || return 1 + run_juju_as_root "[ -e /run/lock ]" + is_equal $? 0 || return 1 + [ -e $JUJU_HOME/${HOME} ] || return 1 + + export -f run_juju_as_root + JUJU_ENV=1 bash -ic "run_juju_as_root" &> /dev/null + is_equal $? 1 || return 1 +} + +function test_run_juju_as_user(){ + install_mini_juju + local output=$(run_juju_as_user "" "mkdir -v /newdir2") + is_equal "$output" "/usr/bin/mkdir: created directory '/newdir2'" || return 1 + [ -e $JUJU_HOME/newdir2 ] + is_equal $? 0 || return 1 + + SH="mkdir -v /newdir" + local output=$(run_juju_as_user "") + is_equal "$output" "/usr/bin/mkdir: created directory '/newdir'" || return 1 + [ -e $JUJU_HOME/newdir ] + is_equal $? 0 || return 1 +} + +function test_run_juju_as_user_proot_args(){ + install_mini_juju + run_juju_as_user "--help" "" 1> /dev/null + is_equal $? 0 || return 1 + run_juju_as_user "--helps" "" &> /dev/null + is_equal $? 1 || return 1 + + mkdir $JUJU_TEMPDIR/newdir + touch $JUJU_TEMPDIR/newdir/newfile + run_juju_as_user "-b $JUJU_TEMPDIR/newdir:/newdir" "ls -l /newdir/newfile" 1> /dev/null + is_equal $? 0 || return 1 +} + +function test_run_juju_as_user_seccomp(){ + install_mini_juju + local output=$(run_juju_as_user "" "env" | grep "PROOT_NO_SECCOMP") + is_equal $output "" || return 1 + + TRUE="/usr/bin/false" + local output=$(run_juju_as_user "" "env" | grep "PROOT_NO_SECCOMP") + is_equal $output "PROOT_NO_SECCOMP=1" || return 1 +} + +function test_run_juju_as_fakeroot(){ + install_mini_juju + local output=$(run_juju_as_fakeroot "" "id" | awk '{print $1}') + is_equal "$output" "uid=0" || return 1 +} + +function test_delete_juju(){ + install_mini_juju + echo "N" | delete_juju 1> /dev/null + is_juju_installed + is_equal $? 0 || return 1 + echo "Y" | delete_juju 1> /dev/null + is_juju_installed + is_equal $? 1 || return 1 + + export -f delete_juju + JUJU_ENV=1 bash -ic "delete_juju" &> /dev/null + is_equal $? 1 || return 1 +} + + +for func in $(declare -F | grep test_ | awk '{print $3}' | xargs) +do + set_up + $func && echo -e "${func}...\033[1;32mOK\033[0m" || echo -e "${func}...\033[1;31mFAIL\033[0m" + tear_down +done diff --git a/tests/test_juju.sh b/tests/test_juju.sh new file mode 100755 index 0000000..8d9a94a --- /dev/null +++ b/tests/test_juju.sh @@ -0,0 +1,134 @@ +#!/bin/bash +source "$(dirname $0)/utils.sh" +source $(dirname $0)/../bin/juju -h &> /dev/null +## Mock functions ## +function usage(){ + echo "usage" +} +function version(){ + echo "version" +} +function build_image_juju(){ + echo "build_image_juju" +} +function delete_juju(){ + echo "delete_juju" +} +function is_juju_installed(){ + return 0 +} +function setup_from_file_juju(){ + echo "setup_from_file_juju $@" +} +function setup_juju(){ + echo "setup_juju" +} +function run_juju_as_fakeroot(){ + echo "run_juju_as_fakeroot $@" +} +function run_juju_as_root(){ + echo "run_juju_as_root $@" +} +function run_juju_as_user(){ + echo "run_juju_as_user $@" +} + +function wrap_juju(){ + parse_arguments $@ + check_cli + execute_operation +} + + +function set_up(){ + echo > /dev/null +} + +function tear_down(){ + echo > /dev/null +} + + +function test_help(){ + local output=$(wrap_juju -h) + is_equal $output "usage" || return 1 + local output=$(wrap_juju --help) + is_equal $output "usage" || return 1 +} +function test_version(){ + local output=$(wrap_juju -v) + is_equal $output "version" || return 1 + local output=$(wrap_juju --version) + is_equal $output "version" || return 1 +} +function test_build_image_juju(){ + local output=$(wrap_juju -b) + is_equal $output "build_image_juju" || return 1 + local output=$(wrap_juju --build-image) + is_equal $output "build_image_juju" || return 1 +} +function test_delete_juju(){ + local output=$(wrap_juju -d) + is_equal $output "delete_juju" || return 1 + local output=$(wrap_juju --delete) + is_equal $output "delete_juju" || return 1 +} +function test_run_juju_as_fakeroot(){ + local output=$(wrap_juju -f) + is_equal $output "run_juju_as_fakeroot" || return 1 + local output=$(wrap_juju --fakeroot) + is_equal $output "run_juju_as_fakeroot" || return 1 + + local output=$(wrap_juju -f -p "-b arg") + is_equal "${output[@]}" "run_juju_as_fakeroot -b arg" || return 1 + local output=$(wrap_juju -f -p "-b arg" -- command) + is_equal "${output[@]}" "run_juju_as_fakeroot -b arg command" || return 1 + local output=$(wrap_juju -f command) + is_equal "${output[@]}" "run_juju_as_fakeroot command" || return 1 +} +function test_run_juju_as_user(){ + local output=$(wrap_juju) + is_equal $output "run_juju_as_user" || return 1 + + local output=$(wrap_juju -p "-b arg") + is_equal "${output[@]}" "run_juju_as_user -b arg" || return 1 + local output=$(wrap_juju -p "-b arg" -- command) + is_equal "${output[@]}" "run_juju_as_user -b arg command" || return 1 + local output=$(wrap_juju command) + is_equal "${output[@]}" "run_juju_as_user command" || return 1 +} +function test_run_juju_as_root(){ + local output=$(wrap_juju -r) + is_equal $output "run_juju_as_root" || return 1 + + local output=$(wrap_juju -r command) + is_equal "${output[@]}" "run_juju_as_root command" || return 1 +} + +function test_check_cli(){ + export -f check_cli + export -f parse_arguments + export -f execute_operation + export -f wrap_juju + bash -ic "wrap_juju -b -h" &> /dev/null + is_equal $? 1 || return 1 + bash -ic "wrap_juju -d -r" &> /dev/null + is_equal $? 1 || return 1 + bash -ic "wrap_juju -h -f" &> /dev/null + is_equal $? 1 || return 1 + bash -ic "wrap_juju -v -i fsd" &> /dev/null + is_equal $? 1 || return 1 + bash -ic "wrap_juju -f -r" &> /dev/null + is_equal $? 1 || return 1 + bash -ic "wrap_juju -p args -v" &> /dev/null + is_equal $? 1 || return 1 + bash -ic "wrap_juju -d args" &> /dev/null + is_equal $? 1 || return 1 +} + +for func in $(declare -F | grep test_ | awk '{print $3}' | xargs) +do + set_up + $func && echo -e "${func}...\033[1;32mOK\033[0m" || echo -e "${func}...\033[1;31mFAIL\033[0m" + tear_down +done diff --git a/tests/test_util.sh b/tests/test_util.sh new file mode 100755 index 0000000..757f08f --- /dev/null +++ b/tests/test_util.sh @@ -0,0 +1,63 @@ +#!/bin/bash +source "$(dirname $0)/utils.sh" +source "$(dirname $0)/../lib/util.sh" + +function test_echoerr(){ + local actual=$(echoerr "Test" 2>&1) + is_equal "$actual" "Test" || return 1 + return 0 +} + +function test_error(){ + local actual=$(error "Test" 2>&1) + local expected=$(echo -e "\033[1;31mTest\033[0m") + is_equal "$actual" "$expected" || return 1 + return 0 +} + +function test_warn(){ + local actual=$(warn "Test" 2>&1) + local expected=$(echo -e "\033[1;33mTest\033[0m") + is_equal "$actual" "$expected" || return 1 + return 0 +} + +function test_info(){ + local actual=$(info "Test") + local expected=$(echo -e "\033[1;37mTest\033[0m") + is_equal "$actual" "$expected" || return 1 + return 0 +} + +function test_die(){ + local actual=$(die "Test" 2>&1) + local expected=$(echo -e "\033[1;31mTest\033[0m") + is_equal "$actual" "$expected" || return 1 + bash -ic "die Dying" &> /dev/null + is_equal $? 1 || return 1 + return 0 +} + +function test_ask(){ + echo "Y" | ask "Test" &> /dev/null + is_equal $? 0 || return 1 + echo "y" | ask "Test" &> /dev/null + is_equal $? 0 || return 1 + echo "N" | ask "Test" &> /dev/null + is_equal $? 1 || return 1 + echo "n" | ask "Test" &> /dev/null + is_equal $? 1 || return 1 + echo -e "\n" | ask "Test" &> /dev/null + is_equal $? 0 || return 1 + echo -e "\n" | ask "Test" "N" &> /dev/null + is_equal $? 1 || return 1 + echo -e "asdf\n\n" | ask "Test" "N" &> /dev/null + is_equal $? 1 || return 1 + return 0 +} + + +for func in $(declare -F | grep test_ | awk '{print $3}' | xargs) +do + $func && echo -e "${func}...\033[1;32mOK\033[0m" || echo -e "${func}...\033[1;31mFAIL\033[0m" +done diff --git a/tests/utils.sh b/tests/utils.sh new file mode 100644 index 0000000..9f6f263 --- /dev/null +++ b/tests/utils.sh @@ -0,0 +1,50 @@ +function install_executable(){ + for i in $( ldd $* | grep -v dynamic | cut -d " " -f 3 | sed 's/://' | sort | uniq ) + do + cp -f --parents $i $JUJU_HOME + done + + # ARCH amd64 + if [ -f /lib64/ld-linux-x86-64.so.2 ]; then + cp -f --parents /lib64/ld-linux-x86-64.so.2 $JUJU_HOME + fi + + # ARCH i386 + if [ -f /lib/ld-linux.so.2 ]; then + cp -f --parents /lib/ld-linux.so.2 $JUJU_HOME + fi + + cp -f --parents $* $JUJU_HOME/ +} + +function install_package(){ + # Copy the directories + pacman -Ql $1 | grep "/$" | sed 's/.* //' | xargs -I {} mkdir -p $JUJU_HOME/{} + + # Copy the files + pacman -Ql $1 | grep -v "/$" | sed 's/.* //' | xargs -I {} cp -f --parents {} $JUJU_HOME + + # Copy the dynamic libraries of the executables + #export -f install_executable + #export JUJU_HOME + #pacman -Ql $1 | grep -v "/$" | sed 's/.* //' | grep "^/usr/bin/" | xargs -I {} bash -ic "install_executable {}" +} + +function install_mini_juju(){ + install_package filesystem 2> /dev/null + install_package arch-install-scripts + install_package bash + install_package proot + install_package coreutils + install_executable /usr/bin/bash + install_executable /usr/bin/ls + install_executable /usr/bin/mkdir + install_executable /usr/bin/proot +} + + +function is_equal(){ + [ "$1" == "$2" ] || return 1 && return 0 +} + +