source: tags/confman-1.5.3/confmanlib.sh @ 148

Revision 148, 14.7 KB checked in by ccowart, 5 years ago (diff)

Allowing the use of keys for  scp:// fetches.

  • Property rc:mode set to 0444
  • Property svn:keywords set to Id
Line 
1#! /bin/bash
2#
3# This file provides shell libraries to our configuration management system.
4# It will be sourced by the relevant Rescomp scripts.
5#
6# Author: Chris Cowart <ccowart@rescomp.berkeley.edu>
7# Date: 30 March 2006
8#
9# $Id$
10
11REPO_URI="${REPO_PROTOCOL}${REPO_HOSTNAME}${REPO_PATH}"
12REPO_ACTION="${REPO_DB}/last_action"
13REPO_SYNC_STATE="${REPO_DB}/state"
14
15# Checks out the conf tree if we don't already have it. Updates it if we do.
16function conf_checkout_tree {
17        if [ ! -d ${WORK_PATH} ] ; then
18                        local path=`echo ${WORK_PATH} | sed -E 's:/[^/]+$::'`
19                        mkdir -p ${WORK_PATH}
20
21                        # This makes sure nobody can enter your working copy. We relax the
22                        # permissions during the rollout, and restrict them again at
23                        # the end.
24                        chmod 700 ${WORK_PATH}
25
26                        svn checkout ${REPO_URI} ${WORK_PATH}
27        else
28                        svn update ${WORK_PATH}
29        fi
30}
31
32# I update the working copy of a given module to the repository's copy.
33function conf_update_module {
34        local module="$1"
35        svn update ${WORK_PATH}/${module}
36}
37
38# Updates the whole source tree
39function conf_update_tree {
40        svn update ${WORK_PATH}
41}
42
43# Revert a working file
44function conf_revert {
45        svn revert $*
46}
47
48# Assumes that we already have core setup in our work path.
49function conf_create_module {
50        local module=$1
51        svn mkdir ${WORK_PATH}/$module
52        svn mkdir ${WORK_PATH}/${REPO_CHECKPTS}/$module
53        svn commit ${WORK_PATH}/${module} ${WORK_PATH}/${REPO_CHECKPTS}/$module -m \
54                        "Created directory structure for ${module} --`whoami`"
55}
56
57# Commits module $1 with message $2
58function conf_commit {
59        local modules=$(echo $1 | tr ' ' ',')
60        local msg="$2"
61
62    # $modules is in the form foo,bar,baz. This eval trick forces
63    # curly brace expansion.
64    if echo "$modules" | grep ',' > /dev/null ; then
65            svn commit -F "$msg" $(eval echo ${WORK_PATH}/{$modules})
66    else
67        svn commit -F "$msg" ${WORK_PATH}/$modules
68    fi
69}
70
71function conf_fetch {
72    local tmpfile=`mktemp -t confman`
73    local proto="${CONF_EXPORT_URI%:/*}"
74    local remote
75
76    case $proto in
77        sftp|scp)   
78            remote="${CONF_EXPORT_URI#*://}"
79            remote="${remote/\//:/}"
80            scp -p -i ${CONF_SSH_KEY} -o StrictHostKeyCHecking=no \
81                $remote $tmpfile 
82            ;;
83        http|https|ftp)
84            fetch -o $tmpfile $remote
85            ;;
86        file)
87            remote="${CONF_EXPORT_URI#*://}"
88            cp -p $remote $tmpfile
89            ;;
90        *)
91            echo "Unsupported Protocol in conf_fetch" >&2
92            return 1
93            ;;
94    esac
95    echo $tmpfile
96    return 0
97}
98
99# A way to utilize the svn status feature.
100function conf_status {
101        svn status $*
102}
103
104# This exports a working copy of the repository to a tarball
105function conf_export {
106    local tarball="$1"
107    conf_checkout_tree
108    tar -czf "$tarball" -C "${WORK_PATH}" .
109}
110
111# Roll out the specified module, optionally at the specified checkpoint
112# eg:   conf_rollout MODULE [checkpoint]
113function conf_rollout {
114        local module="$1"
115        if [ -z $2 ] ; then
116                local moduledir="${WORK_PATH}/$module"
117        else
118                local moduledir="${WORK_PATH}/checkpoints/${module}/${2}"
119        fi
120        # This is a hack to work around NFS home dirs, for now:
121        chmod o+rx ${WORK_PATH}
122
123        for directory in `find $moduledir -type d -mindepth 1| grep -v "\.svn"`; do
124                local livedir=`echo $directory | sed "s:$moduledir::"`
125                livedir="${LIVE_ROOT}${livedir}"
126                local owner=`conf_get_prop ${directory} owner`
127                local group=`conf_get_prop ${directory} group`
128                local mode=`conf_get_prop ${directory} mode`
129                local opts="-d -o $owner -g $group -m $mode"
130                local cmd="sudo install $opts $livedir"
131                echo $cmd
132                $cmd
133        done
134        for file in `find $moduledir -type f | grep -v "\.svn"` ; do
135                local livefile=`echo "$file" | sed "s:$moduledir::"`
136                local owner=`conf_get_prop ${file} owner`
137                local group=`conf_get_prop ${file} group`
138                local mode=`conf_get_prop ${file} mode`
139                local opts="-Sp -o $owner -g $group -m $mode"
140                local cmd="sudo install $opts $file ${LIVE_ROOT}$livefile"
141                echo $cmd
142                $cmd
143        done
144        # This is a hack to work around NFS home dirs, for now:
145        chmod o-rx ${WORK_PATH}
146}
147
148function conf_install {
149        local module="$1"
150        shift
151        local file=`abspath $1`
152        shift
153        local moduledir="${WORK_PATH}/$module"
154        local livefile=`echo "$file" | sed -E "s:${WORK_PATH}/[^/]+::"`
155        local pathmodule=`echo "$file" | sed -E "s:${WORK_PATH}/([^/]+).*:\1:"`
156        local suffix
157        livefile=`echo "$livefile" | sed -E "s:-${pathmodule}$::"`
158        if [[ $SINGULARITIES =~ $livefile ]] && [ ! -d "$file" ] ; then
159                suffix="-${module}"
160        fi
161        livefile="${livefile}${suffix}"
162
163
164        # See if it even exists
165        file="${moduledir}${livefile}"
166        if [ ! -e "$file" ] && [ -z "$1" ] ; then
167                return 0
168        elif [ ! -f "$file" ] && [ ! -d "$file" ] ; then
169                conf_install $module "$@"
170                return
171        fi
172
173        # If we got here, figure it out
174        local owner=`conf_get_prop ${file} owner`
175        local group=`conf_get_prop ${file} group`
176        local mode=`conf_get_prop ${file} mode`
177
178        if [ -f "$file" ] ; then
179                local opts="-Sp -o $owner -g $group -m $mode"
180            local cmd="sudo install $opts $file ${LIVE_ROOT}$livefile"
181        else
182                local opts="-d -o $owner -g $group -m $mode"
183            local cmd="sudo install $opts ${LIVE_ROOT}$livefile"
184        fi
185
186        local cmd="sudo install $opts $file ${LIVE_ROOT}$livefile"
187
188        chmod o+rx ${WORK_PATH}
189        echo $cmd
190        $cmd
191        chmod o-rx ${WORK_PATH}
192
193    if [ -d "$file" ] ; then
194        conf_install $module "$file"/*
195    fi
196        if [ ! -z $1 ] ; then
197                conf_install $module "$@"
198        fi
199}
200
201
202function conf_list {
203        local file=$1
204        local module=`echo ${file#$WORK_PATH} | sed -E 's:/([^/]+)/.*:\1:'`
205        local livefile=`echo ${file#$WORK_PATH} | sed -E 's:/([^/]+)/:/:'`
206        local owner=`conf_get_prop ${file} owner`
207        local group=`conf_get_prop ${file} group`
208        local mode=`conf_get_prop ${file} mode`
209        local comment=`conf_get_prop ${file} comment`
210        echo -e "$mode\t$owner\t$group\t$comment\t\t$livefile"
211}
212       
213
214
215# This function assembles the singularities:
216function conf_assemble_sing {
217        local file=$1
218        local livefile="${LIVE_ROOT}${file}"
219        local tmpfile=`mktemp -t confman` || exit 1
220        local owner group mode flag livepart msg
221        for layer in $LAYERS ; do
222                livepart="${LIVE_ROOT}${file}-${layer}"
223                myfile="${WORK_PATH}/${layer}/${file}-${layer}"
224                if [ -f $myfile -a -f $livepart ] ; then
225                                owner=`conf_get_prop ${myfile} owner`
226                                group=`conf_get_prop ${myfile} group`
227                                mode=`conf_get_prop     ${myfile} mode`
228                                comment=`conf_get_prop  ${myfile} comment`
229                        cat $livepart >> $tmpfile
230                        sudo rm $livepart
231                        flag=1
232                fi
233        done
234        if [ ! -z $flag ] ; then
235                local opts="-Sp -o $owner -g $group -m $mode"
236                local cmd="sudo install $opts $tmpfile $livefile"
237                echo $cmd
238                $cmd
239        fi
240        rm -f $tmpfile
241}
242
243# This function generates a checkpoint called NAME for the given MODULE.
244# eg: conf_new_checkpoint MODULE NAME
245function conf_new_checkpoint {
246        local module=$1
247        local checkpoint=$2
248        local chkpath="${WORK_PATH}/${REPO_CHECKPTS}/${module}/${checkpoint}"
249        local revision=`conf_revision`
250        echo $revision > $chkpath
251        svn add $chkpath
252        local msg="Created a checkpoint, ${checkpoint} for ${module} --`whoami`"
253        svn commit ${WORK_PATH}/${REPO_CHECKPTS} -m "$msg"
254}
255
256# This function will remove the checkpoint NAME from MODULE.
257# eg: conf_rm_checkpoint MODULE NAME
258function conf_rm_checkpoint {
259        local module=$1
260        local checkpoint=$2
261        local chkpath="${WORK_PATH}/${REPO_CHECKPTS}/${module}/${checkpoint}"
262        svn rm ${chkpath}
263        local msg="Removed the checkpoint ${checkpoint} from ${module} --`whoami`"
264        svn commit ${WORK_PATH}/${REPO_CHECKPTS} -m "$msg"
265}
266
267# This function will restore the state of your module's working copy to the
268# named checkpoint. eg: conf_rollback MODULE NAME
269function conf_rollback {
270        local module=$1
271        local checkpoint=$2
272        local clock=$3
273        local modpath="${WORK_PATH}/${module}"
274        local chkpath="${WORK_PATH}/${REPO_CHECKPTS}/${module}/${checkpoint}"
275        local revision
276        local date=`echo $checkpoint | sed -E 's:(....)(..)(..):\1-\2-\3:'`
277
278        # Named checkpoint
279        if [ -f "${chkpath}" ] ; then
280        revision=`cat $chkpath`
281        elif [ -z $clock ] ; then               # Time checkpoint
282        revision="{${date}}"
283        else
284        clock=`echo $clock | sed -E 's#(..)(..)#\1:\2#'`
285        revision="{${date}T${clock}}"
286        fi
287
288        svn update --revision $revision $modpath
289}
290
291# This function will print the value of the specified property to STDOUT
292function conf_get_prop {
293        local file=$1
294        local prop=$2
295        svn propget "confman:${prop}" ${file}
296}
297
298# This function will set the value of the specified property.
299function conf_set_prop {
300        local file=$1
301        local prop=$2
302        local value=$3
303        svn propset "confman:${prop}" "${value}" ${file}
304}
305
306function conf_gen_file {
307        local module=$1
308        local file=$2
309        local owner=$3
310        local group=$4
311        local mode=$5
312        local comment="$6"
313        local usefile=$7
314        local myfile="${WORK_PATH}/${module}${file}"
315        local warning="${comment} ${CONF_WARNING}"
316        local morewarn="${comment} Managed under ${module} module."
317        local revision="${comment} \$Id\$"
318        echo $file
319
320        if [ -z $usefile ] ; then
321                                        echo -e "${warning}\n${morewarn}\n${revision}\n" > $myfile
322        elif head -n 1 ${usefile} | grep '^#!' >/dev/null ; then
323                                        awk "{if (NR==2)
324                                                        print \"\\n${warning}\\n${morewarn}\\n${revision}\\n\"\$0;
325                                                                else print \$0 }" ${usefile} > $myfile
326        else
327                                        echo -e "${warning}\n${morewarn}\n${revision}\n" > $myfile
328                                        cat ${usefile} >> $myfile
329        fi
330
331        svn add $myfile
332        svn ps svn:keywords "Id" $myfile
333        conf_set_prop $myfile owner $owner
334        conf_set_prop $myfile group $group
335        conf_set_prop $myfile mode      $mode
336        conf_set_prop $myfile comment "$comment"
337}
338
339function conf_rm_file {
340        svn rm $*
341}
342
343function conf_mkdir {
344        local directory=$1
345        local owner=$2
346        local group=$3
347        local mode=$4
348        local workdir="/"
349        local directories=`echo "$directory" | sed 's:/: :g'`
350        local dir
351
352        for dir in $directories ; do
353                workdir="${workdir}/${dir}"
354                if [ ! -d ${workdir} ] ; then
355                        echo svn mkdir ${workdir}
356                        svn mkdir ${workdir}
357                        conf_set_prop $workdir owner $owner
358                        conf_set_prop $workdir group $group
359                        conf_set_prop $workdir mode $mode
360                        conf_set_prop $workdir comment "dir"
361                fi
362        done
363}
364
365function conf_rmmod {
366        local module=$1
367        svn rm ${WORK_PATH}/${module}
368        svn rm ${WORK_PATH}/${REPO_CHECKPTS}/${module}
369}
370
371function conf_rename {
372        local oldmod=$1
373        local newmod=$2
374        local file
375
376        # First, we perform the easy operations
377        svn mv ${WORK_PATH}/${oldmod} ${WORK_PATH}/${newmod} || \
378                {       echo "There was a problem renaming the modules." >&4 ; \
379                        conf_clean_exit 1
380                }
381        svn mv ${WORK_PATH}/${REPO_CHECKPTS}/${oldmod} \
382                        ${WORK_PATH}/${REPO_CHECKPTS}/${newmod} || \
383                {       echo "There was a problem renaming the checkpts." >&4 ; \
384                        conf_clean_exit 1
385                }
386
387        svn update ${WORK_PATH} || \
388                {       echo "Your working copy didn't update correctly." >&4 ; \
389                        conf_clean_exit 1
390                }
391        svn commit -m "Renaming ${oldmod} to ${newmod}, phase 1." \
392                ${WORK_PATH}/{,${REPO_CHECKPTS}}/{${oldmod},${newmod}} || \
393                {       echo "Committing the changes failed." >&4 ; conf_clean_exit 1
394                }
395
396        # Next, we have to rename any singularities
397        for file in ${SINGULARITIES} ; do
398                if [ -f "${WORK_PATH}/${newmod}${file}-${oldmod}" ] ; then
399                        svn mv ${WORK_PATH}/${newmod}${file}-${oldmod} \
400                                        ${WORK_PATH}/${newmod}${file}-${newmod}
401                fi
402        done
403
404        svn update ${WORK_PATH} || \
405                {       echo "Your working copy didn't update correctly." >&4 ; \
406                        conf_clean_exit 1
407                }
408        svn commit -m "Renaming ${oldmod} to ${newmod}, phase 2." \
409                ${WORK_PATH}/${newmod} || \
410                {       echo "Committing the changes failed." >&4 ; conf_clean_exit 1
411                }
412
413        # Now, we have to go fix all the confman headers in all the files...
414        find ${WORK_PATH}/${newmod} -type f -not -path '*/.svn/*' -exec \
415                gsed -i -r "s/^(# Managed under )${oldmod}( module)/\1${newmod}\2/" \
416                {} \;
417       
418        svn update ${WORK_PATH} || \
419                {       echo "Your working copy didn't update correctly." >&4 ; \
420                        conf_clean_exit 1
421                }
422        svn commit -m "Renaming ${oldmod} to ${newmod}, complete." \
423                ${WORK_PATH}/${newmod} || \
424                {       echo "Committing the changes failed." >&4 ; conf_clean_exit 1
425                }
426}
427
428function conf_mv {
429        local oldname=$1
430        local newname=$2
431        svn mv $oldname $newname
432}
433
434function conf_cp {
435        local oldname=$1
436        local newname=$2
437        svn cp $oldname $newname
438}
439
440function conf_diff {
441        svn diff $*
442}
443
444function conf_log {
445        svn log $*
446}
447
448# Accepts log messages on stdin until EOF
449function conf_logger {
450   logger -t "$MYNAME[$$]: $USER" -s 2>&4
451}
452
453function conf_chattr {
454    local attr="confman:$1"
455    shift
456    local value="$1"
457    shift
458    svn propset "$attr" "$value" "$@"
459}
460
461function conf_lsattr {
462    local file="$1"
463    shift
464    echo "$file"
465    for prop in $(svn proplist $file | grep 'confman:' | sort) ; do
466        echo -e "\t${prop#confman:}\t" $(svn propget $prop $file)
467    done
468    if [ -n "$1" ] ; then
469        conf_lsattr "$@"
470    fi
471}
472
473function conf_rmattr {
474    local attr="confman:$1"
475    shift
476    svn propdel "$attr" "$@"
477}
478
479function conf_syncheck {
480    local cmd
481    local toReturn=true
482    for file in "$@" ; do
483        cmd=$(svn propget "confman:syncheck" "$file")
484        if [ -z "$cmd" ] ; then
485            continue
486        fi
487        if ! $cmd "$file" ; then
488            echo "Syntax verification failed for $file" >&2
489            toReturn=false
490        fi
491    done
492    $toReturn
493}
494
495function conf_markclean {
496    echo clean > $REPO_SYNC_STATE
497}
498
499function conf_markdirty {
500    echo dirty > $REPO_SYNC_STATE
501}
502
503function conf_isclean {
504    local state=`cat $REPO_SYNC_STATE 2>/dev/null`
505    case $state in
506        clean)  return 0 ;;
507        dirty)  return 1 ;;
508        *)      echo "Invalid state. Reporting dirty." >&2 ; return 1 ;;
509    esac
510}
511
512# Returns the current revision number of the repository on stdout
513function conf_revision {
514    svn info ${WORK_PATH} | awk '/Last Changed Rev:/ {print $4}'
515}
516
517# Returns the revision of the last commit or install operation on stdout
518function conf_sysrev {
519    cut -d ':' -f 2 $REPO_ACTION
520}
521
522# Returns the last action of the repository on stdout
523function conf_lastact {
524    cut -d ':' -f 1 $REPO_ACTION
525}
526
527# Records the specified action
528function conf_recordAction {
529    local action="$1"
530    local revision=`conf_revision`
531    echo "${action}:${revision}" > $REPO_ACTION
532}
533
534# WARNING: If you refer to fd 2 in this code, this function will silently
535# fail when called by trap. DO NOT ATTEMPT TO LOG TO fd 2 IN THIS FUNCTION!
536# The SIGNAL seems to kill the logger process spawned by the parent
537# process, which is receiving its input from our fd 2.
538function conf_cleanexit {
539    echo "Received a signal. Exiting cleanly." | conf_logger
540        echo "Abort, Abort! Patience is a virtue." >&4
541        echo "Please wait until I finish cleaning up after you." >&4
542
543        # And in case we got an interrupt during a rollout, we still want the
544        # permissions here to be in a consistent state.
545        chmod 700 ${WORK_PATH}
546
547        echo "Removing leftover temp files..." >&4
548        find /tmp/confman* -maxdepth 0 -user `whoami` -exec rm -rf {} \;
549
550        # And this clears any locks we may have created on our working copy:
551        echo "Undoing damage to your working copy..." >&4
552        [ -d "${WORK_PATH}" ] && svn cleanup ${WORK_PATH}
553        echo "All clean. Terminating." >&4
554
555        exit ${1:-1}
556}       
557
558# vim:ts=4
559
Note: See TracBrowser for help on using the repository browser.