Add all 44 scripts, update CI: error severity baseline, PowerShell validation, multi-distro testing
Amp-Thread-ID: https://ampcode.com/threads/T-019cc404-c628-759e-a50b-f5eeea35b91f Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
@@ -0,0 +1,565 @@
|
||||
#!/bin/bash
|
||||
|
||||
##############################################################################
|
||||
#### Promtail to Grafana Alloy Migration Script ####
|
||||
#### ####
|
||||
#### Detects OS, reads existing Promtail config for Loki URL/hostname, ####
|
||||
#### generates equivalent Alloy River config, installs Alloy, and ####
|
||||
#### handles the cutover from Promtail to Alloy. ####
|
||||
#### ####
|
||||
#### Supports: Ubuntu, Debian, RHEL, CentOS, Rocky, Alma, Amazon Linux ####
|
||||
#### ####
|
||||
#### Author: Phil Connor ####
|
||||
#### License: MIT ####
|
||||
#### Contact: contact@mylinux.work ####
|
||||
#### Version: 1.0.0-030326 ####
|
||||
##############################################################################
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
readonly SCRIPT_NAME=$(basename "$0")
|
||||
readonly SCRIPT_VERSION="1.0.0-030326"
|
||||
|
||||
# Defaults
|
||||
ALLOY_CONFIG_DIR="/etc/alloy"
|
||||
ALLOY_CONFIG_FILE="/etc/alloy/config.alloy"
|
||||
PROMTAIL_CONFIG="/etc/promtail/config.yml"
|
||||
LOKI_URL=""
|
||||
CUSTOM_HOSTNAME=""
|
||||
DRY_RUN=false
|
||||
GENERATE_ONLY=false
|
||||
SKIP_INSTALL=false
|
||||
SKIP_CUTOVER=false
|
||||
KEEP_PROMTAIL=true
|
||||
VERBOSE=false
|
||||
INCLUDE_JOURNAL=true
|
||||
INCLUDE_NGINX=false
|
||||
INCLUDE_APACHE=false
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
error() { echo -e "${RED}[ERROR]${NC} $1" >&2; }
|
||||
debug() { [[ "$VERBOSE" == true ]] && echo -e "${BLUE}[DEBUG]${NC} $1"; }
|
||||
|
||||
show_help() {
|
||||
cat << EOF
|
||||
Usage: $SCRIPT_NAME [OPTIONS]
|
||||
|
||||
Migrate from Promtail to Grafana Alloy. Generates an Alloy config that
|
||||
maintains Promtail-compatible labels so existing dashboards keep working.
|
||||
|
||||
OPTIONS:
|
||||
--loki-url URL Loki push URL (default: extracted from Promtail config)
|
||||
--hostname NAME Override hostname (default: auto-detect or from Promtail)
|
||||
--promtail-config F Path to existing Promtail config (default: /etc/promtail/config.yml)
|
||||
--output FILE Alloy config output path (default: /etc/alloy/config.alloy)
|
||||
--generate-only Only generate the Alloy config, don't install or cutover
|
||||
--skip-install Skip Alloy installation (already installed)
|
||||
--skip-cutover Generate config and install, but don't stop Promtail
|
||||
--no-journal Skip systemd journal collection
|
||||
--include-nginx Include nginx log collection
|
||||
--include-apache Include Apache log collection
|
||||
--remove-promtail Remove Promtail package after cutover (default: keep)
|
||||
--dry-run Show what would be done without making changes
|
||||
--verbose Enable verbose output
|
||||
--version Show version
|
||||
--help, -h Show this help message
|
||||
|
||||
EXAMPLES:
|
||||
# Auto-detect everything from existing Promtail config
|
||||
sudo $SCRIPT_NAME
|
||||
|
||||
# Specify Loki URL and hostname
|
||||
sudo $SCRIPT_NAME --loki-url http://loki.example.com:3100 --hostname web-01
|
||||
|
||||
# Generate config only (don't install or cutover)
|
||||
$SCRIPT_NAME --generate-only --loki-url http://loki:3100 --output /tmp/config.alloy
|
||||
|
||||
# Full migration with nginx logs
|
||||
sudo $SCRIPT_NAME --include-nginx --remove-promtail
|
||||
|
||||
# Dry run to see what would happen
|
||||
sudo $SCRIPT_NAME --dry-run
|
||||
EOF
|
||||
}
|
||||
|
||||
parse_arguments() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--loki-url) LOKI_URL="$2"; shift 2 ;;
|
||||
--hostname) CUSTOM_HOSTNAME="$2"; shift 2 ;;
|
||||
--promtail-config) PROMTAIL_CONFIG="$2"; shift 2 ;;
|
||||
--output) ALLOY_CONFIG_FILE="$2"; shift 2 ;;
|
||||
--generate-only) GENERATE_ONLY=true; shift ;;
|
||||
--skip-install) SKIP_INSTALL=true; shift ;;
|
||||
--skip-cutover) SKIP_CUTOVER=true; shift ;;
|
||||
--no-journal) INCLUDE_JOURNAL=false; shift ;;
|
||||
--include-nginx) INCLUDE_NGINX=true; shift ;;
|
||||
--include-apache) INCLUDE_APACHE=true; shift ;;
|
||||
--remove-promtail) KEEP_PROMTAIL=false; shift ;;
|
||||
--dry-run) DRY_RUN=true; shift ;;
|
||||
--verbose) VERBOSE=true; shift ;;
|
||||
--version) echo "$SCRIPT_NAME version $SCRIPT_VERSION"; exit 0 ;;
|
||||
--help|-h) show_help; exit 0 ;;
|
||||
*) error "Unknown option: $1"; show_help; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
detect_os() {
|
||||
if [[ -f /etc/os-release ]]; then
|
||||
. /etc/os-release
|
||||
OS=$ID
|
||||
OS_PRETTY="$PRETTY_NAME"
|
||||
else
|
||||
error "Cannot detect OS"
|
||||
exit 1
|
||||
fi
|
||||
debug "Detected OS: $OS_PRETTY"
|
||||
}
|
||||
|
||||
detect_hostname() {
|
||||
if [[ -n "$CUSTOM_HOSTNAME" ]]; then
|
||||
DETECTED_HOSTNAME="$CUSTOM_HOSTNAME"
|
||||
debug "Using custom hostname: $DETECTED_HOSTNAME"
|
||||
return
|
||||
fi
|
||||
|
||||
# Try to extract from Promtail config
|
||||
if [[ -f "$PROMTAIL_CONFIG" ]]; then
|
||||
local pt_host
|
||||
pt_host=$(grep -m1 'host:' "$PROMTAIL_CONFIG" 2>/dev/null | awk '{print $2}' | tr -d '"' || true)
|
||||
if [[ -n "$pt_host" ]]; then
|
||||
DETECTED_HOSTNAME="$pt_host"
|
||||
debug "Extracted hostname from Promtail config: $DETECTED_HOSTNAME"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
DETECTED_HOSTNAME=$(hostname -f 2>/dev/null || hostname)
|
||||
debug "Using system hostname: $DETECTED_HOSTNAME"
|
||||
}
|
||||
|
||||
detect_loki_url() {
|
||||
if [[ -n "$LOKI_URL" ]]; then
|
||||
debug "Using provided Loki URL: $LOKI_URL"
|
||||
return
|
||||
fi
|
||||
|
||||
# Extract from Promtail config
|
||||
if [[ -f "$PROMTAIL_CONFIG" ]]; then
|
||||
LOKI_URL=$(grep -m1 'url:' "$PROMTAIL_CONFIG" 2>/dev/null | awk '{print $2}' | tr -d '"' | sed 's|/loki/api/v1/push||' || true)
|
||||
if [[ -n "$LOKI_URL" ]]; then
|
||||
debug "Extracted Loki URL from Promtail config: $LOKI_URL"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
error "Could not determine Loki URL"
|
||||
error "Provide with --loki-url or ensure Promtail config exists at $PROMTAIL_CONFIG"
|
||||
exit 1
|
||||
}
|
||||
|
||||
check_promtail_status() {
|
||||
if systemctl is-active --quiet promtail 2>/dev/null; then
|
||||
PROMTAIL_RUNNING=true
|
||||
log "Promtail is currently running"
|
||||
else
|
||||
PROMTAIL_RUNNING=false
|
||||
debug "Promtail is not running"
|
||||
fi
|
||||
}
|
||||
|
||||
# Generate an Alloy loki.source.file block if the log file exists
|
||||
generate_file_source() {
|
||||
local label="$1"
|
||||
local path="$2"
|
||||
local job="$3"
|
||||
local extra_labels="$4"
|
||||
|
||||
if [[ "$DRY_RUN" == true ]] || [[ -f "$path" ]] || [[ "$path" == *"*"* ]]; then
|
||||
cat << EOF
|
||||
|
||||
loki.source.file "$label" {
|
||||
targets = [
|
||||
{
|
||||
"__path__" = "$path",
|
||||
"job" = "$job",
|
||||
"host" = "$DETECTED_HOSTNAME",${extra_labels}
|
||||
},
|
||||
]
|
||||
forward_to = [loki.write.default.receiver]
|
||||
}
|
||||
EOF
|
||||
else
|
||||
debug "Skipping $path (file does not exist)"
|
||||
fi
|
||||
}
|
||||
|
||||
generate_alloy_config() {
|
||||
log "Generating Alloy config for $OS ($DETECTED_HOSTNAME)..."
|
||||
|
||||
local os_label
|
||||
case "$OS" in
|
||||
ubuntu|debian) os_label="ubuntu" ;;
|
||||
rhel|centos|rocky|almalinux|amzn) os_label="rhel-family" ;;
|
||||
*) os_label="$OS" ;;
|
||||
esac
|
||||
|
||||
local config=""
|
||||
|
||||
# Header
|
||||
config+="// Grafana Alloy Configuration for $DETECTED_HOSTNAME
|
||||
// Migrated from Promtail on $(date +%Y-%m-%d)
|
||||
// OS: $OS_PRETTY
|
||||
// Labels maintained for Promtail dashboard compatibility
|
||||
|
||||
logging {
|
||||
level = \"info\"
|
||||
}
|
||||
"
|
||||
|
||||
# Journal source
|
||||
if [[ "$INCLUDE_JOURNAL" == true ]]; then
|
||||
config+="
|
||||
// System logs via systemd journal
|
||||
loki.source.journal \"systemd_journal\" {
|
||||
max_age = \"12h\"
|
||||
labels = {
|
||||
job = \"systemd-journal\",
|
||||
host = \"$DETECTED_HOSTNAME\",
|
||||
os = \"$os_label\",
|
||||
}
|
||||
forward_to = [loki.relabel.journal_relabel.receiver]
|
||||
}
|
||||
|
||||
loki.relabel \"journal_relabel\" {
|
||||
forward_to = [loki.write.default.receiver]
|
||||
|
||||
rule {
|
||||
source_labels = [\"__journal__systemd_unit\"]
|
||||
target_label = \"unit\"
|
||||
}
|
||||
|
||||
rule {
|
||||
source_labels = [\"__journal_priority\"]
|
||||
target_label = \"priority\"
|
||||
}
|
||||
|
||||
rule {
|
||||
source_labels = [\"__journal__hostname\"]
|
||||
target_label = \"hostname\"
|
||||
}
|
||||
}
|
||||
"
|
||||
fi
|
||||
|
||||
# OS-specific file sources
|
||||
case "$OS" in
|
||||
ubuntu|debian)
|
||||
config+="
|
||||
// Ubuntu/Debian system logs"
|
||||
config+=$(generate_file_source "syslog" "/var/log/syslog" "messages" "
|
||||
\"os\" = \"ubuntu\",")
|
||||
config+=$(generate_file_source "auth" "/var/log/auth.log" "auth" "
|
||||
\"log_type\" = \"authentication\",")
|
||||
config+=$(generate_file_source "kern" "/var/log/kern.log" "kernel" "")
|
||||
config+=$(generate_file_source "cron" "/var/log/cron.log" "cron" "")
|
||||
config+=$(generate_file_source "mail" "/var/log/mail.log" "mail" "")
|
||||
config+=$(generate_file_source "apt" "/var/log/apt/history.log" "packages" "
|
||||
\"package_manager\" = \"apt\",")
|
||||
config+=$(generate_file_source "boot" "/var/log/boot.log" "boot" "")
|
||||
;;
|
||||
rhel|centos|rocky|almalinux|amzn)
|
||||
config+="
|
||||
// RHEL/CentOS/Rocky/Alma/Amazon Linux system logs"
|
||||
config+=$(generate_file_source "messages" "/var/log/messages" "messages" "
|
||||
\"os\" = \"rhel-family\",")
|
||||
config+=$(generate_file_source "secure" "/var/log/secure" "auth" "
|
||||
\"log_type\" = \"authentication\",")
|
||||
config+=$(generate_file_source "cron" "/var/log/cron" "cron" "")
|
||||
config+=$(generate_file_source "maillog" "/var/log/maillog" "mail" "")
|
||||
config+=$(generate_file_source "yum" "/var/log/yum.log" "packages" "
|
||||
\"package_manager\" = \"yum\",")
|
||||
config+=$(generate_file_source "boot" "/var/log/boot.log" "boot" "")
|
||||
;;
|
||||
*)
|
||||
config+="
|
||||
// Generic system logs"
|
||||
config+=$(generate_file_source "syslog" "/var/log/syslog" "messages" "")
|
||||
config+=$(generate_file_source "auth" "/var/log/auth.log" "auth" "
|
||||
\"log_type\" = \"authentication\",")
|
||||
;;
|
||||
esac
|
||||
|
||||
# Application wildcard
|
||||
config+=$(generate_file_source "application_logs" "/var/log/*.log" "application" "")
|
||||
|
||||
# Nginx
|
||||
if [[ "$INCLUDE_NGINX" == true ]]; then
|
||||
config+="
|
||||
// Nginx logs"
|
||||
config+=$(generate_file_source "nginx_access" "/var/log/nginx/access.log" "nginx" "
|
||||
\"log_type\" = \"access\",")
|
||||
config+=$(generate_file_source "nginx_error" "/var/log/nginx/error.log" "nginx" "
|
||||
\"log_type\" = \"error\",")
|
||||
fi
|
||||
|
||||
# Apache
|
||||
if [[ "$INCLUDE_APACHE" == true ]]; then
|
||||
config+="
|
||||
// Apache logs"
|
||||
config+=$(generate_file_source "apache_access" "/var/log/apache2/access.log" "apache" "
|
||||
\"log_type\" = \"access\",")
|
||||
config+=$(generate_file_source "apache_error" "/var/log/apache2/error.log" "apache" "
|
||||
\"log_type\" = \"error\",")
|
||||
config+=$(generate_file_source "httpd_access" "/var/log/httpd/access_log" "apache" "
|
||||
\"log_type\" = \"access\",")
|
||||
config+=$(generate_file_source "httpd_error" "/var/log/httpd/error_log" "apache" "
|
||||
\"log_type\" = \"error\",")
|
||||
fi
|
||||
|
||||
# Loki write endpoint
|
||||
config+="
|
||||
|
||||
// Write to Loki
|
||||
loki.write \"default\" {
|
||||
endpoint {
|
||||
url = \"${LOKI_URL}/loki/api/v1/push\"
|
||||
}
|
||||
}
|
||||
"
|
||||
|
||||
GENERATED_CONFIG="$config"
|
||||
}
|
||||
|
||||
write_config() {
|
||||
local output_file="$1"
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
log "DRY RUN: Would write config to $output_file"
|
||||
echo "--- Generated config.alloy ---"
|
||||
echo "$GENERATED_CONFIG"
|
||||
echo "--- End config ---"
|
||||
return
|
||||
fi
|
||||
|
||||
local output_dir
|
||||
output_dir=$(dirname "$output_file")
|
||||
mkdir -p "$output_dir"
|
||||
|
||||
# Backup existing config
|
||||
if [[ -f "$output_file" ]]; then
|
||||
local backup="${output_file}.bak.$(date +%Y%m%d%H%M%S)"
|
||||
cp "$output_file" "$backup"
|
||||
log "Backed up existing config to $backup"
|
||||
fi
|
||||
|
||||
echo "$GENERATED_CONFIG" > "$output_file"
|
||||
log "Alloy config written to $output_file"
|
||||
}
|
||||
|
||||
install_alloy() {
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
log "DRY RUN: Would install Grafana Alloy"
|
||||
return
|
||||
fi
|
||||
|
||||
# Check if already installed
|
||||
if command -v alloy >/dev/null 2>&1; then
|
||||
log "Alloy is already installed: $(alloy --version 2>&1 | head -1)"
|
||||
return
|
||||
fi
|
||||
|
||||
log "Installing Grafana Alloy..."
|
||||
|
||||
case "$OS" in
|
||||
ubuntu|debian)
|
||||
apt-get install -y apt-transport-https software-properties-common
|
||||
mkdir -p /etc/apt/keyrings/
|
||||
wget -q -O - https://apt.grafana.com/gpg.key | gpg --dearmor | tee /etc/apt/keyrings/grafana.gpg > /dev/null
|
||||
echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" | tee /etc/apt/sources.list.d/grafana.list
|
||||
apt-get update -qq
|
||||
apt-get install -y alloy
|
||||
;;
|
||||
rhel|centos|rocky|almalinux|amzn)
|
||||
cat > /etc/yum.repos.d/grafana.repo << 'REPO'
|
||||
[grafana]
|
||||
name=grafana
|
||||
baseurl=https://rpm.grafana.com
|
||||
repo_gpgcheck=1
|
||||
enabled=1
|
||||
gpgcheck=1
|
||||
gpgkey=https://rpm.grafana.com/gpg.key
|
||||
sslverify=1
|
||||
sslcacert=/etc/pki/tls/certs/ca-bundle.crt
|
||||
REPO
|
||||
if command -v dnf >/dev/null 2>&1; then
|
||||
dnf install -y alloy
|
||||
else
|
||||
yum install -y alloy
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
error "Unsupported OS for automatic installation: $OS"
|
||||
error "Install Alloy manually: https://grafana.com/docs/alloy/latest/set-up/install/"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
log "Alloy installed: $(alloy --version 2>&1 | head -1)"
|
||||
}
|
||||
|
||||
validate_config() {
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
log "DRY RUN: Would validate config with 'alloy fmt'"
|
||||
return
|
||||
fi
|
||||
|
||||
if ! command -v alloy >/dev/null 2>&1; then
|
||||
warn "Alloy not installed, skipping validation"
|
||||
return
|
||||
fi
|
||||
|
||||
log "Validating Alloy config..."
|
||||
if alloy fmt "$ALLOY_CONFIG_FILE" >/dev/null 2>&1; then
|
||||
log "Config validation passed"
|
||||
else
|
||||
error "Config validation failed. Check $ALLOY_CONFIG_FILE for syntax errors"
|
||||
error "Run: alloy fmt $ALLOY_CONFIG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
perform_cutover() {
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
log "DRY RUN: Would stop Promtail and start Alloy"
|
||||
return
|
||||
fi
|
||||
|
||||
# Stop Promtail
|
||||
if systemctl is-active --quiet promtail 2>/dev/null; then
|
||||
log "Stopping Promtail..."
|
||||
systemctl stop promtail
|
||||
systemctl disable promtail
|
||||
log "Promtail stopped and disabled"
|
||||
fi
|
||||
|
||||
# Add alloy user to required groups
|
||||
if getent group adm >/dev/null 2>&1; then
|
||||
usermod -a -G adm alloy 2>/dev/null || true
|
||||
fi
|
||||
if getent group systemd-journal >/dev/null 2>&1; then
|
||||
usermod -a -G systemd-journal alloy 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Start Alloy
|
||||
log "Starting Alloy..."
|
||||
systemctl enable --now alloy
|
||||
sleep 2
|
||||
|
||||
if systemctl is-active --quiet alloy; then
|
||||
log "Alloy is running"
|
||||
else
|
||||
error "Alloy failed to start. Check: journalctl -u alloy --no-pager -n 30"
|
||||
error "Rolling back — restarting Promtail"
|
||||
systemctl enable --now promtail 2>/dev/null || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Remove Promtail if requested
|
||||
if [[ "$KEEP_PROMTAIL" == false ]]; then
|
||||
log "Removing Promtail package..."
|
||||
case "$OS" in
|
||||
ubuntu|debian) apt-get remove -y promtail 2>/dev/null || true ;;
|
||||
*) yum remove -y promtail 2>/dev/null || dnf remove -y promtail 2>/dev/null || true ;;
|
||||
esac
|
||||
log "Promtail removed"
|
||||
else
|
||||
log "Promtail package kept (use 'systemctl start promtail' to rollback)"
|
||||
fi
|
||||
}
|
||||
|
||||
print_summary() {
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Migration Summary"
|
||||
echo "=========================================="
|
||||
echo " OS: $OS_PRETTY"
|
||||
echo " Hostname: $DETECTED_HOSTNAME"
|
||||
echo " Loki URL: $LOKI_URL"
|
||||
echo " Alloy config: $ALLOY_CONFIG_FILE"
|
||||
|
||||
if [[ "$DRY_RUN" != true ]] && [[ "$GENERATE_ONLY" != true ]]; then
|
||||
echo ""
|
||||
echo " Alloy status: $(systemctl is-active alloy 2>/dev/null || echo 'not checked')"
|
||||
echo ""
|
||||
echo " Verify:"
|
||||
echo " systemctl status alloy"
|
||||
echo " journalctl -u alloy -f"
|
||||
echo " curl http://localhost:12345 (Alloy UI)"
|
||||
echo ""
|
||||
echo " Rollback:"
|
||||
echo " sudo systemctl stop alloy"
|
||||
echo " sudo systemctl start promtail"
|
||||
fi
|
||||
|
||||
if [[ "$GENERATE_ONLY" == true ]]; then
|
||||
echo ""
|
||||
echo " Config generated. Review and deploy manually."
|
||||
fi
|
||||
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
}
|
||||
|
||||
main() {
|
||||
parse_arguments "$@"
|
||||
|
||||
log "Promtail → Alloy Migration Script v${SCRIPT_VERSION}"
|
||||
echo ""
|
||||
|
||||
# Check root (unless generate-only)
|
||||
if [[ "$GENERATE_ONLY" != true ]] && [[ "$DRY_RUN" != true ]] && [[ "$EUID" -ne 0 ]]; then
|
||||
error "This script must be run as root (or use --generate-only)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
detect_os
|
||||
detect_hostname
|
||||
detect_loki_url
|
||||
check_promtail_status
|
||||
|
||||
# Generate config
|
||||
generate_alloy_config
|
||||
write_config "$ALLOY_CONFIG_FILE"
|
||||
|
||||
if [[ "$GENERATE_ONLY" == true ]]; then
|
||||
print_summary
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Install Alloy
|
||||
if [[ "$SKIP_INSTALL" != true ]]; then
|
||||
install_alloy
|
||||
fi
|
||||
|
||||
# Validate
|
||||
validate_config
|
||||
|
||||
# Cutover
|
||||
if [[ "$SKIP_CUTOVER" != true ]]; then
|
||||
perform_cutover
|
||||
fi
|
||||
|
||||
print_summary
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user