summaryrefslogtreecommitdiff
path: root/baserock-system-config-sync/baserock-system-config-sync
diff options
context:
space:
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
+