3 # ============LICENSE_START=======================================================
5 # ================================================================================
6 # Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
7 # ================================================================================
8 # Licensed under the Apache License, Version 2.0 (the "License");
9 # you may not use this file except in compliance with the License.
10 # You may obtain a copy of the License at
12 # http://www.apache.org/licenses/LICENSE-2.0
14 # Unless required by applicable law or agreed to in writing, software
15 # distributed under the License is distributed on an "AS IS" BASIS,
16 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 # See the License for the specific language governing permissions and
18 # limitations under the License.
19 # ============LICENSE_END=========================================================
21 # #####################################################################
23 # Upgrade/Downgrade SQL File Name Format:
25 # <VERSION>-<pdp|feature-name>[-description](.upgrade|.downgrade).sql
27 # This tool operates on a migration working directory at
29 # $POLICY_HOME/etc/db/migration
31 # Upgrade/Downgrade files for each schema (aka database) names to be maintained
32 # by this tool are located at
34 # $POLICY_HOME/etc/db/migration/<schema-name>/sql
36 # The nature of the migration directories is dynamic.
37 # Other tooling aware of when migrations are needed are in charge to populate
38 # the migrations directory accordingly.
40 # One of these tools is the 'features' when a feature with DB requirements
41 # is 'enabled', the upgrade scripts will be made present in the migration directory.
42 # When a features is 'disabled' downgrade scripts will be made available in the
43 # migration directory.
45 # The 'policy' tool via its operations 'status' or 'start' will signal the
46 # need to perform upgrade or downgrade for a given schema.
48 # At any given time the following invariant must be preserved in any given
49 # $POLICY_HOME/etc/db/migration/<schema-name>/sql directory
51 # There is only upgrade scripts, or only downgrade scripts, or none.
53 # #####################################################################
56 METADATA_TABLE="${METADATA_DB}".metadata_versions
57 MIGRATION_DIR="${POLICY_HOME}"/etc/db/migration
60 UPGRADE_SQL_SUFFIX=".upgrade.sql"
61 DOWNGRADE_SQL_SUFFIX=".downgrade.sql"
63 SQL_QUOTES="SET SESSION SQL_MODE=ANSI_QUOTES;"
65 #####################################################
67 #####################################################
71 echo -e "syntax: $(basename "$0") "
72 echo -e "\t -s <schema-name> "
73 echo -e "\t [-b <migration-dir>] "
74 echo -e "\t [-f <from-version>]"
75 echo -e "\t [-t <target-version>]"
76 echo -e "\t -o <operations> "
78 echo -e "\t where <operations>=upgrade|downgrade|auto|version|erase|report"
81 echo -e "Configuration Options:"
82 echo -e "\t -s|--schema|--database: schema to operate on ('ALL' to apply on all)"
83 echo -e "\t -b|--basedir: overrides base DB migration directory"
84 echo -e "\t -f|--from: overrides current release version for operations"
85 echo -e "\t -t|--target: overrides target release to upgrade/downgrade"
88 echo -e "\t upgrade: upgrade operation"
89 echo -e "\t downgrade: performs a downgrade operation"
90 echo -e "\t auto: autonomous operation, determines upgrade or downgrade"
91 echo -e "\t version: returns current version, and in conjunction if '-f' sets the current version"
92 echo -e "\t erase: erase all data related <schema> (use with care)"
93 echo -e "\t report: migration detailed report on an schema"
94 echo -e "\t ok: is the migration status valid"
99 #####################################################
100 # ensure global metadata
101 #####################################################
103 function ensure_metadata
105 if [[ ${DEBUG} == y ]]; then
106 echo "-- ${FUNCNAME[0]} --"
112 sql="CREATE DATABASE IF NOT EXISTS ${METADATA_DB};"
113 ${MYSQL} --execute "${sql}"
115 if [[ ${rc} != 0 ]]; then
119 sql="CREATE TABLE IF NOT EXISTS ${METADATA_TABLE} "
120 sql+="(name VARCHAR(60) NOT NULL, version VARCHAR(20), PRIMARY KEY(name));"
121 ${MYSQL} --execute "${sql}"
126 #####################################################
127 # ensure metadata on a per schema basis
128 #####################################################
130 function ensure_metadata_schema
132 if [[ ${DEBUG} == y ]]; then
133 echo "-- ${FUNCNAME[0]} --"
139 sql="CREATE TABLE IF NOT EXISTS ${METADATA_HISTORY} "
140 sql+="(script VARCHAR(80) NOT NULL, operation VARCHAR(10), success VARCHAR(1), "
141 sql+="atTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, "
142 sql+="PRIMARY KEY(script));"
143 ${MYSQL} --execute "${sql}"
145 if [[ ${rc} != 0 ]]; then
149 sql="CREATE DATABASE IF NOT EXISTS ${SCHEMA_DB};"
150 ${MYSQL} --execute "${sql}"
155 #####################################################
157 #####################################################
159 function target_release
161 if [[ ${DEBUG} == y ]]; then
162 echo "-- ${FUNCNAME[0]} --"
166 local sql sqlName upgradeSqls downgradeSqls
168 TARGET_UPGRADE_RELEASE=${ZERO_VERSION}
169 TARGET_DOWNGRADE_RELEASE=${ZERO_VERSION}
171 upgradeSqls=$(ls -v -r "${UPGRADE_DIR}"/*"${UPGRADE_SQL_SUFFIX}" 2> /dev/null)
172 for sql in ${upgradeSqls}; do
173 sqlName=$(basename "${sql}")
174 TARGET_UPGRADE_RELEASE="${sqlName%-*}"
178 # default unless overriden
179 TARGET_DOWNGRADE_RELEASE="${ZERO_VERSION}"
182 #####################################################
184 #####################################################
188 if [[ ${DEBUG} == y ]]; then
189 echo "-- ${FUNCNAME[0]} --"
195 upgradeSqls=$(ls "${UPGRADE_DIR}"/*"${UPGRADE_SQL_SUFFIX}" 2> /dev/null)
196 if [[ -z ${upgradeSqls} ]]; then
204 #####################################################
206 #####################################################
208 function is_downgrade
210 if [[ ${DEBUG} == y ]]; then
211 echo "-- ${FUNCNAME[0]} --"
217 downgradeSqls=$(ls "${DOWNGRADE_DIR}"/*"${DOWNGRADE_SQL_SUFFIX}" 2> /dev/null)
218 if [[ -z ${downgradeSqls} ]]; then
226 #####################################################
228 #####################################################
230 function current_release
232 if [[ ${DEBUG} == y ]]; then
233 echo "-- ${FUNCNAME[0]} --"
238 local query="SELECT version FROM ${METADATA_TABLE} WHERE name='${SCHEMA}'"
240 CURRENT_RELEASE=$(${MYSQL} --skip-column-names --silent --execute "${query}")
241 if [[ -z ${CURRENT_RELEASE} ]]; then
242 set_current_release "${ZERO_VERSION}"
250 #####################################################
251 # set_current_release
252 #####################################################
254 function set_current_release
256 if [[ ${DEBUG} == y ]]; then
257 echo "-- ${FUNCNAME[0]} $* --"
261 CURRENT_RELEASE="${1}"
263 local sql="INSERT INTO ${METADATA_TABLE} (name, version) "
264 sql+="VALUES('${SCHEMA}', '${CURRENT_RELEASE}') "
265 sql+="ON DUPLICATE KEY UPDATE version='${CURRENT_RELEASE}';"
267 ${MYSQL} --execute "${sql}"
272 #####################################################
273 # execute sql script history
274 #####################################################
276 function track_script
278 if [[ ${DEBUG} == y ]]; then
279 echo "-- ${FUNCNAME[0]} $* --"
283 local script="${1}" operation="${2}" success="${3}"
284 local sql="INSERT INTO ${METADATA_HISTORY}(script,operation,success,atTime) "
285 sql+="VALUES ('${script}','${operation}','${success}',now()) "
286 sql+="ON DUPLICATE KEY UPDATE operation=values(operation), success=values(success), atTime=values(atTime);"
288 ${MYSQL} --execute "${sql}"
293 #####################################################
295 #####################################################
299 if [[ ${DEBUG} == y ]]; then
300 echo "-- ${FUNCNAME[0]} $* --"
304 local operation="${1}" script="${2}" scriptPath="${3}"
307 echo "> ${operation} ${script}"
309 ${MYSQL} --verbose < "${scriptPath}"
311 if [[ ${rc} != 0 ]]; then
317 track_script "${script}" "${operation}" "${success}"
322 #####################################################
324 #####################################################
328 if [[ ${DEBUG} == y ]]; then
329 echo "-- ${FUNCNAME[0]} --"
333 local sqlName sqlFile schemaVersion upgradeSqls rc
335 ${MYSQL} --execute "USE ${SCHEMA_DB}"
337 echo "upgrade: ${CURRENT_RELEASE} -> ${TARGET_UPGRADE_RELEASE}"
339 if [[ ${CURRENT_RELEASE} < ${TARGET_UPGRADE_RELEASE} ]]; then
340 upgradeSqls=$(ls -v "${UPGRADE_DIR}"/*"${UPGRADE_SQL_SUFFIX}" 2> /dev/null)
341 for sqlFile in ${upgradeSqls}; do
342 sqlName=$(basename "${sqlFile}")
343 schemaVersion="${sqlName%-*}"
344 if [ "${schemaVersion}" -gt "${CURRENT_RELEASE}" ] && \
345 [ "${schemaVersion}" -le "${TARGET_UPGRADE_RELEASE}" ]; then
346 run_script "upgrade" "${sqlName}" "${sqlFile}"
348 if [[ ${rc} != 0 ]]; then
349 echo "${SCHEMA}: upgrade aborted at ${schemaVersion} by script ${sqlName}"
350 set_current_release "${schemaVersion}"
356 set_current_release "${TARGET_UPGRADE_RELEASE}"
362 #####################################################
364 #####################################################
368 if [[ ${DEBUG} == y ]]; then
369 echo "-- ${FUNCNAME[0]} --"
373 local sqlName sqlFile schemaVersion downgradeSqls rc
375 ${MYSQL} --execute "USE ${SCHEMA_DB}"
377 echo "downgrade: ${CURRENT_RELEASE} -> ${TARGET_DOWNGRADE_RELEASE}"
379 if [[ ${CURRENT_RELEASE} > ${TARGET_DOWNGRADE_RELEASE} ]]; then
380 downgradeSqls=$(ls -v -r "${DOWNGRADE_DIR}"/*"${DOWNGRADE_SQL_SUFFIX}" 2> /dev/null)
381 for sqlFile in ${downgradeSqls}; do
382 sqlName=$(basename "${sqlFile}")
383 schemaVersion="${sqlName%-*}"
384 if [ "${schemaVersion}" -le "${CURRENT_RELEASE}" ] && \
385 [ "${schemaVersion}" -gt "${TARGET_DOWNGRADE_RELEASE}" ]; then
386 run_script "downgrade" "${sqlName}" "${sqlFile}"
388 if [[ ${rc} != 0 ]]; then
389 echo "${SCHEMA}: downgrade aborted at ${schemaVersion} by script ${sqlName}"
390 set_current_release "${schemaVersion}"
396 set_current_release "${TARGET_DOWNGRADE_RELEASE}"
402 #####################################################
404 #####################################################
408 if [[ ${DEBUG} == y ]]; then
409 echo "-- ${FUNCNAME[0]} --"
413 local updateMetadata="UPDATE ${METADATA_TABLE} SET version='${ZERO_VERSION}';"
414 ${MYSQL} --execute "${updateMetadata}"
416 local deleteHistory="DELETE FROM ${METADATA_HISTORY};"
417 ${MYSQL} --execute "${deleteHistory}"
419 local dropDB="DROP DATABASE IF EXISTS ${SCHEMA_DB}";
420 ${MYSQL} --execute "${dropDB}"
423 #####################################################
425 #####################################################
429 if [[ ${DEBUG} == y ]]; then
430 echo "-- ${FUNCNAME[0]} --"
434 local versionSql="SELECT * FROM ${METADATA_TABLE} WHERE name='${SCHEMA}';"
435 ${MYSQL} --execute "${versionSql}"
437 local historySql="SELECT * FROM ${METADATA_HISTORY} ORDER BY atTime ASC;"
438 ${MYSQL} --execute "${historySql}"
445 if [[ ${DEBUG} == y ]]; then
446 echo "-- ${FUNCNAME[0]} --"
452 if [[ ${CURRENT_RELEASE} == "${TARGET_UPGRADE_RELEASE}" ]]; then
453 echo "${SCHEMA}: OK @ ${CURRENT_RELEASE}"
455 echo "${SCHEMA}: upgrade available: ${CURRENT_RELEASE} -> ${TARGET_UPGRADE_RELEASE}"
459 if [[ ${CURRENT_RELEASE} == "${TARGET_DOWNGRADE_RELEASE}" ]]; then
460 echo "${SCHEMA}: OK @ ${CURRENT_RELEASE}"
462 echo "${SCHEMA}: downgrade available: ${CURRENT_RELEASE} -> ${TARGET_DOWNGRADE_RELEASE}"
470 #####################################################
472 #####################################################
474 if [[ ${DEBUG} == y ]]; then
479 until [[ -z "$1" ]]; do
481 -s|--schema|--database) shift
488 INPUT_TARGET_RELEASE=$1
491 INPUT_CURRENT_RELEASE=$1
493 -o|--operation) shift
511 *) echo "error: invalid operation provided"
517 if [[ -z ${SCHEMA} ]]; then
518 echo "error: a database name must be provided"
523 source "${POLICY_HOME}"/etc/profile.d/base.conf
525 if [[ -z ${SQL_HOST} ]] || [[ -z ${SQL_USER} ]] || [[ -z ${SQL_PASSWORD} ]]; then
526 echo "error: no database has been set up"
530 MYSQL="mysql -u${SQL_USER} -p${SQL_PASSWORD} -h ${SQL_HOST}";
531 if ! ${MYSQL} -h"${SQL_HOST}" --execute "show databases;" > /dev/null 2>&1; then
532 echo "error: No DB connectivity to ${SQL_HOST} for ${SQL_USER}"
536 if [[ ${SCHEMA} == ALL ]]; then
540 SCHEMA_S=$(ls -d "${MIGRATION_DIR}"/${SCHEMA}/ 2> /dev/null)
541 if [[ -z ${SCHEMA_S} ]]; then
542 echo "error: no databases available"
546 if ! ensure_metadata; then
547 echo "error: migration metadata not accessible"
552 for dbPath in ${SCHEMA_S}; do
553 SCHEMA=$(basename "${dbPath}")
554 SCHEMA_DB="\`${SCHEMA}\`"
555 UPGRADE_DIR="${MIGRATION_DIR}"/"${SCHEMA}"/sql
556 DOWNGRADE_DIR=${UPGRADE_DIR}
557 METADATA_HISTORY="${METADATA_DB}.\`${SCHEMA}_history\`"
558 TARGET_RELEASE=${INPUT_TARGET_RELEASE}
559 CURRENT_RELEASE=${INPUT_CURRENT_RELEASE}
561 if is_upgrade && is_downgrade; then
562 echo "${SCHEMA}: failure: invalid configuration: ${UPGRADE_SQL_SUFFIX} and "\
563 "${DOWNGRADE_SQL_SUFFIX} exist under ${DOWNGRADE_DIR}"
568 if [[ ${operation} == auto ]]; then
576 if ! ensure_metadata_schema; then
577 echo "${SCHEMA}: failure: metadata not accessible for this schema"
581 if [[ -z ${TARGET_RELEASE} ]]; then
584 # user asked to override
585 TARGET_UPGRADE_RELEASE="${TARGET_RELEASE}"
586 TARGET_DOWNGRADE_RELEASE="${TARGET_RELEASE}"
589 if [[ -z ${CURRENT_RELEASE} ]]; then
590 if ! current_release; then
591 echo "${SCHEMA}: failure: cannot obtain current release"
595 if ! set_current_release "${CURRENT_RELEASE}"; then
596 echo "${SCHEMA}: failure: cannot set current release"
602 upgrade) if upgrade; then
603 echo "${SCHEMA}: OK: upgrade (${CURRENT_RELEASE})"
606 echo "${SCHEMA}: failure: upgrade to release ${TARGET_UPGRADE_RELEASE} (${CURRENT_RELEASE})"
609 downgrade) if downgrade; then
610 echo "${SCHEMA}: OK: downgrade (${CURRENT_RELEASE})"
613 echo "${SCHEMA}: failure: downgrade to release ${TARGET_DOWNGRADE_RELEASE} (${CURRENT_RELEASE})"
616 version) echo "${SCHEMA}: ${CURRENT_RELEASE}"