summaryrefslogtreecommitdiff
path: root/baserock-system-config-sync/baserock-system-config-sync
diff options
context:
space:
mode:
authorTiago Gomes <tiago.gomes@codethink.co.uk>2013-06-15 23:40:34 +0000
committerRichard Maw <richard.maw@codethink.co.uk>2013-06-25 15:31:15 +0100
commit51f930da27942f1e412a16d87e38c1d6e4d215a4 (patch)
tree6c040cb0b5f7f00ebfd450811ca207beb46b173e /baserock-system-config-sync/baserock-system-config-sync
parent160ccc9e93284ad567f476beb32620bb1e0e33b7 (diff)
downloadtbdiff-51f930da27942f1e412a16d87e38c1d6e4d215a4.tar.gz
Script to merge and syncronize /etc in different system versions
This commit adds a script to merge and syncronize /etc in different system versions. The first argument read from command line is the mode, which can be one of the following: - test: the purpose of this mode is to test some merge cases. It receives from the command line v1_dir, vu_dir and v2_dir and vt_dir. The meaning of these arguments is explained in the script. - merge: merges the user changes in /etc in the run system of the version given as argument - sync: syncronizes /etc in all run versions, so that this directory is exactly the same as the version given as argument. This commit also includes an auxiliary script to mount the systems directory in a give directory given as argument, and some testing folders to use with the test mode.
Diffstat (limited to 'baserock-system-config-sync/baserock-system-config-sync')
-rwxr-xr-xbaserock-system-config-sync/baserock-system-config-sync216
1 files changed, 216 insertions, 0 deletions
diff --git a/baserock-system-config-sync/baserock-system-config-sync b/baserock-system-config-sync/baserock-system-config-sync
new file mode 100755
index 0000000..00ceac0
--- /dev/null
+++ b/baserock-system-config-sync/baserock-system-config-sync
@@ -0,0 +1,216 @@
+#!/bin/sh
+#
+# Copyright (c) 2013 Codethink Ltd.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License Version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+set -eu
+
+
+mounting_script="$(dirname "$0")/../libexec/mount-system-versions-dir"
+
+
+usage() {
+ echo "Usage: $(basename $0) test V1_DIR VU_DIR V2_DIR VT_DIR" >&2
+ echo " $(basename $0) merge NEW_VERSION_LABEL" >&2
+ echo " $(basename $0) sync CANONICAL_VERSION_LABEL" >&2
+ exit 1
+}
+
+
+die () {
+ echo $@ >&2
+ exit 1
+}
+
+
+merge() {
+ local vp_dir="$1" # version being processed
+ local v1_dir="$2" # factory version
+ local vu_dir="$3" # user modified version
+ local v2_dir="$4" # unmodified new deployed version
+ local vt_dir="$5" # target version where the result of the
+ # merge should be placed
+
+ # use `find "$vp_dir/"*` instead of `find "$vp_dir"` because
+ # the last one also gives $vp_dir in the list of directories
+ find "$vp_dir/"* | while read f; do
+ # strip first component from file name
+ local stripped_filename=${f#$vp_dir/}
+
+ local vp="$vp_dir/$stripped_filename"
+ local v1="$v1_dir/$stripped_filename"
+ local vu="$vu_dir/$stripped_filename"
+ local v2="$v2_dir/$stripped_filename"
+ local vt="$vt_dir/$stripped_filename"
+
+ if [ -e "$vt" ]; then
+ # If the file already exists in the target,
+ # check if it is of the same kind
+ vp_type="$(stat -c %F "$vp")"
+ vt_type="$(stat -c %F "$vt")"
+ echo "$vp - $vt - $vp_type - $vt_type" >&2
+ if [ "$vp_type" != "$vt_type" ]; then
+ die "found two different types for '$stripped_filename':" \
+ "$vp_type and $vt_type"
+ fi
+ elif [ -d "$vp" ] && [ ! -h "$vp" ]; then
+ mkdir "$vt"
+ elif [ -h "$vp" ]; then
+ # Discussion: what we want to do about symbolic links?
+ # I chose a symbolic link in this order of preference:
+ # Vuser, V2, V1
+ if [ -h "$vu" ]; then
+ cp -a "$vu" "$vt"
+ elif [ -h "$v2" ]; then
+ cp -a "$v2" "$vt"
+ else
+ cp -a "$v1" "$vt"
+ fi
+ elif [ -f "$vp" ]; then
+ merge_regular_file "$v1" "$vu" "$v2" "$vt"
+ else
+ die "ERROR: unexpected error"
+ fi
+ done
+}
+
+
+merge_regular_file() {
+ local v1="$1"
+ local vu="$2"
+ local v2="$3"
+ local vt="$4"
+ # Reference table for merging a regular file
+ #
+ # V1 Vuser V2 action
+ # ------------------------------------------
+ # none none none inconceivable!
+ # exists none none use V1
+ # none exists none use Vuser
+ # none none exists use V2
+ # exists none exists use V2
+ # exists exists none use V2
+ # none exists exists diff V2 Vuser applied to V2
+ # exists exists exists diff V1 Vuser applied to V2
+
+ v1_exists=$(test -f "$v1" && echo exists || echo none)
+ vu_exists=$(test -f "$vu" && echo exists || echo none)
+ v2_exists=$(test -f "$v2" && echo exists || echo none)
+ case "$v1_exists $vu_exists $v2_exists" in
+ 'exists none none')
+ cp -a "$v1" "$vt"
+ ;;
+ 'none exists none')
+ cp -a "$vu" "$vt"
+ ;;
+ 'none none exists')
+ cp -a "$v2" "$vt"
+ ;;
+ 'exists none exists')
+ cp -a "$v2" "$vt"
+ ;;
+ 'exists exists none')
+ # In the relevant mustard node it is specified that
+ # when we have v1 and vu, but not v2, we shouldn't
+ # do nothing. I changed to copy the user configuration
+ # instead
+ cp -a "$vu" "$vt"
+ ;;
+ 'none exists exists')
+ # In the relevant mustard node it is specified a diff
+ # between /dev/null and vu. However this causes a
+ # complaint against reversed patches. The option
+ # "-R" didn't help.
+ if ! (diff "$v2" "$vu" | patch "$v2" -o "$vt"); then
+ cp -a "$v2" "$vt" # merge failed, use v2
+ fi
+ ;;
+ 'exists exists exists')
+ if ! (diff "$v1" "$vu" | patch "$v2" -o "$vt"); then
+ cp -a "$v2" "$vt" # merge failed, use v2
+ fi
+ ;;
+ *)
+ die "ERROR: unexpected error"
+ ;;
+ esac
+}
+
+
+if [ "$#" = 0 ]; then
+ usage "$0"
+fi
+
+
+if [ "$1" = "test" ]; then
+ if [ "$#" != 5 ]; then
+ usage "$0"
+ fi
+ local v1="$2"
+ local vu="$3"
+ local v2="$4"
+ local vt="$5"
+ mkdir -p "$vt"
+ # For every pathname in V1, Vuser, or V2
+ merge "$v1" "$v1" "$vu" "$v2" "$vt"
+ merge "$vu" "$v1" "$vu" "$v2" "$vt"
+ merge "$v2" "$v1" "$vu" "$v2" "$vt"
+elif [ "$1" = "merge" ]; then
+ if [ "$#" != 2 ]; then
+ usage "$0"
+ fi
+ local new_version="$2"
+ local mounting_point=$(mktemp -d)
+ "$mounting_script" "$mounting_point"
+ trap 'umount "$mounting_point"' INT ERR EXIT
+ if [ ! -d "$mounting_point/systems/$new_version" ]; then
+ die "Error: version not found - '$new_version'"
+ fi
+ local v1_dir="$mounting_point/systems/default/orig/etc"
+ local vu_dir="$mounting_point/systems/default/run/etc"
+ local v2_dir="$mounting_point/systems/$new_version/run/etc"
+ local vt_dir="$mounting_point/systems/$new_version/run/etc.new"
+ mkdir "$vt_dir"
+
+ # For every pathname in V1, Vuser, or V2
+ merge "$v1_dir" "$v1_dir" "$vu_dir" "$v2_dir" "$vt_dir"
+ merge "$vu_dir" "$v1_dir" "$vu_dir" "$v2_dir" "$vt_dir"
+ merge "$v2_dir" "$v1_dir" "$vu_dir" "$v2_dir" "$vt_dir"
+
+ rm -rf "$v2_dir"
+ mv "$vt_dir" "$v2_dir"
+elif [ "$1" = "sync" ]; then
+ local canonical_version="$2"
+ local mounting_point=$(mktemp -d)
+ "$mounting_script" "$mounting_point"
+ trap 'umount "$mounting_point"' INT EXIT
+ if [ ! -d "$mounting_point/systems/$canonical_version" ]; then
+ die "Error: version not found - '$canonical_version'"
+ fi
+ for version_dir in "$mounting_point/systems/"*; do
+ version="$(basename "$version_dir")"
+ if [ -d "$version_dir" ] && [ ! -h "$version_dir" ] \
+ && [ $version != $canonical_version ]; then
+ cp -a "$mounting_point/systems/$canonical_version/run/etc" \
+ "$version_dir/run/etc.new"
+ mv "$version_dir/run/etc" "$version_dir/run/etc.old"
+ mv "$version_dir/run/etc.new" "$version_dir/run/etc"
+ rm -rf "$version_dir/run/etc.old"
+ fi
+ done
+else
+ usage "$0"
+fi
+