#!/usr/bin/env bash # ====================================================================== # # API CLIENT :: GET A CI FILE FROM PACKAGE SERVER # # Source: https://git-repo.iml.unibe.ch/iml-open-source/imldeployment-client/ # ---------------------------------------------------------------------- # 2021-03-31 v1.0 <axel.hahn@iml.unibe.ch> init # 2021-04-13 v1.1 <axel.hahn@iml.unibe.ch> add support for custom config # 2021-04-15 v1.2 <axel.hahn@iml.unibe.ch> added debugging of curl request # 2021-10-14 v1.3 <axel.hahn@iml.unibe.ch> add nanoseconds in hashed base data # 2023-02-14 v1.4 <axel.hahn@unibe.ch> compatibility to openssl v3 # 2023-09-15 v1.5 <axel.hahn@unibe.ch> remove temp file after download of multiple files # ====================================================================== # ---------------------------------------------------------------------- # CONFIG # ---------------------------------------------------------------------- version="v1.5" about="CI PACKAGE GETTER $version; (c) 2021 Institute for Medical Education (IML); University of Bern; GNU GPL 3.0" line="----------------------------------------------------------------------" bDebug=0 customconfig= . $0.cfg # ---------------------------------------------------------------------- # FUNCTIONS # ---------------------------------------------------------------------- function showhelp(){ self=$( basename $0 ) echo "$line $about $line Get packages from a software sattelite of IML ci server. SYNTAX: $self [OPTIONS] OPTIONS: -h Show this help -v Show version -c CFGFILE load custom config file after defaults in $self.cfg -d enable debug infos -e PHASE phase; overrides env variable IMLCI_PHASE -f FILE filename to get (without path); overrides env variable IMLCI_FILE -l ITEM list -o OUTFILE optional output file -p PROJECT ci project id; overrides env variable IMLCI_PROJECT -s SECRET override secret in IMLCI_PKG_SECRET -u URL URL of iml ci server without trailing /; overrides env variable IMLCI_URL VALUES: CFGFILE custom config file. It is useful to handle files of different projects on a server. PHASE is a phase of the ci server; one of preview|stage|live FILE is a filename without path that was created by ci server. OUTFILE Output file. It can countain a path. If none is given the filename will be taken from FILE and stored in current directory PROJECT project id of the ci server SECRET secret to access project data on package server. Your given secret must match the secret on package server to get access to any url. ITEM type what to list; one of phases|projects|files To list projects a phase must be set. To list files a phase and a project must be set. DEFAULTS: You don't need to set all values by command line. Use a config to set defaults $0.cfg EXAMPLES: If url, secret, project and phase are set in the config you can operate by setting the filename to request. $self -f FILE downloads FILE to the current dir. $self -f FILE -o my-own-filename.tgz downloads FILE as my-own-filename.tgz $self -f ALL there is a special file ALL; it fetches all filenames by executing a directory listing and then downloads all remote files with their original name $self -e preview -l projects list existing projects in phase preview $self -l files list existing files of current project Remark: The directory listing can be turned off on the package server and results in a 403 status. " } # make an http request to fetch the software # # param string method; should be GET # param string request url (without protocol and server) # param string optional: filename for output data # param string optional: secret; default: it will be generated # # global int bDebug (0|1) # global string line string for a line with dashes function makeRequest(){ local apiMethod=$1 local apiRequest=$2 local outfile=$3 local secret=$4 # local outfile=$( mktemp ) if [ $bDebug = 1 ]; then echo $line echo "$apiMethod ${apiHost}${apiRequest}" echo $line fi if [ ! -z "$secret" ]; then # --- date in http format LANG=en_EN # export TZ=GMT apiTS=$(date "+%a, %d %b %Y %H:%M:%S.%N %Z") # --- generate data to hash: method + uri + timestamp; delimited with line break data="${apiMethod} ${apiRequest} ${apiTS} " # these ase non critical data ... it does not show the ${secret} if [ "$bDebug" = "1" ]; then echo "RAW data for hashed secret:" echo "$data" fi # generate hash - split in 2 commands (piping "cut" sends additional line break) myHash=$(echo -n "$data" | openssl dgst -sha1 -hex -hmac "${secret}" | cut -f 2 -d " ") myHash=$(echo -n "$myHash" | base64) moreheaders="--fail" test $bDebug = 1 && moreheaders="-i" tmpdownloadfile="${outfile}.downloading" curl \ -H "Accept: application/json" -H "Content-Type: application/json" \ -H "Date: ${apiTS}" \ -H "Authorization: bash-client:${myHash}" \ -X $apiMethod \ -o "${tmpdownloadfile}" \ $moreheaders \ -s \ ${IMLCI_URL}${apiRequest} rc=$? if [ "$bDebug" = "1" ]; then cat "${tmpdownloadfile}" rm -f "${tmpdownloadfile}" exit 0 fi if [ $rc -eq 0 ]; then # echo OK. # no outfile (= request to a directory) if [ -z "$outfile" ]; then # echo # echo ----- RESPONSE BODY: cat "${tmpdownloadfile}" rm -f "${tmpdownloadfile}" else mv "${tmpdownloadfile}" "${outfile}" ls -l "${outfile}" fi else echo ERROR: Download failed. exit 1 fi else curl\ -H "Accept: application/json" -H "Content-Type: application/json" \ -X $apiMethod \ -o "${tmpdownloadfile}" \ ${IMLCI_URL}${apiRequest} fi } # ---------------------------------------------------------------------- # MAIN # ---------------------------------------------------------------------- if [ $# -lt 1 ]; then showhelp exit 1 fi while getopts "c:de:f:hl:o:p:s:u:v" option; do case ${option} in c) customconfig="$OPTARG" ;; d) bDebug=1 ;; e) export IMLCI_PHASE=$OPTARG ;; f) export IMLCI_FILE=$OPTARG ;; h) showhelp exit 0 ;; l) case $OPTARG in phases) IMLCI_PHASE='' IMLCI_PROJECT='' IMLCI_FILE='' ;; projects) IMLCI_PROJECT='' IMLCI_FILE='' ;; files) IMLCI_FILE='' ;; *) echo ERROR: invalid value for option [-l] echo showhelp exit 2 esac ;; o) export IMLCI_OUTFILE=$OPTARG ;; p) export IMLCI_PROJECT=$OPTARG ;; s) export IMLCI_PKG_SECRET=$OPTARG ;; u) export IMLCI_URL=$OPTARG ;; v) echo $about; exit 0 ;; *) echo ERROR: invalid option [${option}] echo showhelp exit 2 esac done if [ ! -z "$customconfig" ]; then if [ -r "$customconfig" ]; then . "$customconfig" || exit 2 else echo "ERROR: unable to read custom config [$customconfig]." exit 2 fi fi test -z ${IMLCI_OUTFILE} && IMLCI_OUTFILE=$IMLCI_FILE if [ $bDebug = 1 ]; then pre=">>>>>> " echo $line echo echo DEBUG INFOS echo echo "${pre} defaults in $0.cfg" cat $0.cfg 2>/dev/null echo if [ ! -z "$customconfig" ]; then echo "${pre} custom config $customconfig" cat "$customconfig" echo fi echo "${pre} Params (override default values)" echo $* echo echo "${pre} effective values" echo "IMLCI_URL = $IMLCI_URL" echo "IMLCI_PKG_SECRET = $IMLCI_PKG_SECRET" echo "IMLCI_PROJECT = $IMLCI_PROJECT" echo "IMLCI_PHASE = $IMLCI_PHASE" echo "IMLCI_FILE = $IMLCI_FILE" echo "IMLCI_OUTFILE = $IMLCI_OUTFILE" echo fi if [ "$IMLCI_FILE" = "ALL" ]; then # echo ALL files were requested ... printf "%-30s" "get list of all files... " tmpfilelist=$( mktemp ) $0 -u "${IMLCI_URL}" \ -p "${IMLCI_PROJECT}" \ -e "${IMLCI_PHASE}" \ -s "${IMLCI_PKG_SECRET}" \ -l files \ -o "${tmpfilelist}" # cat "${tmpfilelist}" cat "${tmpfilelist}" | grep "^file:" | while read fileline do # echo $line myfile=$( echo $fileline | cut -f 2- -d ':' ) printf "%-30s" "GET $myfile... " $0 -u "${IMLCI_URL}" \ -p "${IMLCI_PROJECT}" \ -e "${IMLCI_PHASE}" \ -s "${IMLCI_PKG_SECRET}" \ -f "${myfile}" done rm -f "${tmpfilelist}" else makeRequest GET "/packages/$IMLCI_PHASE/$IMLCI_PROJECT/$IMLCI_FILE" "$IMLCI_OUTFILE" "$IMLCI_PKG_SECRET" fi