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/>. # along with cdist. If not, see <http://www.gnu.org/licenses/>.
# #
file="$(cat "$__object/parameter/file")" remove_line() {
state="$(cat "$__object/parameter/state")"
mkdir "$__object/files"
_cksum() {
echo "$1" | cksum | cut -d' ' -f 1
}
_do_line() {
file="$1" file="$1"
line="$2" line="$2"
state="$3" cat << DONE
line_id="$(_cksum "$file")-$(_cksum "$line")" tmpfile=\$(mktemp ${file}.cdist.XXXXXXXXXX)
# preserve ownership and permissions of existing file
set -- "$line_id" if [ -f "$file" ]; then
set -- "$@" --file "$file" cp -p "$file" "\$tmpfile"
set -- "$@" --line "$line" fi
set -- "$@" --state "$state" grep -v -F -x '$line' '$file' > \$tmpfile || true
# Ensure __line does not read stdin mv -f "\$tmpfile" "$file"
__line "$@" < /dev/null 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 # Generate the entry as it should be
( (
if [ -f "$__object/parameter/option" ]; then if [ -f "$__object/parameter/option" ]; then
# comma seperated list of options
options="$(cat "$__object/parameter/option" | tr '\n' ',')" options="$(cat "$__object/parameter/option" | tr '\n' ',')"
printf '%s ' "${options%*,}" printf '%s ' "${options%*,}"
fi fi
@ -56,20 +61,37 @@ _do_line() {
fi fi
) > "$__object/files/should" ) > "$__object/files/should"
# Check for existing and conflicting entries and remove them # Remove conflicting entries if any
if [ -s "$__object/explorer/entry" ]; then if [ -s "$__object/explorer/entry" ]; then
# We have existing entries for this key. # Note that the files have to be sorted for comparison with `comm`.
# 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`.
sort "$__object/explorer/entry" > "$__object/files/is" sort "$__object/explorer/entry" > "$__object/files/is"
comm -13 "$__object/files/should" "$__object/files/is" | { comm -13 "$__object/files/should" "$__object/files/is" | {
# Remove conflicting entries
while read entry; do while read entry; do
_do_line "$file" "$entry" absent remove_line "$file" "$entry"
done done
} }
fi 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 # 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")" 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