#!/bin/bash -e

SCRIPT=$(readlink -f "$0")
SCRIPTPATH=$(dirname "$SCRIPT")
BUILDPATH=$(pwd)

if [ ! -e Makefile ] && [ ! -e build.ninja ] || [ ! -e ../do_cmake.sh ]; then
    echo "must run from cmake build dir"
    exit 1
fi

base="quay.ceph.io/ceph-ci/ceph:main"
target=""
push=0
strip=1

py=0
dashboard=0
core=0
cephfs=0
rgw=0
rbd=0
all=1
asroot=""

usage() {
    echo "usage: $SCRIPT [options]"
    echo "  --base <image>       base container image [$base]"
    echo "  --target <image>     target image (required)"
    echo "  --push               push when done"
    echo "  --strip              strip binaries"
    echo "  --root-build         build image as root"
    echo
    echo "  --py                 python components (python-common, mgr)"
    echo "  --dashboard          dashboard"
    echo "  --core               mon, mgr, osd, mds, bins and libraries"
    echo "  --rgw                radosgw, radosgw-admin"
}

while [ -n "$1" ]; do
    case $1 in
	--base)
	    shift
	    base="$1"
	    ;;
	--target | -t)
	    shift
	    target="$1"
	    ;;
	--nostrip)
	    strip=0
	    ;;
	--root-build)
	    asroot="sudo"
	    ;;
	-h | --help)
	    usage
	    exit 0
	    ;;
	--push)
	    push=1
	    ;;

	--py)
	    py=1
	    all=0
	    ;;
	--dashboard)
	    py=1
	    dashboard=1
	    all=0
	    ;;
	--core)
	    core=1
	    all=0
	    rbd=1
	    cephfs=1
	    ;;
	--rgw)
	    rgw=1
	    all=0
	    ;;

	*)
	    echo "unrecognized option $1"
	    exit 1
	    ;;
    esac
    shift
done

if [ -z "$target" ]; then
    echo "must specify --target <image>"
    exit 1
fi

if [ -x /usr/bin/podman ]; then
    runtime="podman"
elif [ -x /usr/bin/docker ]; then
    runtime="docker"
else
    echo "cannot find podman or docker in PATH"
    exit 1
fi

TMP="$BUILDPATH/tmp.cpatch"
if [ -d $TMP ]; then rm -rf $TMP ; fi
mkdir -p $TMP

if [ $all -eq 1 ]; then
    echo "consider --py, --core, and/or --rgw for an abbreviated (faster) build."
fi

dockerfile="FROM $base"$'\n'

if [ $py -eq 1 ] || [ $all -eq 1 ]; then
    pushd ../src/pybind/mgr > /dev/null
    find ./ -name "*.pyc" -exec rm -f {} \;
    if [ $dashboard -eq 1 ] || [ $all -eq 1 ]; then
	echo "py + dashboard"
	exclude=""
    else
	echo "py"
	# Exclude node_modules because it's the huge sources in
	# dashboard/frontend
	exclude="--exclude=node_modules --exclude=.tox --exclude=.angular"
    fi
    tar $exclude --exclude=tests --exclude-backups -cf $TMP/mgr_plugins.tar *
    popd > /dev/null
    dockerfile+=$'ADD mgr_plugins.tar /usr/share/ceph/mgr\n'

    pushd ../src/python-common > /dev/null
    find ./ -name "*.pyc" -exec rm -f {} \;
    # Exclude node_modules because it's the huge sources in dashboard/frontend
    tar --exclude=node_modules --exclude=tests --exclude=.tox --exclude-backups -cf $TMP/python_common.tar *
    popd > /dev/null
    dockerfile+=$'ADD python_common.tar tmp_python_common\n'
    dockerfile+=$'RUN for i in tmp_python_common/*; do find /usr/lib/python*/site-packages -type d -name $(basename $i) -exec cp -frpv $i/* \'{}\' \;; done && rm -rf tmp_python_common\n'

    pushd lib/cython_modules/lib.3
    CYTHONLIBS="*.cpython-3*.so"
    mkdir -p $TMP/cythonlib
    for f in $CYTHONLIBS; do cp $f $TMP/cythonlib ; done
    [ $strip -eq 1 ] && strip $TMP/cythonlib/*
    popd > /dev/null
    dockerfile+=$'ADD cythonlib tmp_python_common\n'
    dockerfile+=$'RUN for i in tmp_python_common/*; do find /usr/lib/python*/site-packages -type d -name $(basename $i) -exec cp -frpv $i/* \'{}\' \;; done && rm -rf tmp_python_common\n'

    # cephadm
    pushd ../src/cephadm > /dev/null
    ./build.sh $TMP/cephadm
    dockerfile+=$'ADD cephadm /usr/sbin/cephadm\n'
    popd > /dev/null
fi

# Create some temporary directories.  The binaries or libraries to patch are placed in these as the cli options are processed.
# At the end the base container is searched for files in these directories and the original files are replaced.
mkdir -p $TMP/bin
mkdir -p $TMP/lib

if [ $core -eq 1 ] || [ $all -eq 1 ]; then
    # binaries are annoying because the ceph version is embedded all over
    # the place, so we have to include everything but the kitchen sink.
    BINS="ceph-mgr ceph-mon ceph-osd rados"
    if [ $core -eq 1 ]; then
        echo "core"
        for f in $BINS; do cp bin/$f $TMP/bin ; done
    else
        # copy ALL locally built binaries (apart from test programs) over those that already exist in the image.
        echo "all"
        find bin -type f \! \( -name "ceph_test*" -o -name "test_*" -o -name "unittest_*" \) -exec cp {} $TMP/bin \; 
        # Need to strip all binaries that are copied (except those in the core BINS list) otherwise the container will be huge
        # Some of the files in the bins directory are actually scripts so ignore errors when strip fails for these files.
        find $TMP/bin -type f $(printf "! -name %s " $BINS) -exec strip {} \; || true
    fi
    [ $strip -eq 1 ] && for f in $BINS; do strip $TMP/bin/$f; done

    # Copy all locally built libraries over those that already exist in the image
    cp -d lib/*.so lib/*.so.* $TMP/lib
    [ $strip -eq 1 ] && strip $TMP/lib/*
fi

if [ $rgw -eq 1 ] || [ $all -eq 1 ]; then
    echo "rgw"
    RGW="radosgw radosgw-admin"
    for f in $RGW; do cp bin/$f $TMP/bin ; done
    [ $strip -eq 1 ] && for f in $RGW; do strip $TMP/bin/$f; done

    RGWLIBS="librados.so.* libceph-common.so.*"
    for f in $RGWLIBS; do cp lib/$f $TMP/lib ; done
    [ $strip -eq 1 ] && for f in $RGWLIBS; do strip $TMP/lib/$f; done
fi

if [ $cephfs -eq 1 ] || [ $all -eq 1 ]; then
    echo "cephfs"
    FS="ceph-mds"
    for f in $FS; do cp bin/$f $TMP/bin ; done
    [ $strip -eq 1 ] && for f in $FS; do strip $TMP/bin/$f; done

    FSLIBS="libcephfs.so*"
    for f in lib/$FSLIBS; do cp $f $TMP/fslib ; done
    [ $strip -eq 1 ] && for f in $FSLIBS; do strip $TMP/lib/$f; done
fi

if [ $rbd -eq 1 ] || [ $all -eq 1 ]; then
    echo "rbd"
    RBD="rbd rbd-mirror"
    for f in $RBD; do cp bin/$f $TMP/bin ; done
    [ $strip -eq 1 ] && for f in $RBD; do strip $TMP/bin/$f; done

    RBDLIBS="librbd.so*"
    for f in lib/$RBDLIBS; do cp $f $TMP/lib ; done
    [ $strip -eq 1 ] && for f in $RBDLIBS; do strip $TMP/lib/$f; done
fi

# For every binary file that was copied to the $TMP/bin directory by the steps above, search for the existing file in the container and replace it.
dockerfile+=$'ADD bin /tmpbin\n'
dockerfile+=$'RUN for i in tmpbin/*; do find /usr/bin /usr/sbin -name $(basename $i) -exec mv -f $i \'{}\' \;; echo $(basename $i); done && rm -rf tmpbin\n'

# For every library file that was copied to the $TMP/lib directory by the steps above, search for the existing file in the container and replace it.
dockerfile+=$'ADD lib /tmplib\n'
dockerfile+=$'RUN for i in tmplib/*; do find /usr/lib64 -name $(basename $i) -exec mv -f $i \'{}\' \;; echo $(basename $i); done && rm -rf tmplib\n'

# by default locally built binaries assume /usr/local
dockerfile+=$'RUN rm -rf /usr/local/lib64 ; ln -sf /usr/lib64 /usr/local ; ln -sf /usr/share/ceph /usr/local/share\n'
# locally built binaries assume libceph-common.so.2 is in /usr/lib64 - create link to library that was just copied
dockerfile+=$'RUN ln -sf /usr/lib64/ceph/libceph-common.so.2 /usr/lib64/libceph-common.so.2\n'

echo "build"
pushd $TMP > /dev/null
echo "$dockerfile" > Dockerfile
$asroot $runtime build -t $target .
popd > /dev/null

if [ $push -eq 1 ]; then
    echo "push"
    $asroot $runtime push $target
fi

rm -r $TMP
