#!/bin/bash #emacs: -*- mode: shell-script; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*- #ex: set sts=4 ts=4 sw=4 noet: # # # COPYRIGHT: Yaroslav Halchenko 2018 # # LICENSE: MIT # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # # # TODOs when get to refactor this prototype # - add options to # ... # Description for invocation # - you can specify a list of images, as their original docker/github ids # which should be considered. E.g. # # scripts/create_singularities poldracklab/ds003-example bids/validator # # will consider only two images - one for poldracklab and another one # bids/validator, the other ones will be reported skipped set -eu IMAGES_DIR="images" ARGS=( "${@}" ) # Use local dedicated "tmp/" directory since any other might be too small topdir=$(readlink -f "$0" | xargs dirname | xargs dirname) export TMPDIR="$topdir/tmp" export SINGULARITY_TMPDIR="$TMPDIR" mkdir -p "$TMPDIR" cd "$topdir" # A helper to select only specific images we would like to check/deal with function check_if_of_interest() { # Somehow if no arguments provided then ARGS variable can become unbound here # so we define it with default value then : "${ARGS:=}" if [ -z "$ARGS" ]; then return 0 fi for arg in "${ARGS[@]}"; do if [ "$1" = "$arg" ]; then return 0 fi done info "skip $1" return 1 } function debug() { : # echo -e "D:" "$@" >&2 } function info() { echo -e "I:" "$@" >&2 } function error() { echo -e "E:" "$@" >&2 } function get_last_docker_version_tag() { dh="$1" j=$(curl -s -k "https://registry.hub.docker.com/v1/repositories/$dh/tags") versions=$(echo "$j" | jq -c '.[]["name"]' | sed -e 's,",,g' | sort -Vr) nversions=$(echo "$versions" | wc -l) if [ "$nversions" -gt 1 ]; then # need to sort etc # select only the ones which seems to be semantic and/or master/latest # this one would print both version tag and the one with stripped leading vV # | sed -n -r 's/^(v*([0-9].*[0-9]\..*))/\1 \2/gp' # This one would also allow for master and latest. # \2 works in one case, \4 in the other ;) # | sed -n -r 's/^([Vv]?(([0-9]{1,10}\..*))|((master|latest)$))/\1 \2\4/gp' # sort -V is not disregarding v prefix, so we do store pure version and original tag in the same line # Some release alpha releases, so probably would skip thos for now good_versions=$(echo "$versions" \ | grep -v -e '[ab][0-9][0-9]*$' -e 'rc[0-9]*$' \ | sed -n -r 's/^(([Vv]|version-|)([0-9]{1,10}\..*)|(master)$)/\3@\1/gp') last_version=$(echo -e "$good_versions" |sort -Vr |head -n 1) elif [ -z "$versions" ]; then info " $dh no version. Tags: $versions" return else last_version="$versions" fi echo "$last_version" } function get_familyname() { repoid="$1" family="$2" name=${repoid#*/} # sanitize for datalad not allowing _ in the container names name=${name//_/-} echo "$family-$name" } function get_imagename() { repoid="$1" family="$2" versiontag="$3" familyname=$(get_familyname "$repoid" "$family") echo "${familyname}--${versiontag}" } function create_singularity_file() { dh="$1" version_tag="$2" version_pure="$3" family="$4" imagename=$(get_imagename "$dh" "$family" "$version_pure") outdir="$IMAGES_DIR/$family" mkdir -p "$outdir" singfile="$outdir/Singularity.$imagename" # info "$singfile \t from \t $dh:$version_tag" # Do not recreate the file ATM. Since we changed the env vars # we define, we don't want to recreate it if already present if [ ! -e "$singfile" ]; then cat >| "$singfile" <&2 datalad publish 1>&2 fi echo "$singfile" } function add_singularity_versioned_image() { repoid="$1" family="$2" version_pure="$3" # If image repo id is different (e.g. ReproNim/containers) from original # repoid where we are getting image for (e.g. generating Singularity containers # from Docker images) -- specify it as 4th arg if [ "$#" -gt 3 ]; then origrepoid="$4" else origrepoid="$repoid" fi imagedir="$IMAGES_DIR/$family" familyname=$(get_familyname "$origrepoid" "$family") imagename=$(get_imagename "$origrepoid" "$family" "$version_pure") # Figure out full shub URL, which would depend either we build the image or # it was some original one not from our repo if [ "$origrepoid" != "$repoid" ]; then # So we are hosting the image and thus our tags correspond to imagename version_tag="$imagename" else version_tag="$version_pure" fi imagefilename="$imagename.sing" imagefile="$imagedir/$imagefilename" singfile="$imagedir/Singularity.$imagename" # name=${imagefilename%%--*} if [ ! -e "$imagefile" ] && [ ! -L "$imagefile" ]; then # eventually can use docker builds, e.g. # docker run -it -v $PWD:$PWD -w $PWD --privileged quay.io/singularity/singularity:v3.5.1 build ./busybox.sing-3.5.1 ./Singularity.busybox # docker run -it -v $PWD:$PWD -w $PWD --entrypoint chown quay.io/singularity/singularity:v2.6 -- $UID.$GID ./busybox.sing # unfortunately 2.6 version tries to connect to docker service and fails, thus can't fetch anything sudo --preserve-env=TMPDIR,SINGULARITY_TMPDIR singularity build "$imagefile.tmp" "$singfile" # abuse BTRFS CoW to avoid sudo/docker to chown cp --reflink=auto "$imagefile.tmp" "$imagefile" && rm -f "$imagefile.tmp" datalad save -m "Adding built image for $imagename" "$imagefile" datalad containers-add "$familyname" \ -i "$imagefile" \ --update \ --call-fmt '{img_dspath}/scripts/singularity_cmd run {img} {cmd}' datalad publish # so we share with the world # TODO: later make it work with updating existing one. We will not be able to use # containers-add --update since original URL is version specific. That is why it # also does not make much sense to create a image file without version. We better # collect all versions available, so in case an execution of previous version is # needed it could still be done "semi-manually". May be just via adding # -c datalad.containers.NAME.image variable pointing to the previous version of the # container fi } function generate_singularity_for_docker_image() { githubid="$1" dockerhubid="$(echo "$githubid" | tr '[:upper:]' '[:lower:]')" family="$2" check_if_of_interest "$githubid" || return 0 info "$family <- docker $dockerhubid" last_version=$(get_last_docker_version_tag "$dockerhubid") if [ -n "$last_version" ]; then last_version_pure=${last_version%%@*} last_version_tag=${last_version#*@} # echo $dockerhubid $last_version singfile=$(create_singularity_file "$dockerhubid" "$last_version_tag" "$last_version_pure" "$family") # version_tag="$(basename ${singfile//Singularity./})" # Tag on singularity-hub add_singularity_versioned_image "ReproNim/containers" "$family" "$last_version_pure" "$dockerhubid" fi } # # We are still using elderly singularity 2.6.1 to build images, so elderly singularity # could run images we generate, since images produced by 3 seems to not be usable with 2. # if ! singularity --version | grep -q 2.6.1; then echo "ERROR: singularity is 'too new'" exit 1 fi curl -s -k https://raw.githubusercontent.com/BIDS-Apps/bids-apps.github.io/master/_config.yml \ | awk '/^ *dh:/{print $2}' \ | sed -e "s,',,g" \ | while read -r dockerhubid; do generate_singularity_for_docker_image "$dockerhubid" "bids" done # validator is not bids-app but we will stick it along with bids generate_singularity_for_docker_image "bids/validator" "bids" # # Additional ones from Poldrack generate_singularity_for_docker_image "poldracklab/ds003-example" "poldracklab" # # ReproNim # # "Native" Singularity image for ReproIn # TODO: figure out who would build those now that shub is gone?! add_singularity_versioned_image "ReproNim/reproin" "repronim" "0.9.0" # Docker image for simple_workflow generate_singularity_for_docker_image "ReproNim/simple_workflow" "repronim" # One time run! specify nonesense to not match anything else # add_singularity_versioned_image "ReproNim/containers" "repronim" "repronim-ptb-3--3.0.15.20190401.dfsg1-1+nd100" # TODO: it resulted in repronim-containers--repronim-ptb-3--3.0.15.20190401.dfsg1-1+nd100.sing # image filename since used that prefix... renaming manually for now # neuronets generate_singularity_for_docker_image "neuronets/kwyk" "neuronets"