|
| 1 | +#!/usr/bin/env bash |
| 2 | + |
| 3 | +# Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. |
| 4 | +# SPDX-License-Identifier: Apache-2.0 |
| 5 | + |
| 6 | +set -euo pipefail |
| 7 | + |
| 8 | +# shellcheck disable=SC1091 |
| 9 | +source "${TOOLS_LIB}/libcli.source" |
| 10 | +# shellcheck disable=SC1091 |
| 11 | +source "${SPLICE_ROOT}/cluster/scripts/utils.source" |
| 12 | + |
| 13 | +function usage() { |
| 14 | + _info "Usage: $0 <namespace> <migration_id> <internal (true|false)>" |
| 15 | +} |
| 16 | + |
| 17 | +function is_full_backup_kube() { |
| 18 | + local component_backup_names=$1 |
| 19 | + local expected_components=$2 |
| 20 | + |
| 21 | + # Check if all expected components can be found in the component_backup_names |
| 22 | + for component in $expected_components; do |
| 23 | + count=$(echo "$component_backup_names" | grep -c "$component") |
| 24 | + if [ "$count" -ne 1 ]; then |
| 25 | + return 1 |
| 26 | + fi |
| 27 | + done |
| 28 | + |
| 29 | + return 0 |
| 30 | +} |
| 31 | + |
| 32 | +function get_component_backup_names_kube() { |
| 33 | + local migration_id=$1 |
| 34 | + local run_id=$2 |
| 35 | + component_backup_names=$(kubectl get volumesnapshot -n "$namespace" --sort-by=.metadata.creationTimestamp -o json | jq "[.items[] | select(.metadata.annotations[\"migrationId\"] == \"$migration_id\") | select(.metadata.name | endswith(\"$run_id\"))]" | jq -r '.[].metadata.name // empty' ) |
| 36 | + echo "$component_backup_names" |
| 37 | +} |
| 38 | + |
| 39 | +function latest_full_backup_run_id_kube() { |
| 40 | + local namespace=$1 |
| 41 | + local migration_id=$2 |
| 42 | + local is_sv=$3 |
| 43 | + local expected_components=$4 |
| 44 | + if [ "$is_sv" == "true" ]; then |
| 45 | + expected_components="$expected_components cometbft" |
| 46 | + fi |
| 47 | + |
| 48 | + local all_run_ids |
| 49 | + # get all run id postgres |
| 50 | + all_run_ids=$(kubectl get volumesnapshot -n "$namespace" --sort-by=.metadata.creationTimestamp -o json | jq "[.items[] | select(.metadata.annotations[\"migrationId\"] == \"$migration_id\")]" | jq -r '.[].metadata.name // empty' | grep -o '[^-]*$' | sort -rn | uniq ) |
| 51 | + |
| 52 | + while read -r run_id; do |
| 53 | + component_backup_names=$(get_component_backup_names_kube "$migration_id" "$run_id") |
| 54 | + if is_full_backup_kube "$component_backup_names" "$expected_components"; then |
| 55 | + echo "$run_id" |
| 56 | + return 0 |
| 57 | + fi |
| 58 | + done <<< "$all_run_ids" |
| 59 | + return 1 |
| 60 | +} |
| 61 | + |
| 62 | +function latest_full_backup_run_id_gcloud() { |
| 63 | + local namespace=$1 |
| 64 | + local migration_id=$2 |
| 65 | + local is_sv=$3 |
| 66 | + local expected_components=$4 |
| 67 | + local internal=$5 |
| 68 | + local num_components |
| 69 | + num_components=$(echo "$expected_components" | wc -w) |
| 70 | + local stack |
| 71 | + |
| 72 | + declare -A run_ids_dict |
| 73 | + |
| 74 | + for component in $expected_components; do |
| 75 | + stack=$(get_stack_for_namespace_component "$namespace" "$component" "$internal") |
| 76 | + instance="$(create_component_instance "$component" "$migration_id" "$namespace" "$internal")" |
| 77 | + local full_component_instance="$namespace-$instance-pg" |
| 78 | + |
| 79 | + local cloudsql_id |
| 80 | + cloudsql_id=$(get_cloudsql_id "$full_component_instance" "$stack") |
| 81 | + # We always create backups with a description field while this field could be missing for automated backups done by Google Cloud. |
| 82 | + # So we filter those out while looking for the most recent backup. |
| 83 | + mapfile -t run_ids < <(gcloud sql backups list --instance "$cloudsql_id" --filter=type="ON_DEMAND" --format=json | jq -r '.[] | select(has("description")) | .description') |
| 84 | + run_ids_dict[$component]="${run_ids[*]}" |
| 85 | + done |
| 86 | + |
| 87 | + if [ "$is_sv" == "true" ]; then |
| 88 | + mapfile -t cometbft_run_ids < <(kubectl get volumesnapshot -n "$namespace" --sort-by=.metadata.creationTimestamp -o json | jq "[.items[] | select(.metadata.annotations[\"migrationId\"] == \"$migration_id\") | select(.metadata.name | contains(\"cometbft\"))]" | jq -r '.[].metadata.name // empty' | grep -o '[^-]*$' | sort -rn | uniq ) |
| 89 | + run_ids_dict["cometbft"]="${cometbft_run_ids[*]}" |
| 90 | + ((num_components++)) |
| 91 | + fi |
| 92 | + |
| 93 | + declare -A count_dict |
| 94 | + for key in "${!run_ids_dict[@]}"; do |
| 95 | + for value in ${run_ids_dict[$key]}; do |
| 96 | + if [ -z "${count_dict[$value]+_}" ]; then |
| 97 | + count_dict[$value]=0 |
| 98 | + fi |
| 99 | + ((count_dict[$value]++)) |
| 100 | + done |
| 101 | + done |
| 102 | + |
| 103 | + largest_run_id=$(for key in "${!count_dict[@]}"; do |
| 104 | + if [ "${count_dict[$key]}" -eq "$num_components" ]; then |
| 105 | + echo "$key" |
| 106 | + fi |
| 107 | + done | sort -n | tail -n 1) |
| 108 | + |
| 109 | + echo "$largest_run_id" |
| 110 | +} |
| 111 | + |
| 112 | +function main() { |
| 113 | + if [ "$#" -lt 3 ]; then |
| 114 | + usage |
| 115 | + exit 1 |
| 116 | + fi |
| 117 | + |
| 118 | + local namespace=$1 |
| 119 | + local migration_id=$2 |
| 120 | + local internal=$3 |
| 121 | + |
| 122 | + case "$namespace" in |
| 123 | + sv-1|sv-2|sv-3|sv-4) |
| 124 | + is_sv=true |
| 125 | + full_instance="$namespace-cn-apps-pg" |
| 126 | + expected_components="cn-apps sequencer participant mediator" |
| 127 | + stack=$(get_stack_for_namespace_component "$namespace" "cn-apps" "$internal") |
| 128 | + ;; |
| 129 | + *) |
| 130 | + is_sv=false |
| 131 | + full_instance="$namespace-validator-pg" |
| 132 | + expected_components="validator participant" |
| 133 | + stack=$(get_stack_for_namespace_component "$namespace" "participant" "$internal") |
| 134 | + ;; |
| 135 | + esac |
| 136 | + |
| 137 | + type=$(get_postgres_type "$full_instance" "$stack") |
| 138 | + # We only check the postgres type of one component and assume other components have the same type. |
| 139 | + if [ "$type" == "canton:network:postgres" ]; then |
| 140 | + backup_run_id=$(latest_full_backup_run_id_kube "$namespace" "$migration_id" "$is_sv" "$expected_components") |
| 141 | + echo "$backup_run_id" |
| 142 | + elif [ "$type" == "canton:cloud:postgres" ]; then |
| 143 | + backup_run_id=$(latest_full_backup_run_id_gcloud "$namespace" "$migration_id" "$is_sv" "$expected_components" "$internal") |
| 144 | + echo "$backup_run_id" |
| 145 | + elif [ -z "$type" ]; then |
| 146 | + _error "No postgres instance $full_instance found in stack ${stack}. Is the cluster deployed with split DB instances?" |
| 147 | + else |
| 148 | + _error "Unknown postgres type: $type" |
| 149 | + fi |
| 150 | +} |
| 151 | + |
| 152 | +main "$@" |
0 commit comments