flatcar-scripts/run_chroot_version_hooks
Mike Frysinger 7cd3c285d2 run_chroot_version_hooks: sanity check for multiple upgrades hooks
If people land upgrade hooks of the same version levels independently and
no one notices, we get into an inconsistent state where only one of the
hooks actually runs.  Add a sanity check so we bail rather than upgrading
the chroot with an arbitrarily picked script.

BUG=None
TEST=run build_packages with multiple 17_xxx files in a ver 16 chroot and see script error out
TEST=run build_packages with single 17_xxx file in a ver 16 chroot and see successful run

Change-Id: If9a71f60badb7c9643c1a03ad49bb23ffb64f341
Reviewed-on: https://gerrit.chromium.org/gerrit/11972
Reviewed-by: Zdenek Behan <zbehan@chromium.org>
Commit-Ready: Mike Frysinger <vapier@chromium.org>
Tested-by: Mike Frysinger <vapier@chromium.org>
2011-11-20 15:22:24 -08:00

154 lines
4.8 KiB
Bash
Executable File

#!/bin/bash
# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Load common CrOS utilities. Inside the chroot this file is installed in
# /usr/lib/crosutils. Outside the chroot we find it relative to the script's
# location.
. "$(dirname "$0")/common.sh" || { echo "Unable to load common.sh"; exit 1; }
# Script must run inside the chroot.
assert_inside_chroot
# May not be run as root.
assert_not_root_user
DEFINE_string version "" \
"Assume current chroot version is this."
DEFINE_boolean force_latest "${FLAGS_FALSE}" \
"Assume latest version and recreate the version file"
DEFINE_boolean skipfirst "${FLAGS_FALSE}" \
"Skip the first upgrade. This may be dangerous."
FLAGS "$@" || exit 1
VERSION_FILE=/etc/cros_chroot_version
UPGRADE_D="$(dirname ${0})/chroot_version_hooks.d"
function update_version() {
sudo touch ${VERSION_FILE}
sudo chown ${USER} ${VERSION_FILE}
echo "${1}" > "${VERSION_FILE}"
}
######################################################################
# Sanity checks:
if [ -n "${FLAGS_version}" ] && \
( [ "${FLAGS_skipfirst}" == "${FLAGS_TRUE}" ] || \
[ "${FLAGS_force_latest}" == "${FLAGS_TRUE}" ] ); then
error "The option --version cannot be combined with either"
error "--skipfirst or --force_latest."
exit 1
fi
if [ "${FLAGS_skipfirst}" == "${FLAGS_TRUE}" ] &&
[ "${FLAGS_force_latest}" == "${FLAGS_TRUE}" ]; then
error "--skipfirst and --force_latest cannot be combined."
exit 1
fi
# Latest version is the version of last upgrade.d file.
# Name format is ${number}_${short_description}
# Versions must be -n sorted, that is, the first continuous sequence
# of numbers is what counts. 12_ is before 111_, etc.
LATEST_VERSION=$(
ls "${UPGRADE_D}" | grep "^[0-9]*_" | \
sort -n | tail -n 1 | cut -f1 -d'_'
)
if [ "${FLAGS_force_latest}" == "${FLAGS_TRUE}" ]; then
update_version "${LATEST_VERSION}"
exit 0
fi
# If the file does not exist at all, chroot is old and does not have a version.
# default goes here
if ! [ -f "${VERSION_FILE}" ]; then
info "Chroot of unknown version, initializing to 0"
update_version 0
fi
CHROOT_VERSION=$(cat "${VERSION_FILE}")
# Check if version is a number.
if ! [ "${CHROOT_VERSION}" -ge "0" ] &> /dev/null; then
error "Your chroot version file ${VERSION_FILE} is bogus: ${CHROOT_VERSION}"
exit 1
fi
if [ "${FLAGS_skipfirst}" == "${FLAGS_TRUE}" ]; then
if [ "${CHROOT_VERSION}" -lt "${LATEST_VERSION}" ]; then
# if the new one is latest, this becomes noop
CHROOT_VERSION=$(expr ${CHROOT_VERSION} + 1)
update_version "${CHROOT_VERSION}"
else
error "Nothing to skip"
exit 1
fi
fi
if [ -n "${FLAGS_version}" ]; then
# Check if it's a number.
if ! [ "${FLAGS_version}" -ge "0" ] &> /dev/null; then
error "Trying to force invalid version: ${FLAGS_version}"
exit 1
fi
if [ "${FLAGS_version}" -gt "${LATEST_VERSION}" ]; then
error "Forcing nonexistant version: ${FLAGS_version}"
exit 1
fi
CHROOT_VERSION="${FLAGS_version}"
fi
if [ "${LATEST_VERSION}" -gt "${CHROOT_VERSION}" ]; then
info "Old chroot version (${CHROOT_VERSION}) found, running upgrade hooks"
pushd "${UPGRADE_D}" 1> /dev/null
for n in $(seq "$(expr ${CHROOT_VERSION} + 1)" "${LATEST_VERSION}"); do
hook=(${n}_*)
# Sanity check: if there are multiple ${n}_* files, then CL's landed
# at the same time and people didn't notice. Let's notice for them.
if [ ${#hook[@]} -gt 1 ]; then
error "Fatal: Upgrade ${n} has multiple hooks:"
error " ${hook[*]}"
error "Connor MacLeod knows: There can be only one."
exit 1
fi
hook=${hook[0]}
# Deprecation check; Deprecation can be done by removing old upgrade
# scripts and causing too old chroots to have to start over.
# Upgrades have to form a continuous sequence.
if ! [ -f ${hook} ]; then
error "Fatal: Upgrade ${n} doesn't exist."
error "Your chroot is so old, that some updates have been deprecated!"
error "You need to re-create it!"
exit 1
fi
info "Rollup ${hook}"
# Attempt the upgrade.
# NOTE: We source the upgrade scripts because:
# 1) We can impose set -something on them.
# 2) They can reuse local variables and functions (fe. from common.sh)
# Side effect is that the scripts have to be internally enclosed in
# a code block, otherwise simply running "exit" in any of them would
# terminate the master script, so we call it in a subshell.
if ! ( source ${hook} ); then
error "Fatal: failed to upgrade ${n}!"
exit 1
fi
# Each upgrade is atomic. If a middle upgrade fails, we won't retry
# all the ones that passed on a previous run.
update_version "${n}"
done
popd 1> /dev/null
fi