#!/usr/bin/env bash # # Author: Georg Voell - georg.voell@standby.cloud # Version: @(#)store-object 3.2.1 21.08.2025 (c)2025 Standby.cloud # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ # # This script can be used free of charge. Use it as is or customize as needed. It is not guaranteed to be # error free and you will not be reimbursed for any damage it may cause. # #@ Export an OS image, a local file or all files from given local folder to ObjectStorage. #@ #@Usage: store-object [options] [object] #@ Options: -h, --help : Displays helptext. #@ -v, --version: Displays the version of the script. #@ -q, --quiet : Don't display any error messages. #@ -f, --force : Force overwriting. #@ -p, --prefix : Prefix used for ObjectStorage. #@ -u, --url : Specify an URL to write to ObjectStorage. #@ -i, --image : Store an OS image in ObjectStorage. #@ Object: : Can be a file, folder or image e.g. "Bastion.oci" or "Base.oci". #@ If not specified, list capabilities of URL. #@ #@Examples: #@ store-object ~/.ssh #@ Zips all the content under ~/.ssh and exports it to Object Storage # # Exit codes: # 01: Unknown parameter or wrong URL. # 02: Not a valid image. # 03: Not valid file or folder. # 04: No curl or zip in PATH. # 05: Error while zipping. # 06: Unable to upload file to ObjectStorage. # 07: RCLONE not usable. # 99: User interrupt. # # See also: # **install-scripts**(1) # # ToDo: # # - More objects. # # Update history: # # V 1.0.0 22.03.2021 First version # V 1.0.1 23.03.2021 Bugfix for Write- and Read-URI is empty # V 3.0.0 15.11.2021 Export any directory # V 3.1.0 05.06.2023 New copyright # V 3.2.0 13.09.2024 New minor version # V 3.2.1 21.08.2025 Import also OS images and script renamed to store-object # # Find executable bash library and source it lib=`which lib.bash 2>/dev/null | sed 's|^no 'lib.bash' in .*||'` if [ "$lib" != "" ]; then source "$lib" else progdir=`dirname "$0"` if [ -r "${progdir}/lib.bash" ]; then source "${progdir}/lib.bash" else echo "Unexpected error: Unable to locate bash library 'lib.bash'." exit 1 fi fi # Needed to download OCI OS images readonly imageurl="https://objectstorage.eu-frankfurt-1.oraclecloud.com/p/dAxQHw_fY2bz0agilOPilkLYji2Z2jMrxqrgxg01HcNGKPo_xzkh7exokWXjJV4q/n/frelkczkpnqg/b/Images/o" # Project information in JSON format readonly projectjson="$REALHOME/.oci/project.json" # Do extra cleanup function ExtraCleanup() { filecheck -rm ${scratchfile}.obj } # Check if first parameter is not empty, begins with 'https://objectstorage.', basename is "o" and remove trailing / function CheckURL() { local url=`echo "$1" | sed 's|/$||'` local newurl="" local stat=1 if [ "$url" != "" ]; then newurl=`echo "$url" | grep '^https://objectstorage\..*\.oraclecloud.com/'` if [ "$newurl" != "" ]; then newurl=`basename "$url"` if [ "$newurl" = "o" ]; then newurl=`dirname "$url"` if [ "$newurl" != "" ]; then echo "${newurl}/o" stat=0 fi fi fi fi return $stat } # Returm endpoint function GetEndpointFromURL() { local url=${1} local retstr="" if [ "$url" != "" ]; then retstr=`echo "$url" | sed 's|^https://||' | cut -d'/' -f1` echo "$retstr" fi } # Returm location function GetLocationFromURL() { local url=${1} local retstr="" if [ "$url" != "" ]; then retstr=`echo "$url" | sed 's|^https://||' | cut -d'/' -f2-` echo "$retstr" fi } # Returm region name function GetRegionFromEndpoint() { local ep=${1} local retstr="" if [ "$ep" != "" ]; then retstr=`echo "$ep" | cut -d'.' -f2 | tolower` if [ "$retstr" != "" ]; then retstr=`echo $retstr | grep '^..-.*-.'` echo "$retstr" fi fi } # Returm preauth function GetPreauthFromLocation() { local loc=${1} local retstr="" if [ "$loc" != "" ]; then retstr=`echo "$loc" | grep '^p/'` if [ "$retstr" != "" ]; then retstr=`echo "$loc" | cut -d'/' -f2` echo "$retstr" fi fi } # Returm preauth function GetNamespaceFromLocation() { local loc=${1} local retstr="" if [ "$loc" != "" ]; then retstr=`echo "$loc" | grep '^p/'` if [ "$retstr" != "" ]; then retstr=`echo "$loc" | cut -d'/' -f3-` else retstr="$loc" fi if [ "$retstr" != "" ]; then retstr=`echo "$retstr" | grep '^n/'` if [ "$retstr" != "" ]; then retstr=`echo "$retstr" | cut -d'/' -f2` echo "$retstr" fi fi fi } # Returm bocketname function GetBucketFromLocation() { local loc=${1} local retstr="" # Example: /n/frelkczkpnqg/b/Images/o if [ "$loc" != "" ]; then loc=`dirname "$loc"` if [ "$loc" != "" ]; then retstr=`dirname "$loc"` if [ "$retstr" != "" ]; then retstr=`basename "$retstr"` if [ "$retstr" = "b" ]; then retstr=`basename "$loc"` echo "$retstr" fi fi fi fi } # Check if we got an error from objectstorage function CheckError() { local grepres="" local code="" if [ -r $scratchfile ]; then grepres=`grep '^{"code":"' $scratchfile` if [ "$grepres" != "" ]; then code=`echo "$grepres" | cut -d'"' -f4` echo "$code" fi fi } # Read all objects from url function GetObjects() { local url=${1} local code="" local stat=1 if [ "$url" != "" -a "$curl" != "" ]; then $curl -s "${url}/" -o $scratchfile stat=$? if [ $stat -eq 0 ]; then # Check for error code=`CheckError` if [ "$code" != "" ]; then echo "$code" > ${scratchfile}.obj stat=2 else if [ "$jq" = "" ]; then echo "NoJQ" > ${scratchfile}.obj stat=3 else cat $scratchfile | $jq -r ".objects[].name" > ${scratchfile}.obj stat=$? if [ $stat -ne 0 ]; then rm -f ${scratchfile}.obj fi fi fi fi fi return $stat } # Check if the given object is existing in storage function CheckObjects() { local url=${1} local object=${2} local name="" local stat=0 GetObjects "$url" stat=$? if [ $stat -eq 0 ]; then name=`grep "^${object}$" ${scratchfile}.obj` echo "$name" fi # Cleanup filecheck -rm $scratchfile filecheck -rm ${scratchfile}.obj } function CheckToOverwrite() { local object=${1} local name="" local stat=0 if [ "$readuri" != "" -a "$object" != "" ]; then name=`CheckObjects "$readuri" "$object"` if [ "$name" = "$object" ]; then # Object exists on ObjectStorage - ask for overwrite confirm "Object '$object' is already uploaded. Overwrite it?" stat=$? fi fi return $stat } # Preset param="" prefix="" specuri="" writeuri="" readuri="" bucketname="" regionname="" endpoint="" location="" preauth="" filename="" quietstr="" force=false storeimage=false havefolder=false # Check parameters: Loop until all parameters are used up while [ $# -gt 0 ]; do pname=${1} case "$pname" in -v | --version) shift showversion=true ;; -h | --help) shift showhelp=true ;; -f | --force) shift force=true ;; -q | --quiet) shift quietstr="$pname" ;; -i | --image) shift storeimage=true ;; -p | --prefix) shift if [ "$1" != "" ]; then if [ "$prefix" = "" ]; then # Detete leading and trailing / prefix=`echo "$1" | tr -s '/' | sed 's|^/||' | sed 's|/$||'` else errstr="Option '$pname' used more then once." fi shift else errstr="Please specify a prefix after parameter '$pname'." fi ;; -u | --url) shift if [ "$1" != "" ]; then if [ "$specuri" = "" ]; then specuri="$1" else errstr="Option '$pname' used more then once." fi shift else errstr="Please specify an URL after parameter '$pname'." fi ;; *) shift paramck=`echo "$pname" | grep '^-'` # Keys don't begin with '-' if [ "$paramck" != "" ]; then errstr="Unknown option '$pname'." else if [ "$param" = "" ]; then param="$pname" else errstr="Object was already specified '$param'. Unknown additional parameter: '$pname'." fi fi esac done # Check if oci cli is configured and we have a project definition if URL was not specified if [ "$specuri" = "" -a -r "$projectjson" ]; then writeuri=`browse-json project/writeuri --quiet --raw --select 1 --import "$projectjson"` readuri=`browse-json project/readuri --quiet --raw --select 1 --import "$projectjson"` fi # Plausibility checks if [ "$specuri" = "" -a "$writeuri" = "" ]; then errstr="No URL specified to write to ObjectStorage or defined in '$projectjson'." else if [ "$specuri" != "" ]; then writeuri=`CheckURL "$specuri"` else writeuri=`CheckURL "$writeuri"` readuri=`CheckURL "$readuri"` fi if [ "$writeuri" != "" ]; then endpoint=`GetEndpointFromURL "$writeuri"` regionname=`GetRegionFromEndpoint "$endpoint"` location=`GetLocationFromURL "$writeuri"` preauth=`GetPreauthFromLocation "$location"` namespace=`GetNamespaceFromLocation "$location"` bucketname=`GetBucketFromLocation "$location"` fi if [ "$bucketname" = "" -o "$regionname" = "" ]; then errstr="Invalid URL specified to write to ObjectStorage." fi fi # Display help or error message DisplayHelp # Main # Check if we have needed tools installed rclone=`filecheck -x rclone` curl=`filecheck -x curl` zip=`filecheck -x zip` jq=`filecheck -x jq` # Check if rclone is configured for region if [ "$rclone" != "" ]; then rcloneregion=`$rclone -q listremotes | grep "^${regionname}:$"` else rcloneregion="" fi if [ "$param" = "" ]; then # No parameter specified - list capabilities of URL commentvers=`head -n 5 "$script" | grep '^# Version: @(' | cut -d')' -f2-` namestr=`echo "$commentvers" | cut -d' ' -f1` # Name from comment cversion=`echo "$commentvers" | cut -d' ' -f2` # Version from comment cright=`echo "$commentvers" | cut -d' ' -f4` # Copyright cowner=`echo "$commentvers" | cut -d' ' -f5-` # Copyright owner printf "\n%s %s, %s %s\n\n" "$namestr" "$cversion" "$cright" "$cowner" if [ "$readuri" != "" ]; then echo " readuri: $readuri" fi echo " writeuri: $writeuri" echo " endpoint: $endpoint" echo " regionname: $regionname" echo " namespace: $namespace" echo " bucketname: $bucketname" if [ "$preauth" != "" ]; then echo " usingpreauth: true" else echo " usingpreauth: false" fi GetObjects "$writeuri" stat=$? if [ $stat -eq 0 ]; then echo "objectlisting: true" else code=`cat ${scratchfile}.obj` if [ "$code" = "BucketNotFound" ]; then echo "objectlisting: false" fi fi if [ "$rcloneregion" != "" ]; then commentvers=`basename $scratchfile` $rclone -q copyto ${scratchfile}.obj "${rcloneregion}/${bucketname}/$commentvers" stat=$? if [ $stat -eq 0 ]; then echo "rcloneenabled: true" $curl -s "${writeuri}/$commentvers" -o $scratchfile stat=$? if [ $stat -eq 0 ]; then code=`CheckError` if [ "$code" != "" ]; then echo " readable: false" else echo " readable: true" fi else echo " readable: false" fi $curl -sT $scratchfile "${writeuri}/$commentvers" >$scratchfile 2>&1 stat=$? if [ $stat -eq 0 ]; then code=`CheckError` if [ "$code" != "" ]; then echo " writable: false" else echo " writable: true" fi else echo " writable: false" fi $rclone -q deletefile "${rcloneregion}/${bucketname}/$commentvers" stat=$? fi else echo "rcloneenabled: false" echo " readable: unknown" echo " writable: unknown" fi else # Define filename if [ "$storeimage" = "true" ]; then # We want to store an image filename=`CheckObjects "$imageurl" "$param"` stat=$? if [ "$param" != "$filename" ]; then exitcode=2 errormsg $quietstr $exitcode "Specified object '$param' is not a valid image." else if [ "$prefix" = "" ]; then filename="$param" else filename="${prefix}/$param" fi fi else if [ -d "$param" ]; then # Object is a folder havefolder=true parentdir=`dirname "$param"` basedir=`basename "$param"` if [ "$prefix" = "" ]; then filename="${basedir}.zip" else filename="${prefix}/${basedir}.zip" fi else # Assuming object is a file if [ -f "$param" ]; then basedir=`basename "$param"` if [ "$prefix" = "" ]; then filename="$basedir" else filename="${prefix}/$basedir" fi else exitcode=3 errormsg $quietstr $exitcode "Specified object '$param' is not valid file or folder." fi fi fi fi # Check if we want to copy an OS image or if we have a valid file or folder if [ "$filename" != "" -a $exitcode -eq 0 ]; then if [ "$storeimage" = "true" ]; then if [ "$rclone" = "" -o "$rcloneregion" = "" ]; then exitcode=7 if [ "$rcloneregion" = "" ]; then errormsg $quietstr $exitcode "Remote '${regionname}:' is not defined in 'rclone'." else errormsg $quietstr $exitcode "Tool 'rclone' not found in PATH '$PATH'" fi else if [ "$force" = "true" ]; then stat=0 else CheckToOverwrite "$filename" stat=$? fi if [ $stat -eq 0 ]; then $rclone -q copyurl "${imageurl}/$param" "${rcloneregion}/${bucketname}/$filename" stat=$? if [ $stat -eq 0 ]; then if [ "$quietstr" = "" ]; then echo "Image '$param' was uploaded to ObjectStorage region '$regionname' and bucket '$bucketname'." fi else exitcode=6 errormsg $quietstr $exitcode "Unable to upload file to ObjectStorage" fi fi fi else if [ "$curl" != "" -a "$zip" != "" ]; then if [ "$force" = "true" ]; then stat=0 else CheckToOverwrite "$filename" stat=$? fi if [ $stat -eq 0 ]; then if [ "$havefolder" = "true" ]; then cd "$parentdir" $zip -r -q -y $scratchfile "$basedir" -x \*.DS_Store stat=$? if [ $stat -eq 0 ]; then $curl -sT $scratchfile "${writeuri}/$filename" >$scratchfile 2>&1 stat=$? if [ $stat -eq 0 ]; then code=`CheckError` if [ "$code" != "" ]; then exitcode=6 errormsg $quietstr $exitcode "Unable to upload folder to ObjectStorage" else if [ "$quietstr" = "" ]; then if [ "$readuri" = "" ]; then echo "Folder '${basedir}.zip' uploaded." else echo "Folder zipped and uploaded. To download zip, please use this command:" echo "curl -s ${readuri}/$filename -o ${basedir}.zip" fi fi fi else exitcode=6 errormsg $quietstr $exitcode "Unable to upload folder to ObjectStorage" fi else exitcode=5 errormsg $quietstr $exitcode "Unable to zip '$HOME/.ssh'" fi else # Upload file $curl -sT "$param" "${writeuri}/$filename" >$scratchfile 2>&1 stat=$? if [ $stat -eq 0 ]; then code=`CheckError` if [ "$code" != "" ]; then exitcode=6 errormsg $quietstr $exitcode "Unable to upload folder to ObjectStorage" else if [ "$quietstr" = "" ]; then if [ "$readuri" = "" ]; then echo "File '$filename' uploaded." else echo "File uploaded. To download file, please use this command:" echo "curl -s ${readuri}/$filename -o $param" fi fi fi else exitcode=6 errormsg $quietstr $exitcode "Unable to upload file to ObjectStorage" fi fi fi else exitcode=4 errormsg $quietstr $exitcode "Tool 'curl' or 'zip' not found in PATH '$PATH'" fi fi fi # Cleanup and exit Cleanup exit $exitcode