296 lines
9.9 KiB
Bash
296 lines
9.9 KiB
Bash
#!/bin/bash
|
|
|
|
set -e
|
|
|
|
CONFIG_FILE="${1:-config.yaml}"
|
|
STYLE_SETTING1="--width 80 --cursor.foreground 220 --prompt.foreground 220"
|
|
STYLE_SETTING2="--width 80 --border-foreground 220"
|
|
|
|
if [[ "$EUID" -ne 0 ]]; then
|
|
echo -e "\e[31m[FATAL]\e[39m Currently this script requires being ran as root user - please try again as root."
|
|
exit 1
|
|
fi
|
|
|
|
echo -e "\n\nINSTALL LOG: $(date --rfc-3339=seconds)\n" >> /tmp/multiplane.log
|
|
|
|
info() {
|
|
echo -e "\e[34m[INFO]\e[39m $1"
|
|
echo "[INFO] $1" >> /tmp/multiplane.log
|
|
}
|
|
|
|
debug() {
|
|
if [[ ! -z "$DEBUG" ]]; then
|
|
echo -e "\e[96m[DEBUG]\e[39m $1"
|
|
fi
|
|
echo "[DEBUG] $1" >> /tmp/multiplane.log
|
|
}
|
|
|
|
warn() {
|
|
echo -e "\e[33m[WARNING]\e[39m $1"
|
|
echo "[WARNING] $1" >> /tmp/multiplane.log
|
|
}
|
|
|
|
fatal() {
|
|
echo -e "\e[31m[FATAL]\e[39m $1"
|
|
echo "[FATAL] $1" >> /tmp/multiplane.log
|
|
exit 1
|
|
}
|
|
|
|
# Function to validate MAC address
|
|
validate_mac() {
|
|
[[ $1 =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]
|
|
}
|
|
|
|
# Function to validate IPv4 address
|
|
validate_ipv4() {
|
|
[[ $1 =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] && {
|
|
IFS='.' read -ra octets <<< "$1"
|
|
for octet in "${octets[@]}"; do
|
|
((octet > 255)) && return 1
|
|
done
|
|
return 0
|
|
}
|
|
return 1
|
|
}
|
|
|
|
# Function to get and validate MAC address
|
|
get_mac_address() {
|
|
local io="${1:-}"
|
|
local mac
|
|
|
|
while true; do
|
|
if [[ -z "$io" ]]; then
|
|
mac=$(gum input --header "MAC Address (e.g., BC:24:AC:76:96:DE)" --placeholder "XX:XX:XX:XX:XX:XX" $STYLE_SETTING1) || exit 130
|
|
else
|
|
if [[ -z "$(yq $io $CONFIG_FILE)" ]]; then
|
|
mac=$(gum input --header "MAC Address (e.g., BC:24:AC:76:96:DE)" --placeholder "XX:XX:XX:XX:XX:XX" $STYLE_SETTING1) || exit 130
|
|
else
|
|
mac=$(gum input --header "MAC Address (e.g., BC:24:AC:76:96:DE)" --placeholder "XX:XX:XX:XX:XX:XX" $STYLE_SETTING1 --value "$(yq $io $CONFIG_FILE)") && yq -i -oy "$io = \"$mac\"" "$CONFIG_FILE" || exit 130
|
|
fi
|
|
fi
|
|
|
|
if validate_mac "$mac"; then
|
|
echo "$mac"
|
|
break
|
|
else
|
|
gum style --foreground 196 "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX"; sleep 0.5
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Function to get and validate IPv4 address
|
|
get_ipv4_address() {
|
|
local io="${1:-}"
|
|
local ip
|
|
|
|
while true; do
|
|
if [[ -z "$io" ]]; then
|
|
ip=$(gum input --header "IPv4 Address (e.g., 10.1.0.14)" --placeholder "X.X.X.X" $STYLE_SETTING1) || exit 130
|
|
else
|
|
if [[ -z "$(yq $io $CONFIG_FILE)" ]]; then
|
|
ip=$(gum input --header "IPv4 Address (e.g., 10.1.0.14)" --placeholder "X.X.X.X" $STYLE_SETTING1) || exit 130
|
|
else
|
|
ip=$(gum input --header "IPv4 Address (e.g., 10.1.0.14)" --placeholder "X.X.X.X" $STYLE_SETTING1 --value "$(yq $io $CONFIG_FILE)") && yq -i -oy "$io = \"$ip\"" "$CONFIG_FILE" || exit 130
|
|
fi
|
|
fi
|
|
|
|
if validate_ipv4 "$ip"; then
|
|
echo "$ip"
|
|
break
|
|
else
|
|
gum style --foreground 196 "Invalid IPv4 address format"; sleep 0.5
|
|
fi
|
|
done
|
|
}
|
|
|
|
config_setup() {
|
|
|
|
clear; HOSTNAME=$(gum input --header "Enter a cluster hostname. (e.g., cluster.local)" --placeholder "cluster.local" --prompt "* " $STYLE_SETTING1 --value "$(yq '.k0s.hostname' $CONFIG_FILE)") && yq -i -oy ".k0s.hostname = \"$HOSTNAME\"" "$CONFIG_FILE" || exit 0
|
|
|
|
clear; PRIVATE_IP_RANGE=$(gum input --header "Enter a cluster IP subnet. (e.g., 10.1.0.0/24)" --placeholder "10.1.0.0/24" --prompt "* " $STYLE_SETTING1 --value "$(yq '.k0s.private_ip_range' $CONFIG_FILE)") && yq -i -oy ".k0s.private_ip_range = \"$PRIVATE_IP_RANGE\"" "$CONFIG_FILE" || exit 0
|
|
|
|
clear; PUBLIC_IP_RANGE=$(gum input --header "Enter a public IP subnet. (e.g., 192.168.0.0/24" --placeholder "192.168.0.0/24" --prompt "* " $STYLE_SETTING1 --value "$(yq '.k0s.public_ip_range' $CONFIG_FILE)") && yq -i -oy ".k0s.public_ip_range = \"$PUBLIC_IP_RANGE\"" "$CONFIG_FILE" || exit 0
|
|
|
|
# Initialize array to store entries
|
|
declare -a entries
|
|
|
|
# Proxy node entry (2 nodes required)
|
|
clear; gum style --border double --padding "1 2" $STYLE_SETTING2 "Proxy Configuration (2 Required)"; gum spin --spinner dot --spinner.foreground 220 --title "Waiting..." -- sleep 1
|
|
|
|
trap 'clear; echo -e "\nExiting..."; exit 130' INT
|
|
for i in 1 2; do
|
|
clear; gum style --border double --padding "1 2" $STYLE_SETTING2 "Proxy Node $i/2"
|
|
|
|
# Get MAC address
|
|
mac=$(get_mac_address ".haproxy.proxy.$i.mac") || exit 130
|
|
|
|
# Get IPv4 address
|
|
ip=$(get_ipv4_address ".haproxy.proxy.$i.ip") || exit 130
|
|
|
|
# Add entry using yq
|
|
yq -i -oy ".haproxy.proxy.$i = {\"mac\": \"$mac\", \"ip\": \"$ip\"}" "$CONFIG_FILE"
|
|
done
|
|
trap - INT
|
|
|
|
clear; HAPROXY_KUBE_VIP=$(gum input --header "HAProxy Kuberenetes VIP. (e.g., 10.1.0.10)" --placeholder "10.1.0.10" --prompt "* " $STYLE_SETTING1 --value "$(yq '.haproxy.kube.vip' $CONFIG_FILE)") && yq -i -oy ".haproxy.kube.vip = \"$HAPROXY_KUBE_VIP\"" "$CONFIG_FILE" || exit 0
|
|
|
|
# Controller node entry (3 nodes required)
|
|
clear; gum style --border double --padding "1 2" $STYLE_SETTING2 "Controller Node Configuration (3 Required)"; gum spin --spinner dot --spinner.foreground 220 --title "Waiting..." -- sleep 1
|
|
|
|
trap 'clear; echo -e "\nExiting..."; exit 130' INT
|
|
for i in 1 2 3; do
|
|
clear; gum style --border double --padding "1 2" $STYLE_SETTING2 "Controller Node $i/3"
|
|
|
|
# Get MAC address
|
|
mac=$(get_mac_address ".nodes.controller.$i.mac") || exit 130
|
|
|
|
# Get IPv4 address
|
|
ip=$(get_ipv4_address ".nodes.controller.$i.ip") || exit 130
|
|
|
|
# Add entry using yq
|
|
yq -i -oy ".nodes.controller.$i = {\"mac\": \"$mac\", \"ip\": \"$ip\"}" "$CONFIG_FILE"
|
|
done
|
|
trap - INT
|
|
|
|
# Controller node entry (3 nodes required)
|
|
clear; gum style --border double --padding "1 2" $STYLE_SETTING2 "Worker Node Configuration (3 Required)"; gum spin --spinner dot --spinner.foreground 220 --title "Waiting..." -- sleep 1
|
|
|
|
# Add worker node loop
|
|
count=1
|
|
trap 'clear; echo -e "\nExiting..."; exit 130' INT
|
|
while [ $count -lt 250 ]; do
|
|
# Ask to continue
|
|
if [ $count -gt 3 ]; then
|
|
clear; gum confirm "Add another worker node?" || break
|
|
fi
|
|
|
|
clear; gum style --border double --padding "1 2" $STYLE_SETTING2 "Worker Node Entry ($((count))/250)"
|
|
|
|
# Get MAC address
|
|
mac=$(get_mac_address ".nodes.worker.$count.mac") || exit 130
|
|
|
|
# Get IPv4 address
|
|
ip=$(get_ipv4_address ".nodes.worker.$count.ip") || exit 130
|
|
|
|
# Add entry using yq
|
|
yq -i -oy ".nodes.worker.$count = {\"mac\": \"$mac\", \"ip\": \"$ip\"}" "$CONFIG_FILE"
|
|
|
|
count=$((count + 1))
|
|
done
|
|
trap - INT
|
|
|
|
gum confirm "Check configuration file?" && gum pager < $CONFIG_FILE
|
|
|
|
clear; gum style --border double --padding "1 2" $STYLE_SETTING2 "✓ Configuration saved to $CONFIG_FILE"
|
|
|
|
}
|
|
|
|
swab_the_decks() {
|
|
exit 0
|
|
}
|
|
|
|
upgrades_everyone_upgrades() {
|
|
exit 0
|
|
}
|
|
|
|
backup_k0s() {
|
|
|
|
if ! mkdir -p /opt/k0s/backups 2>/dev/null; then
|
|
fatal "Error: Failed to create directory /opt/k0s/backups"
|
|
exit 1
|
|
fi
|
|
|
|
gum spin --spinner dot --spinner.foreground 220 --title "Waiting..." -- k0sctl backup --save-path=/opt/k0s/backups && info "Backup success!" || fatal "Backup of k0s failed!"
|
|
|
|
}
|
|
|
|
restore_k0s() {
|
|
|
|
gum spin --spinner dot --spinner.foreground 220 --title "Waiting..." -- k0sctl apply --restore-from $1 && info "Restore success!" || fatal "Restore of k0s failed!"
|
|
|
|
}
|
|
|
|
your_good_your_good_your_good_and_stop() {
|
|
|
|
gum pager <<EOF
|
|
Parts NOT covered by the k0s backup utility:
|
|
|
|
- PersistentVolumes of any running application
|
|
- data store, in case something else than etcd or Kine/SQLite is used
|
|
- any configuration to the cluster introduced by manual changes (e.g. changes that weren't saved under the <data-dir>/manifests)
|
|
|
|
The backups created by k0s backup command have following pieces of your cluster:
|
|
|
|
- certificates (the content of the <data-dir>/pki directory)
|
|
- etcd snapshot, if the etcd data store is used
|
|
- Kine/SQLite snapshot, if the Kine/SQLite data store is used
|
|
- k0s.yaml
|
|
- any custom defined manifests under the <data-dir>/manifests
|
|
- any image bundles located under the <data-dir>/images
|
|
- any helm configuration
|
|
EOF
|
|
|
|
gum confirm "Start Backup Task?" && backup_k0s || clear; echo "Exiting..."; exit 0
|
|
|
|
}
|
|
|
|
thatll_buff_right_out_captin() {
|
|
|
|
if [ ! -d "/opt/k0s/backups" ]; then
|
|
fatal "Error: directory /opt/k0s/backups does exist"
|
|
exit 1
|
|
fi
|
|
|
|
# Check if directory is empty
|
|
if [ -z "$(ls -A "/opt/k0s/backups" 2>/dev/null)" ]; then
|
|
fatal "Directory /opt/k0s/backups is empty. Exiting..."
|
|
exit 1
|
|
fi
|
|
|
|
clear;
|
|
|
|
file=$(find /opt/k0s/backups -maxdepth 1 -type f -printf "%f\n" | gum filter --limit 1 --placeholder "Select a backup file...")
|
|
|
|
if [ -n "$file" ]; then
|
|
full_path="/opt/k0s/backups/$file"
|
|
fi
|
|
|
|
clear; gum confirm "Start Backup Task?" && restore_k0s $full_path || clear; echo "Exiting..."; exit 0
|
|
|
|
}
|
|
|
|
main() {
|
|
|
|
# Choices List
|
|
CHOICE=$(gum choose --header "Select a function to run:" --cursor "> " --height 10 --cursor.foreground 220 --header.foreground 220 "Configuration Setup" "Run Maintenance Tasks" "Run Cluster Upgrade" "Backup Cluster Configuration" "Restore Cluster Configuration" "Exit")
|
|
|
|
# Execute based on selection
|
|
case "$CHOICE" in
|
|
"Configuration Setup")
|
|
config_setup
|
|
;;
|
|
"Run Maintenance Tasks")
|
|
swab_the_decks
|
|
;;
|
|
"Run Cluster Upgrade")
|
|
upgrades_everyone_upgrades
|
|
;;
|
|
"Backup Cluster Configuration")
|
|
your_good_your_good_your_good_and_stop
|
|
;;
|
|
"Restore Cluster Configuration")
|
|
thatll_buff_right_out_captin
|
|
;;
|
|
"Exit")
|
|
echo "Exiting..."
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "No selection made"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
}
|
|
|
|
main |