removing and adding keys must be atomic

If we delegate this to multiple seprarate objects (e.g. using __line) and the
config run is interrupted after applying only some of them we may leave the
target in some random state.
We may have even locked ourself out of the target.

So remove and add keys ourself so either none are all changes are
applied.

Signed-off-by: Steven Armstrong <steven@icarus.ethz.ch>
This commit is contained in:
Steven Armstrong 2014-09-27 11:40:06 +02:00
parent eed058426a
commit b17a1f0edb

View file

@ -18,31 +18,36 @@
# along with cdist. If not, see <http://www.gnu.org/licenses/>.
#
file="$(cat "$__object/parameter/file")"
state="$(cat "$__object/parameter/state")"
mkdir "$__object/files"
_cksum() {
echo "$1" | cksum | cut -d' ' -f 1
}
_do_line() {
remove_line() {
file="$1"
line="$2"
state="$3"
line_id="$(_cksum "$file")-$(_cksum "$line")"
set -- "$line_id"
set -- "$@" --file "$file"
set -- "$@" --line "$line"
set -- "$@" --state "$state"
# Ensure __line does not read stdin
__line "$@" < /dev/null
cat << DONE
tmpfile=\$(mktemp ${file}.cdist.XXXXXXXXXX)
# preserve ownership and permissions of existing file
if [ -f "$file" ]; then
cp -p "$file" "\$tmpfile"
fi
grep -v -F -x '$line' '$file' > \$tmpfile || true
mv -f "\$tmpfile" "$file"
DONE
}
add_line() {
file="$1"
line="$2"
# escape single quotes
line_sanitised=$(echo "$line" | sed -e "s/'/'\"'\"'/g")
printf '%s' "printf '%s\n' '$line_sanitised' >> $file"
}
file="$(cat "$__object/parameter/file")"
mkdir "$__object/files"
# Generate the entry as it should be
(
if [ -f "$__object/parameter/option" ]; then
# comma seperated list of options
options="$(cat "$__object/parameter/option" | tr '\n' ',')"
printf '%s ' "${options%*,}"
fi
@ -56,20 +61,37 @@ _do_line() {
fi
) > "$__object/files/should"
# Check for existing and conflicting entries and remove them
# Remove conflicting entries if any
if [ -s "$__object/explorer/entry" ]; then
# We have existing entries for this key.
# Check if any of them are in conflict to how the entry should be.
# Note that the file has to be sorted for comparison with `comm`.
# Note that the files have to be sorted for comparison with `comm`.
sort "$__object/explorer/entry" > "$__object/files/is"
comm -13 "$__object/files/should" "$__object/files/is" | {
# Remove conflicting entries
while read entry; do
_do_line "$file" "$entry" absent
remove_line "$file" "$entry"
done
}
fi
# Determine the current state
state_should="$(cat "$__object/parameter/state")"
if grep -q -F -x "$entry" "$__object/explorer/entry"; then
state_is="present"
else
state_is="absent"
fi
# Manage the actual entry as it should be
if [ "$state_should" = "$state_is" ]; then
# Nothing to do
exit 0
fi
entry="$(cat "$__object/files/should")"
_do_line "$file" "$entry" "$state"
case "$state_should" in
present)
add_line "$file" "$entry"
;;
absent)
remove_line "$file" "$entry"
;;
esac