316 lines
10 KiB
Go
316 lines
10 KiB
Go
package initfiles
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"regexp"
|
|
"strings"
|
|
"unicode"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
"github.com/truecharts/public/clustertool/pkg/helper"
|
|
)
|
|
|
|
func LoadTalEnv(noFail bool) error {
|
|
// Check if clusterenv.yaml file exists
|
|
if _, err := os.Stat(helper.ClusterPath + "/clusterenv.yaml"); err == nil {
|
|
// Load environment variables from clusterenv.yaml
|
|
err := helper.LoadEnvFromFile(helper.ClusterPath+"/clusterenv.yaml", helper.TalEnv)
|
|
if err != nil {
|
|
log.Info().Msgf("Error loading environment from clusterenv.yaml: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
} else if os.IsNotExist(err) {
|
|
// If the file doesn't exist, check noFail to determine next steps
|
|
if noFail {
|
|
log.Info().Msg("clusterenv.yaml file not found, but skipping due to noFail being true.")
|
|
return nil // Skip execution without error
|
|
} else {
|
|
log.Info().Msg("clusterenv.yaml file not found, exiting as noFail is false.")
|
|
os.Exit(1) // Exit with error code 1
|
|
}
|
|
} else {
|
|
log.Info().Msgf("Error checking clusterenv.yaml file: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// If file exists, continue with processing
|
|
clusterName()
|
|
checkQuotedNumbersInFile()
|
|
PostProcessTalEnv()
|
|
clusterEnvtoEnv()
|
|
log.Info().Msgf("ClusterEnv loaded successfully\n")
|
|
return nil
|
|
}
|
|
|
|
// Function to check if all numbers after ':' in a file are unquoted integers or floats
|
|
func checkQuotedNumbersInFile() (bool, error) {
|
|
|
|
filePath := helper.ClusterPath + "/clusterenv.yaml"
|
|
// Regular expression to find patterns like ': number' where number can be an int or float
|
|
re := regexp.MustCompile(`:\s*(.+)`) // Matches anything after ': '
|
|
|
|
// Open the file
|
|
file, err := os.Open(filePath)
|
|
if err != nil {
|
|
log.Error().Msgf("Failed to open file: %s \nError: %s", filePath, err)
|
|
os.Exit(1)
|
|
return false, err
|
|
}
|
|
defer file.Close()
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
|
|
// Skip lines that start with a number
|
|
trimmedLine := strings.TrimSpace(line)
|
|
if len(trimmedLine) > 0 && unicode.IsDigit(rune(trimmedLine[0])) {
|
|
continue
|
|
}
|
|
|
|
// Find matches for entries in each line
|
|
matches := re.FindStringSubmatch(line)
|
|
if len(matches) < 2 {
|
|
continue // Skip lines without a colon and value
|
|
}
|
|
|
|
// Get the value after the colon
|
|
value := strings.TrimSpace(matches[1])
|
|
|
|
// Check if the value is a valid number (int or float with a single dot)
|
|
isValidNumber := regexp.MustCompile(`^[0-9]+(\.[0-9]+)?$`).MatchString(value)
|
|
|
|
// If it's a valid number, log an error
|
|
if isValidNumber {
|
|
log.Error().Msgf("Unquoted number found %s line: %s", filePath, line)
|
|
os.Exit(1)
|
|
return false, nil
|
|
}
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
log.Error().Msgf("Error scanning the file: %s", err)
|
|
os.Exit(1)
|
|
return false, err
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func clusterName() {
|
|
helper.TalEnv["CLUSTERNAME"] = helper.ClusterName
|
|
}
|
|
|
|
func clusterEnvtoEnv() {
|
|
// Split IP/NETMASK and normalize IPs
|
|
for key, value := range helper.TalEnv {
|
|
os.Setenv(key, value)
|
|
}
|
|
}
|
|
func PostProcessTalEnv() {
|
|
// Split IP/NETMASK and normalize IPs
|
|
for key, value := range helper.TalEnv {
|
|
ip, netmask, err := splitIPandNetmask(value)
|
|
if err == nil {
|
|
// Update TalEnv with IP and NETMASK entries
|
|
helper.TalEnv[key+"_IP"] = ip
|
|
helper.TalEnv[key+"_NETMASK"] = netmask
|
|
helper.TalEnv[key+"_CIDR"] = ip + "/" + netmask
|
|
}
|
|
}
|
|
|
|
// Validate and normalize specific IP variables
|
|
ValidateAndNormalizeIPsInTalEnv()
|
|
|
|
// Validate and normalize IP/NETMASK variables
|
|
ValidateAndNormalizeIPNetmaskVarsInTalEnv()
|
|
}
|
|
|
|
func splitIPandNetmask(ipWithMask string) (string, string, error) {
|
|
// Check if IP/NETMASK format
|
|
parts := strings.Split(ipWithMask, "/")
|
|
if len(parts) == 2 {
|
|
ip := parts[0]
|
|
netmask := parts[1]
|
|
// Validate netmask format (you might want to add more rigorous validation)
|
|
if _, _, err := net.ParseCIDR(ipWithMask); err != nil {
|
|
return "", "", fmt.Errorf("invalid IP/NETMASK format: %s", ipWithMask)
|
|
}
|
|
return ip, netmask, nil
|
|
}
|
|
|
|
// Assume NETMASK 24 if only IP provided
|
|
ip := ipWithMask
|
|
netmask := "24"
|
|
// Validate IP format (you might want to add more rigorous validation)
|
|
if net.ParseIP(ip) == nil {
|
|
return "", "", fmt.Errorf("invalid IP format: %s", ipWithMask)
|
|
}
|
|
return ip, netmask, nil
|
|
}
|
|
|
|
func ValidateAndNormalizeIPsInTalEnv() {
|
|
ipVariables := []string{"Master1IP"}
|
|
|
|
for _, key := range ipVariables {
|
|
value, exists := helper.TalEnv[key]
|
|
if !exists {
|
|
continue // Skip if the variable doesn't exist in TalEnv
|
|
}
|
|
|
|
ip, err := normalizeIP(value)
|
|
if err != nil {
|
|
log.Info().Msgf("Error processing %s: %v\n", key, err)
|
|
continue
|
|
}
|
|
|
|
// Update TalEnv with normalized IP value
|
|
helper.TalEnv[key] = ip
|
|
}
|
|
}
|
|
|
|
func normalizeIP(ipWithMask string) (string, error) {
|
|
// Check if IP/NETMASK format
|
|
parts := strings.Split(ipWithMask, "/")
|
|
if len(parts) == 2 {
|
|
ip := parts[0]
|
|
netmask := parts[1]
|
|
// Validate netmask format (you might want to add more rigorous validation)
|
|
if _, _, err := net.ParseCIDR(ipWithMask); err != nil {
|
|
return "", fmt.Errorf("invalid IP/NETMASK format: %s", ipWithMask)
|
|
}
|
|
return ip + "/" + netmask, nil
|
|
}
|
|
|
|
// Assume NETMASK 24 if only IP provided
|
|
ip := ipWithMask
|
|
// Validate IP format (you might want to add more rigorous validation)
|
|
if net.ParseIP(ip) == nil {
|
|
return "", fmt.Errorf("invalid IP format: %s", ipWithMask)
|
|
}
|
|
return ip + "/24", nil // Default to /24 subnet mask
|
|
}
|
|
|
|
func ValidateAndNormalizeIPNetmaskVarsInTalEnv() {
|
|
netmaskVariables := []string{"PODNET", "SVCNET"}
|
|
|
|
for _, key := range netmaskVariables {
|
|
value, exists := helper.TalEnv[key]
|
|
if !exists {
|
|
continue // Skip if the variable doesn't exist in TalEnv
|
|
}
|
|
|
|
ipNetmask, err := normalizeIPNetmask(value)
|
|
if err != nil {
|
|
log.Info().Msgf("Error processing %s: %v\n", key, err)
|
|
continue
|
|
}
|
|
|
|
// Update TalEnv with normalized IP/NETMASK value
|
|
helper.TalEnv[key] = ipNetmask
|
|
}
|
|
}
|
|
|
|
func normalizeIPNetmask(ipNetmask string) (string, error) {
|
|
// Validate IP/NETMASK format
|
|
if _, _, err := net.ParseCIDR(ipNetmask); err != nil {
|
|
return "", fmt.Errorf("invalid IP/NETMASK format: %s", ipNetmask)
|
|
}
|
|
return ipNetmask, nil
|
|
}
|
|
|
|
func CheckEnvVariables() {
|
|
LoadTalEnv(false)
|
|
requiredKeys := []string{
|
|
"VIP",
|
|
"MASTER1IP_IP",
|
|
"MASTER1IP_NETMASK",
|
|
"GATEWAY",
|
|
"METALLB_RANGE",
|
|
"PODNET",
|
|
"SVCNET",
|
|
}
|
|
for _, key := range requiredKeys {
|
|
if helper.TalEnv[key] == "" {
|
|
log.Info().Msgf("%s cannot be empty\n", key)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
// Validate VIP and MASTER1IP format and check subnet compatibility
|
|
vip := helper.TalEnv["VIP_IP"]
|
|
master1ip := helper.TalEnv["MASTER1IP_IP"]
|
|
master1ipCidr := helper.TalEnv["MASTER1IP_CIDR"]
|
|
gateway := helper.TalEnv["GATEWAY"]
|
|
|
|
// Check if MASTER1IP matches GATEWAY or VIP
|
|
if master1ip == gateway || master1ip == vip {
|
|
log.Info().Msg("Cannot proceed, MASTER1IP cannot match GATEWAY or VIP")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Check if VIP matches any Node IPs
|
|
if vip == master1ip {
|
|
log.Info().Msg("Cannot proceed, VIP cannot match any Node IPs")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Check ranges against METALLB_RANGE
|
|
inRange, err := helper.IPInRange(vip, helper.TalEnv["METALLB_RANGE"])
|
|
if err != nil {
|
|
log.Info().Msgf("Error checking VIP against METALLB_RANGE: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
if inRange {
|
|
log.Info().Msg("Cannot proceed, VIP cannot be in the METALLB_RANGE")
|
|
os.Exit(1)
|
|
}
|
|
|
|
inRange, err = helper.IPInRange(master1ip, helper.TalEnv["METALLB_RANGE"])
|
|
if err != nil {
|
|
log.Info().Msgf("Error checking MASTER1IP against METALLB_RANGE: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
if inRange {
|
|
log.Info().Msg("Cannot proceed, MASTER1IP cannot be in the METALLB_RANGE")
|
|
os.Exit(1)
|
|
}
|
|
|
|
inRange, err = helper.IPInRange(gateway, helper.TalEnv["METALLB_RANGE"])
|
|
if err != nil {
|
|
log.Info().Msgf("Error checking GATEWAY against METALLB_RANGE: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
if inRange {
|
|
log.Info().Msg("Cannot proceed, GATEWAY cannot be in the METALLB_RANGE")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Check DASHBOARD_IP against METALLB_RANGE
|
|
if helper.TalEnv["DASHBOARD_IP"] != "" {
|
|
inRange, err = helper.IPInRange(helper.TalEnv["DASHBOARD_IP"], helper.TalEnv["METALLB_RANGE"])
|
|
if err != nil {
|
|
log.Info().Msgf("Error checking DASHBOARD_IP against METALLB_RANGE: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
if !inRange {
|
|
log.Info().Msg("Cannot proceed, DASHBOARD_IP must be in the METALLB_RANGE")
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
// Validate other CIDR/IP checks with new netmask support
|
|
helper.ValidateIPorCIDRNotInCIDR(vip+"/32", helper.TalEnv["PODNET"], "VIP", "PODNET")
|
|
helper.ValidateIPorCIDRNotInCIDR(master1ipCidr, helper.TalEnv["PODNET"], "MASTER1IP", "PODNET")
|
|
helper.ValidateIPorCIDRNotInCIDR(gateway+"/32", helper.TalEnv["PODNET"], "GATEWAY", "PODNET")
|
|
helper.ValidateRangeNotInCIDR(helper.TalEnv["METALLB_RANGE"], helper.TalEnv["PODNET"], "METALLB_RANGE", "PODNET")
|
|
|
|
helper.ValidateIPorCIDRNotInCIDR(vip+"/32", helper.TalEnv["SVCNET"], "VIP", "SVCNET")
|
|
helper.ValidateIPorCIDRNotInCIDR(master1ipCidr, helper.TalEnv["SVCNET"], "MASTER1IP", "SVCNET")
|
|
helper.ValidateIPorCIDRNotInCIDR(gateway+"/32", helper.TalEnv["SVCNET"], "GATEWAY", "SVCNET")
|
|
helper.ValidateRangeNotInCIDR(helper.TalEnv["METALLB_RANGE"], helper.TalEnv["SVCNET"], "METALLB_RANGE", "SVCNET")
|
|
}
|