#!/bin/bash without loosing your sleep

Post on 25-Jun-2015

608 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

Operators are used to scripting, but learning bash can be confusing for developers. It starts out by putting a few commands in sequence and running it. Immediately you experience the power of the shell, and you want to do more. That's when you may experience that your scripts become a nightmare, and I am sure operators have experienced that to. When developers get more experienced in scripting they miss the set of tools and techniques they are used to from the developers world, and they start finding ways to incorporate them in bash scripting. And when they do, their scripts become a lot more manageable and less error-prone. Things like functions, unit tests, organising of code files, version control and logging frameworks enhances the quality, maintainability, readability, reusability, cleanliness, changeability, testability, security and ease of use. In this talk I will explain problems you may encounter when scripting bash and how to cope with them. I will show how techniques and tools from the programming world may be interesting to people coming from the operators world, and what developers need to learn from the operators world to become effective bash programmers.

TRANSCRIPT

#!/bin/bash without loosing your sleep

#!/bin/bash# Usage: deploy.sh <artifact> <version>

artifact=$1version=$2

wget https://nexus.bekk.no/${artifact}/${version}/${artifact}-${version}.zip

unzip ${artifact}.zip

/etc/init.d/${artifact} stop

rm ${artifact} # softlink

ln -s ${artifact}-${version} ${artifact}

/etc/init.d/${artifact} start

#!/bin/bash# Usage: rollback.sh <artifact> <version>

artifact=$1version=$2

/etc/init.d/${artifact} stop

rm ${artifact} # softlink

ln -s ${artifact}-${version} ${artifact}

/etc/init.d/${artifact} start

includes=( "../include/common_functions.sh" "../include/common_config.sh" )

for include in ${includes[@]}do if [ -f ${include} ]; then . ${include} else _fatal "File ${include} not found. Quitting! :-(" fidone

_is_snapshot() { [[ "${1}" =~ ^[0-9]+(\.[0-9]+)+-SNAPSHOT$ ]] && return 0 || return 1}

_run() { local cmd=${1} _info "Running: ${cmd}" if [ $? -ne 0 ]; then _fatal "${cmd} failed! Please retry the command manually." fi}

_mwget() { local wget="wget -nd -nH -r -l1 --no-parent -A \"${1}\" ${2}" _info "Running: ${wget}" eval $wget return $?}

_ms() { local S=${1} ((m=S%3600/60)) ((s=S%60)) printf "%dm:%ds" $m $s}

expected=10m:15s

val=$(_ms 615) && retval=$? || retval=$? && [[ "${val}" == "${expected}" ]] \&& _info "test passed! (retval=${retval})" \|| _fatal "test failed! Expected \"${expected}\" but was \"$val\" (retval=${retval})"exit 0

_assertEquals() { local arguments=( "$@" ) local expected="${1}" local actual="${2}" local msg="${arguments[@]:2}" [[ "${expected}" == "${actual}" ]] \ && _info "test passed! (value=${actual}) ${msg}" \ || _fatal "test failed! Expected value ${expected} but was ${actual} ${msg}" [[ $1 =~ "^[0-9]+$" ]] && return ${actual} || return 0}

HOSTNAME="node1"expected="8080"actual=$( _getWebServerPort )_assertEquals 0 $?_assertEquals ${expected} ${actual} "Test: _getWebServerPort when server is ${HOSTNAME}"

HOSTNAME="no_such_server"expected="UNDEFINED"actual=$( _getWebServerPort )_assertEquals 1 $?_assertEquals ${expected} ${actual} "Test: _getWebServerPort when server is nonexistent"

# Syntax check echo "Running syntax check ..."scripts=$(ls -1 *.sh ../include/*.sh ../scripts/*.sh)

for script in ${scripts[@]}do echo "checking ${script}" bash -n ${script} || exit 1doneecho "Syntax check finished ..."

# Testsecho "Running tests ..."tests=$(ls -1 ../tests/test*.sh )

for test in ${tests[@]}do echo "running ${test}" ${test} || exit 1doneecho "Tests finished ..."exit 0

...13 if [ "${version}" != "rollback" ] && [ ${#artifactsAndVersions[@]} -ne 0 ]; then14 if ( _contains ${targets[@]} ${host} ) || ( _is_snapshot ${deployVersion} ) ; 15 then16 deployFile="${M2_REPO}/no/posten/dpost/deploy/${deployVersion}/deploy-${deployVersion}.zip"17 if [ ! -s "${deployFile}" ]; then18 echo "mkdir -p ${M2_REPO}/no/posten/dpost/deploy/${deployVersion}/"19 if ( _is_snapshot ${deployVersion} ); then20 if [ -s ~/deploy-${deployVersion}.zip ]; then21 _debug "Found deploy-${deployVersion}.zip in homedir. Moving it to ${deployFile}."22 _run "echo ~/deploy-${deployVersion}.zip ${deployFile}"23 if [ ! -s ~/deploy-${deployVersion}.zip ]; then24 _fatal "You are trying to deploy a SNAPSHOT version, but it is not installed"25 fi26 else27 _fatal "Could not find deploy-${deployVersion}.zip at ${deployFile}."28 fi29 [[ -e ${deployFile} ]] || _fatal "${deployFile} not found locally or in Nexus! Exiting!"30 fi31 artifactFilesToUpload+=( "${deployFile}" )32 else33 installDeployCmds=( "wget -O deploy-${deployVersion}.zip ${NEXUS_URL}deploy-${deployVersion}.zip" )34 fi35 fi

/Users/stein/deploy.sh: line 35: syntax error: unexpected end of file

debug="true"

_run_ssh() { local -a servers=( "${!1}" ) local cmd=${2} for server in ${servers[@]} do local remote_cmd="ssh -tt ${server} \"${cmd}\"" _info "Running: ${remote_cmd}" test "${debug}" == "true" && debug_cmds+=( "${remote_cmd}" ) || eval ${remote_cmd} if [ $? -ne 0 ]; then _fatal "${remote_cmd} failed! Please retry the command(s) on the remote server(s)." fi done test "${debug}" == "true" && echo "${debug_cmds[@]}"}

_info() { echo -e 1>&2 "\033[32m-->" $@ "\033[0m" # green}

_debug() { test "${debug}" == "true" && echo -e 1>&2 "\033[34m-->" $@ "\033[0m" # purple}

_error() { echo -e 1>&2 "\033[31m-->" $@ "\033[0m" # red}

_fatal() { echo -e 1>&2 "\033[31m-->" $@ "\033[0m" # red exit 2}

_happyQuit() { echo -e 1>&2 "\033[36m-->" $@ "\033[0m" # blue exit 0}

_init_log() { local timestamp=`date +%Y%m%d` logs="./logs" log_filename="${1}" if [ ! -d ${logs} ]; then mkdir $logs fi find $logs -name ${log_filename} -type f -size +512k | while read logfile do echo $logfile local newlogfile=$logfile.$timestamp cp $logfile $newlogfile cat /dev/null > $logfile gzip -f -9 $newlogfile done find $logs -name "${log_filename}*.gz" -type f -mtime 30 |xargs rm -f}

_run_with_rollback_if_fail() { _info "Running ${1} ..." ${1} response=$? if [ $response -ne 0 ]; then _rollback "${previous_version_directory}" "${home}/${artifact}-${version}" fi}

_rollback() { local rollback_to="${1}" local rollback_from="${2}" cd ${home} _info "Rolling back from ${rollback_from} to ${rollback_to}" if [ -d "${rollback_to}" ]; then _stop if [ -h "${artifact}" ]; then _delete ${artifact} fi if [ -d "${rollback_from}" ]; then _delete ${rollback_from} fi mv ${rollback_to} ${home} ln -s ${artifact}-${version} ${artifact} if ( _start ); then _info "Rollback to ${rollback_to} successful." else _fatal "Could not start ${artifact}. Rollback to ${rollback_to} failed!" fi else _error "File ${rollback_to} not found. Rollback failed!" if [ -d "${rollback_from}" ] && [ -h "${artifact}" ]; then _start else _fatal "No executable versions of ${artifact} exists!" fi fi}

THANK YOU!

Stein Inge Morisbak

Practice lead Continuous Delivery and DevOps @ BEKK

top related