kubekey/pkg/util/util.go
2020-05-26 22:25:05 +08:00

364 lines
7.8 KiB
Go

/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"bytes"
"encoding/binary"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"net"
"os"
"os/exec"
"os/user"
"runtime"
"strconv"
"strings"
"text/template"
)
const (
VERSION = "KubeKey Version v1.0.0-dev\nKubernetes Version v1.17.6\nKubeSphere Version 3.0.0"
VersionBig = 1
VersionSmall = 2
VersionEqual = 0
)
func InitLogger(verbose bool) *log.Logger {
logger := log.New()
logger.Formatter = &log.TextFormatter{
FullTimestamp: true,
TimestampFormat: "15:04:05 MST",
}
if verbose {
logger.SetLevel(log.DebugLevel)
}
return logger
}
func IsExist(path string) bool {
_, err := os.Stat(path)
if err != nil {
if os.IsExist(err) {
return true
}
if os.IsNotExist(err) {
return false
}
return false
}
return true
}
func CreateDir(path string) error {
if IsExist(path) == false {
err := os.MkdirAll(path, os.ModePerm)
if err != nil {
return err
}
}
return nil
}
type Data map[string]interface{}
// Render text template with given `variables` Render-context
func Render(tmpl *template.Template, variables map[string]interface{}) (string, error) {
var buf strings.Builder
//buf.WriteString(`set -xeu pipefail`)
//buf.WriteString("\n\n")
//buf.WriteString(`export "PATH=$PATH:/sbin:/usr/local/bin:/opt/bin"`)
//buf.WriteString("\n\n")
if err := tmpl.Execute(&buf, variables); err != nil {
return "", errors.Wrap(err, "Failed to render cmd or script template")
}
return buf.String(), nil
}
func ParseIp(ip string) []string {
var availableIPs []string
// if ip is "1.1.1.1/",trim /
ip = strings.TrimRight(ip, "/")
if strings.Contains(ip, "/") == true {
if strings.Contains(ip, "/32") == true {
aip := strings.Replace(ip, "/32", "", -1)
availableIPs = append(availableIPs, aip)
} else {
availableIPs = GetAvailableIP(ip)
}
} else if strings.Contains(ip, "-") == true {
ipRange := strings.SplitN(ip, "-", 2)
availableIPs = GetAvailableIPRange(ipRange[0], ipRange[1])
} else {
availableIPs = append(availableIPs, ip)
}
return availableIPs
}
func GetAvailableIPRange(ipStart, ipEnd string) []string {
var availableIPs []string
firstIP := net.ParseIP(ipStart)
endIP := net.ParseIP(ipEnd)
if firstIP.To4() == nil || endIP.To4() == nil {
return availableIPs
}
firstIPNum := ipToInt(firstIP.To4())
EndIPNum := ipToInt(endIP.To4())
pos := int32(1)
newNum := firstIPNum
for newNum <= EndIPNum {
availableIPs = append(availableIPs, intToIP(newNum).String())
newNum = newNum + pos
}
return availableIPs
}
func GetAvailableIP(ipAndMask string) []string {
var availableIPs []string
ipAndMask = strings.TrimSpace(ipAndMask)
ipAndMask = IPAddressToCIDR(ipAndMask)
_, ipnet, _ := net.ParseCIDR(ipAndMask)
firstIP, _ := networkRange(ipnet)
ipNum := ipToInt(firstIP)
size := networkSize(ipnet.Mask)
pos := int32(1)
max := size - 2 // -1 for the broadcast address, -1 for the gateway address
var newNum int32
for attempt := int32(0); attempt < max; attempt++ {
newNum = ipNum + pos
pos = pos%max + 1
availableIPs = append(availableIPs, intToIP(newNum).String())
}
return availableIPs
}
func IPAddressToCIDR(ipAdress string) string {
if strings.Contains(ipAdress, "/") == true {
ipAndMask := strings.Split(ipAdress, "/")
ip := ipAndMask[0]
mask := ipAndMask[1]
if strings.Contains(mask, ".") == true {
mask = IPMaskStringToCIDR(mask)
}
return ip + "/" + mask
} else {
return ipAdress
}
}
func IPMaskStringToCIDR(netmask string) string {
netmaskList := strings.Split(netmask, ".")
var mint []int
for _, v := range netmaskList {
strv, _ := strconv.Atoi(v)
mint = append(mint, strv)
}
myIPMask := net.IPv4Mask(byte(mint[0]), byte(mint[1]), byte(mint[2]), byte(mint[3]))
ones, _ := myIPMask.Size()
return strconv.Itoa(ones)
}
func IPMaskCIDRToString(one string) string {
oneInt, _ := strconv.Atoi(one)
mIPmask := net.CIDRMask(oneInt, 32)
var maskstring []string
for _, v := range mIPmask {
maskstring = append(maskstring, strconv.Itoa(int(v)))
}
return strings.Join(maskstring, ".")
}
func networkRange(network *net.IPNet) (net.IP, net.IP) {
netIP := network.IP.To4()
firstIP := netIP.Mask(network.Mask)
lastIP := net.IPv4(0, 0, 0, 0).To4()
for i := 0; i < len(lastIP); i++ {
lastIP[i] = netIP[i] | ^network.Mask[i]
}
return firstIP, lastIP
}
func networkSize(mask net.IPMask) int32 {
m := net.IPv4Mask(0, 0, 0, 0)
for i := 0; i < net.IPv4len; i++ {
m[i] = ^mask[i]
}
return int32(binary.BigEndian.Uint32(m)) + 1
}
func ipToInt(ip net.IP) int32 {
return int32(binary.BigEndian.Uint32(ip.To4()))
}
func intToIP(n int32) net.IP {
b := make([]byte, 4)
binary.BigEndian.PutUint32(b, uint32(n))
return net.IP(b)
}
func GetLocalIP() (string, error) {
addrs, err := net.InterfaceAddrs()
if err != nil {
return "", err
}
for _, addr := range addrs {
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil && ipnet.IP.IsGlobalUnicast() {
return ipnet.IP.String(), nil
}
}
}
return "", errors.New("valid local IP not found!")
}
func LocalIP() string {
localIp, err := GetLocalIP()
if err != nil {
log.Fatalf("Failed to get Local IP: %v", err)
}
return localIp
}
// Compare Version Numbers
func CompareVersion(versionA, versionB string) int {
return compareStrVer(versionA, versionB)
}
func compareStrVer(verA, verB string) int {
verStrArrA := spliteStrByNet(verA)
verStrArrB := spliteStrByNet(verB)
lenStrA := len(verStrArrA)
lenStrB := len(verStrArrB)
if lenStrA != lenStrB {
panic("Inconsistent version number format!")
}
return compareArrStrVers(verStrArrA, verStrArrB)
}
func spliteStrByNet(strV string) []string {
return strings.Split(strV, ".")
}
func compareArrStrVers(verA, verB []string) int {
for index, _ := range verA {
littleResult := compareLittleVer(verA[index], verB[index])
if littleResult != VersionEqual {
return littleResult
}
}
return VersionEqual
}
func compareLittleVer(verA, verB string) int {
bytesA := []byte(verA)
bytesB := []byte(verB)
lenA := len(bytesA)
lenB := len(bytesB)
if lenA > lenB {
return VersionBig
}
if lenA < lenB {
return VersionSmall
}
return compareByBytes(bytesA, bytesB)
}
func compareByBytes(verA, verB []byte) int {
for index, _ := range verA {
if verA[index] > verB[index] {
return VersionBig
}
if verA[index] < verB[index] {
return VersionSmall
}
}
return VersionEqual
}
// returns the home directory for the executing user.
func Home() (string, error) {
user, err := user.Current()
if nil == err {
return user.HomeDir, nil
}
if "windows" == runtime.GOOS {
return homeWindows()
}
return homeUnix()
}
func homeUnix() (string, error) {
if home := os.Getenv("HOME"); home != "" {
return home, nil
}
var stdout bytes.Buffer
cmd := exec.Command("sh", "-c", "eval echo ~$USER")
cmd.Stdout = &stdout
if err := cmd.Run(); err != nil {
return "", err
}
result := strings.TrimSpace(stdout.String())
if result == "" {
return "", errors.New("blank output when reading home directory")
}
return result, nil
}
func homeWindows() (string, error) {
drive := os.Getenv("HOMEDRIVE")
path := os.Getenv("HOMEPATH")
home := drive + path
if drive == "" || path == "" {
home = os.Getenv("USERPROFILE")
}
if home == "" {
return "", errors.New("HOMEDRIVE, HOMEPATH, and USERPROFILE are blank")
}
return home, nil
}