mirror of
https://github.com/kubesphere/kubekey.git
synced 2025-12-26 09:32:52 +00:00
229 lines
7.5 KiB
Go
229 lines
7.5 KiB
Go
package variable
|
|
|
|
import (
|
|
"net"
|
|
"regexp"
|
|
"slices"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/cockroachdb/errors"
|
|
"k8s.io/apimachinery/pkg/util/rand"
|
|
"k8s.io/klog/v2"
|
|
|
|
_const "github.com/kubesphere/kubekey/v4/pkg/const"
|
|
"github.com/kubesphere/kubekey/v4/pkg/converter/tmpl"
|
|
)
|
|
|
|
// ***************************** GetFunc ***************************** //
|
|
|
|
// GetHostnames retrieves all hostnames from specified groups or hosts.
|
|
// It supports various hostname patterns including direct hostnames, group names,
|
|
// indexed group access (e.g., "group[0]"), and random selection (e.g., "group|random").
|
|
// The function also supports template parsing for hostnames using configuration variables.
|
|
var GetHostnames = func(name []string) GetFunc {
|
|
if len(name) == 0 {
|
|
return emptyGetFunc
|
|
}
|
|
|
|
return func(v Variable) (any, error) {
|
|
vv, ok := v.(*variable)
|
|
if !ok {
|
|
return nil, errors.New("variable type error")
|
|
}
|
|
var hs []string
|
|
for _, n := range name {
|
|
// Try to parse hostname using configuration variables as template context
|
|
if pn, err := tmpl.ParseFunc(Extension2Variables(vv.value.Config.Spec), n, func(b []byte) string { return string(b) }); err == nil {
|
|
n = pn
|
|
}
|
|
// Add direct hostname if it exists in the hosts map
|
|
if _, exists := vv.value.Hosts[n]; exists {
|
|
hs = append(hs, n)
|
|
}
|
|
// Add all hosts from matching groups
|
|
for gn, gv := range ConvertGroup(vv.value.Inventory) {
|
|
if gn == n {
|
|
hs = CombineSlice(hs, gv)
|
|
break
|
|
}
|
|
}
|
|
|
|
// Handle indexed group access (e.g., "group[0]")
|
|
regexForIndex := regexp.MustCompile(`^(.*?)\[(\d+)\]$`)
|
|
if match := regexForIndex.FindStringSubmatch(strings.TrimSpace(n)); match != nil {
|
|
index, err := strconv.Atoi(match[2])
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "failed to convert %q to int", match[2])
|
|
}
|
|
if group, ok := ConvertGroup(vv.value.Inventory)[match[1]]; ok {
|
|
if index >= len(group) {
|
|
return nil, errors.Errorf("index %v out of range for group %s", index, group)
|
|
}
|
|
hs = append(hs, group[index])
|
|
}
|
|
}
|
|
|
|
// Handle random host selection from group (e.g., "group|random")
|
|
regexForRandom := regexp.MustCompile(`^(.+?)\s*\|\s*random$`)
|
|
if match := regexForRandom.FindStringSubmatch(strings.TrimSpace(n)); match != nil {
|
|
if group, ok := ConvertGroup(vv.value.Inventory)[match[1]]; ok {
|
|
hs = append(hs, group[rand.Intn(len(group))])
|
|
}
|
|
}
|
|
}
|
|
|
|
return hs, nil
|
|
}
|
|
}
|
|
|
|
// GetAllVariable retrieves all variables for a given host, including group variables,
|
|
// remote variables, runtime variables, inventory variables, and configuration variables.
|
|
// It also sets default variables for localhost and provides access to global host and group information.
|
|
var GetAllVariable = func(hostname string) GetFunc {
|
|
// getLocalIP retrieves the IPv4 or IPv6 address for the localhost machine
|
|
getLocalIP := func(ipType string) string {
|
|
addrs, err := net.InterfaceAddrs()
|
|
if err != nil {
|
|
klog.ErrorS(err, "get network address error")
|
|
}
|
|
|
|
for _, addr := range addrs {
|
|
if ipNet, ok := addr.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
|
|
if ipType == _const.VariableIPv4 && ipNet.IP.To4() != nil {
|
|
return ipNet.IP.String()
|
|
}
|
|
|
|
if ipType == _const.VariableIPv6 && ipNet.IP.To16() != nil && ipNet.IP.To4() == nil {
|
|
return ipNet.IP.String()
|
|
}
|
|
}
|
|
}
|
|
|
|
klog.V(4).Infof("cannot get local %s address", ipType)
|
|
|
|
return ""
|
|
}
|
|
|
|
// defaultHostVariable sets default variables when hostname is "localhost"
|
|
// It automatically detects and sets IPv4/IPv6 addresses and hostname information
|
|
defaultHostVariable := func(hostname string, hostVars map[string]any) {
|
|
if hostname == _const.VariableLocalHost {
|
|
if _, ok := hostVars[_const.VariableIPv4]; !ok {
|
|
hostVars[_const.VariableIPv4] = getLocalIP(_const.VariableIPv4)
|
|
}
|
|
if _, ok := hostVars[_const.VariableIPv6]; !ok {
|
|
hostVars[_const.VariableIPv6] = getLocalIP(_const.VariableIPv6)
|
|
}
|
|
}
|
|
if os, ok := hostVars[_const.VariableOS]; ok {
|
|
// Try to set hostname by current actual hostname from OS information
|
|
if osd, ok := os.(map[string]any); ok {
|
|
hostVars[_const.VariableHostName] = osd[_const.VariableOSHostName]
|
|
}
|
|
}
|
|
if _, ok := hostVars[_const.VariableInventoryName]; !ok {
|
|
hostVars[_const.VariableInventoryName] = hostname
|
|
}
|
|
if _, ok := hostVars[_const.VariableHostName]; !ok {
|
|
hostVars[_const.VariableHostName] = hostname
|
|
}
|
|
}
|
|
|
|
// getHostsVariable builds a complete variable map for all hosts
|
|
// by combining variables from multiple sources in a specific order
|
|
getHostsVariable := func(v *variable) map[string]any {
|
|
globalHosts := make(map[string]any)
|
|
for hostname := range v.value.Hosts {
|
|
hostVars := make(map[string]any)
|
|
// Set group variables for hosts that belong to groups
|
|
for _, gv := range v.value.Inventory.Spec.Groups {
|
|
if slices.Contains(gv.Hosts, hostname) {
|
|
hostVars = CombineVariables(hostVars, Extension2Variables(gv.Vars))
|
|
}
|
|
}
|
|
// Merge remote variables (variables collected from the actual host)
|
|
hostVars = CombineVariables(hostVars, v.value.Hosts[hostname].RemoteVars)
|
|
// Merge runtime variables (variables set during playbook execution)
|
|
hostVars = CombineVariables(hostVars, v.value.Hosts[hostname].RuntimeVars)
|
|
|
|
// Merge inventory-level variables
|
|
hostVars = CombineVariables(hostVars, Extension2Variables(v.value.Inventory.Spec.Vars))
|
|
// Merge host-specific variables from inventory
|
|
hostVars = CombineVariables(hostVars, Extension2Variables(v.value.Inventory.Spec.Hosts[hostname]))
|
|
// Merge configuration variables
|
|
hostVars = CombineVariables(hostVars, Extension2Variables(v.value.Config.Spec))
|
|
// Set default variables for localhost
|
|
defaultHostVariable(hostname, hostVars)
|
|
globalHosts[hostname] = hostVars
|
|
}
|
|
|
|
return globalHosts
|
|
}
|
|
|
|
return func(v Variable) (any, error) {
|
|
vv, ok := v.(*variable)
|
|
if !ok {
|
|
return nil, errors.New("variable type error")
|
|
}
|
|
hosts := getHostsVariable(vv)
|
|
hostVars, ok := hosts[hostname].(map[string]any)
|
|
if !ok {
|
|
// Return empty map if host variables cannot be found
|
|
return make(map[string]any), nil
|
|
}
|
|
// Add global hosts information to the host variables
|
|
hostVars = CombineVariables(hostVars, map[string]any{
|
|
_const.VariableGlobalHosts: hosts,
|
|
})
|
|
// Add group information to the host variables
|
|
hostVars = CombineVariables(hostVars, map[string]any{
|
|
_const.VariableGroups: ConvertGroup(vv.value.Inventory),
|
|
})
|
|
|
|
return hostVars, nil
|
|
}
|
|
}
|
|
|
|
// GetHostMaxLength calculates the maximum length of all hostnames.
|
|
// This is useful for formatting output or determining display widths.
|
|
var GetHostMaxLength = func() GetFunc {
|
|
return func(v Variable) (any, error) {
|
|
vv, ok := v.(*variable)
|
|
if !ok {
|
|
return nil, errors.New("variable type error")
|
|
}
|
|
var hostnameMaxLen int
|
|
for k := range vv.value.Hosts {
|
|
hostnameMaxLen = max(len(k), hostnameMaxLen)
|
|
}
|
|
|
|
return hostnameMaxLen, nil
|
|
}
|
|
}
|
|
|
|
// GetWorkDir returns the working directory from the configuration.
|
|
var GetWorkDir = func() GetFunc {
|
|
return func(v Variable) (any, error) {
|
|
vv, ok := v.(*variable)
|
|
if !ok {
|
|
return nil, errors.New("variable type error")
|
|
}
|
|
|
|
return _const.GetWorkdirFromConfig(vv.value.Config), nil
|
|
}
|
|
}
|
|
|
|
// GetResultVariable returns the global result variables.
|
|
// This function retrieves the result variables that are set globally and accessible across all hosts.
|
|
var GetResultVariable = func() GetFunc {
|
|
return func(v Variable) (any, error) {
|
|
vv, ok := v.(*variable)
|
|
if !ok {
|
|
return nil, errors.New("variable type error")
|
|
}
|
|
|
|
return vv.value.Result[resultKey], nil
|
|
}
|
|
}
|