github.com/letsencrypt/boulder@v0.20251208.0/sa/migrations.sh (about) 1 #!/usr/bin/env bash 2 3 set -eu 4 5 if type realpath >/dev/null 2>&1 ; then 6 cd "$(realpath -- $(dirname -- "$0"))" 7 fi 8 9 # posix compliant escape sequence 10 esc=$'\033'"[" 11 res="${esc}0m" 12 13 # 14 # Defaults 15 # 16 DB_NEXT_PATH="db-next" 17 DB_PATH="db" 18 OUTCOME="ERROR" 19 PROMOTE=() 20 RUN=() 21 DB="" 22 23 # 24 # Print Functions 25 # 26 function print_outcome() { 27 if [ "${OUTCOME}" == OK ] 28 then 29 echo -e "${esc}0;32;1m${OUTCOME}${res}" 30 else 31 echo -e "${esc}0;31;1m${OUTCOME}${res}" 32 fi 33 } 34 35 function print_usage_exit() { 36 echo "${USAGE}" 37 exit 0 38 } 39 40 # newline + bold magenta 41 function print_heading() { 42 echo 43 echo -e "${esc}0;34;1m${1}${res}" 44 } 45 46 # bold cyan 47 function print_moving() { 48 local src=${1} 49 local dest=${2} 50 echo -e "moving: ${esc}0;36;1m${src}${res}" 51 echo -e "to: ${esc}0;32;1m${dest}${res}" 52 } 53 54 # bold yellow 55 function print_unlinking() { 56 echo -e "unlinking: ${esc}0;33;1m${1}${res}" 57 } 58 59 # bold magenta 60 function print_linking () { 61 local from=${1} 62 local to=${2} 63 echo -e "linking: ${esc}0;35;1m${from} ->${res}" 64 echo -e "to: ${esc}0;39;1m${to}${res}" 65 } 66 67 function check_arg() { 68 if [ -z "${OPTARG}" ] 69 then 70 exit_msg "No arg for --${OPT} option, use: -h for help">&2 71 fi 72 } 73 74 function print_migrations() { 75 iter=1 76 for file in "${migrations[@]}" 77 do 78 echo "${iter}) $(basename -- ${file})" 79 iter=$(expr "${iter}" + 1) 80 done 81 } 82 83 function exit_msg() { 84 # complain to STDERR and exit with error 85 echo "${*}" >&2 86 exit 2 87 } 88 89 # 90 # Utility Functions 91 # 92 function get_promotable_migrations() { 93 local migrations=() 94 local migpath="${DB_NEXT_PATH}/${1}" 95 for file in "${migpath}"/*.sql; do 96 [[ -f "${file}" && ! -L "${file}" ]] || continue 97 migrations+=("${file}") 98 done 99 if [[ "${migrations[@]}" ]]; then 100 echo "${migrations[@]}" 101 else 102 exit_msg "There are no promotable migrations at path: "\"${migpath}\""" 103 fi 104 } 105 106 function get_demotable_migrations() { 107 local migrations=() 108 local migpath="${DB_NEXT_PATH}/${1}" 109 for file in "${migpath}"/*.sql; do 110 [[ -L "${file}" ]] || continue 111 migrations+=("${file}") 112 done 113 if [[ "${migrations[@]}" ]]; then 114 echo "${migrations[@]}" 115 else 116 exit_msg "There are no demotable migrations at path: "\"${migpath}\""" 117 fi 118 } 119 120 # 121 # CLI Parser 122 # 123 USAGE="$(cat -- <<-EOM 124 125 Usage: 126 127 Boulder DB Migrations CLI 128 129 Helper for listing, promoting, and demoting migration files 130 131 ./$(basename "${0}") [OPTION]... 132 -b --db Name of the database, this is required (e.g. boulder_sa or incidents_sa) 133 -n, --list-next Lists migration files present in sa/db-next/<db> 134 -c, --list-current Lists migration files promoted from sa/db-next/<db> to sa/db/<db> 135 -p, --promote Select and promote a migration from sa/db-next/<db> to sa/db/<db> 136 -d, --demote Select and demote a migration from sa/db/<db> to sa/db-next/<db> 137 -h, --help Shows this help message 138 139 EOM 140 )" 141 142 while getopts nchpd-:b:-: OPT; do 143 if [ "$OPT" = - ]; then # long option: reformulate OPT and OPTARG 144 OPT="${OPTARG%%=*}" # extract long option name 145 OPTARG="${OPTARG#$OPT}" # extract long option argument (may be empty) 146 OPTARG="${OPTARG#=}" # if long option argument, remove assigning `=` 147 fi 148 case "${OPT}" in 149 b | db ) check_arg; DB="${OPTARG}" ;; 150 n | list-next ) RUN+=("list_next") ;; 151 c | list-current ) RUN+=("list_current") ;; 152 p | promote ) RUN+=("promote") ;; 153 d | demote ) RUN+=("demote") ;; 154 h | help ) print_usage_exit ;; 155 ??* ) exit_msg "Illegal option --${OPT}" ;; # bad long option 156 ? ) exit 2 ;; # bad short option (error reported via getopts) 157 esac 158 done 159 shift $((OPTIND-1)) # remove parsed opts and args from $@ list 160 161 # On EXIT, trap and print outcome 162 trap "print_outcome" EXIT 163 164 [ -z "${DB}" ] && exit_msg "You must specify a database with flag -b \"foo\" or --db=\"foo\"" 165 166 STEP="list_next" 167 if [[ "${RUN[@]}" =~ "${STEP}" ]] ; then 168 print_heading "Next Migrations" 169 migrations=($(get_promotable_migrations "${DB}")) 170 print_migrations "${migrations[@]}" 171 fi 172 173 STEP="list_current" 174 if [[ "${RUN[@]}" =~ "${STEP}" ]] ; then 175 print_heading "Current Migrations" 176 migrations=($(get_demotable_migrations "${DB}")) 177 print_migrations "${migrations[@]}" 178 fi 179 180 STEP="promote" 181 if [[ "${RUN[@]}" =~ "${STEP}" ]] ; then 182 print_heading "Promote Migration" 183 migrations=($(get_promotable_migrations "${DB}")) 184 declare -a mig_index=() 185 declare -A mig_file=() 186 for i in "${!migrations[@]}"; do 187 mig_index["$i"]="${migrations[$i]%% *}" 188 mig_file["${mig_index[$i]}"]="${migrations[$i]#* }" 189 done 190 191 promote="" 192 PS3='Which migration would you like to promote? (q to cancel): ' 193 194 select opt in "${mig_index[@]}"; do 195 case "${opt}" in 196 "") echo "Invalid option or cancelled, exiting..." ; break ;; 197 *) mig_file_path="${mig_file[$opt]}" ; break ;; 198 esac 199 done 200 if [[ "${mig_file_path}" ]] 201 then 202 print_heading "Promoting Migration" 203 promote_mig_name="$(basename -- "${mig_file_path}")" 204 promoted_mig_file_path="${DB_PATH}/${DB}/${promote_mig_name}" 205 symlink_relpath="$(realpath --relative-to=${DB_NEXT_PATH}/${DB} ${promoted_mig_file_path})" 206 207 print_moving "${mig_file_path}" "${promoted_mig_file_path}" 208 mv "${mig_file_path}" "${promoted_mig_file_path}" 209 210 print_linking "${mig_file_path}" "${symlink_relpath}" 211 ln -s "${symlink_relpath}" "${DB_NEXT_PATH}/${DB}" 212 fi 213 fi 214 215 STEP="demote" 216 if [[ "${RUN[@]}" =~ "${STEP}" ]] ; then 217 print_heading "Demote Migration" 218 migrations=($(get_demotable_migrations "${DB}")) 219 declare -a mig_index=() 220 declare -A mig_file=() 221 for i in "${!migrations[@]}"; do 222 mig_index["$i"]="${migrations[$i]%% *}" 223 mig_file["${mig_index[$i]}"]="${migrations[$i]#* }" 224 done 225 226 demote_mig="" 227 PS3='Which migration would you like to demote? (q to cancel): ' 228 229 select opt in "${mig_index[@]}"; do 230 case "${opt}" in 231 "") echo "Invalid option or cancelled, exiting..." ; break ;; 232 *) mig_link_path="${mig_file[$opt]}" ; break ;; 233 esac 234 done 235 if [[ "${mig_link_path}" ]] 236 then 237 print_heading "Demoting Migration" 238 demote_mig_name="$(basename -- "${mig_link_path}")" 239 demote_mig_from="${DB_PATH}/${DB}/${demote_mig_name}" 240 241 print_unlinking "${mig_link_path}" 242 rm "${mig_link_path}" 243 print_moving "${demote_mig_from}" "${mig_link_path}" 244 mv "${demote_mig_from}" "${mig_link_path}" 245 fi 246 fi 247 248 OUTCOME="OK"