golang 维护 hosts,兼容阿里云函数计算

golang

redis公网集群指定了域名,但是在同局域网的客户端程序中,应该直接用内网ip连接。
华为云那边可以直接在vpc的内网域名配置redis域名的ip,阿里云这边需要自建dns,本着服务之间尽量不耦合的原则,加上阿里云这边的函数计算实际是独立的hosts文件可以直接修改。

下面算法,先用环境变量判断是否修改hosts文件(华为云fc没有修改权限),然后从redis中根据环境得到要修改的hosts内容。而后复制备份/etc/hosts 文件,创建一个新的hosts 然后合并两个文件。

go
package lib_hosts

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/buger/jsonparser"
	"github.com/redis/go-redis/v9"
)

func UpdateHostsFileFormRedisAndLookupIP(rdb redis.ClusterClient, ctx context.Context) {
	EnvHostsTag := os.Getenv("EnvHostsTag")
	if EnvHostsTag == "" {
		log.Println("== 不需要检查和修改 域名解析")
		return
	}
	//是否更新 hosts文件,华为云那边不能修改,需要通过vpc的内网域名的A记录来控制
	enableUpdataHostsFile := os.Getenv("EnvHostsEndEnableUpdateFile") == "1"
	//执行完成后是否ping一下
	EnablePing := os.Getenv("EnvHostsEndEnablePing") == "1"
	val, err := rdb.Get(ctx, "f501:Config:/etc/hosts").Result()
	if err != nil {
		log.Println("UpdateHostsFileFormRedis f501:Config:/etc/hosts  错误!!!", err)
		return
	} else {
		linesString, err := jsonparser.GetString([]byte(val), EnvHostsTag, "Lines")
		if err != nil {
			log.Println("UpdateHostsFileFormRedis 没找到相关信息 ", err)
			return
		}
		if enableUpdataHostsFile {
			UpdateHostsFile(linesString) //更新hosts文件 华为那边zip部署的fc不支持需要手动配置修改vpc
		} else {
			log.Println("部分环境不启用/etc/hosts更新,如果要启动请 到redis修改...")
			log.Println("跳过/etc/hosts配置,你可能需要手动配置dns或vpc组网的内网域名的A记录 ...")
		}
		if EnablePing { //执行ping命令
			log.Println("所有域名分别LookupIP ...")
			time.Sleep(200 * time.Microsecond)
			re := regexp.MustCompile(`\s+`)
			linesString = re.ReplaceAllString(linesString, " ")
			lines := strings.Split(linesString, ",")
			for _, line := range lines {
				linesArr := strings.Split(line, " ")
				if len(linesArr) > 1 {
					ipAddresses, err := net.LookupIP(linesArr[1])
					if err != nil {
						log.Println("net.LookupIP Error:", err)
						return
					}
					for _, ip := range ipAddresses {
						log.Println("net.LookupIP :", linesArr[1], ip)
					}
				} else {
					log.Println("UpdateHostsFileFormRedis 长度不符", linesArr)
				}

			}

		}
	}
}

// 修改hosts文件
func UpdateHostsFile(linesString string) {
	log.Println("更新hosts 文件...")

	lines := strings.Split(linesString, ",")

	exists := FileExists("/etc/hosts-fish-source")
	if !exists {
		err := CopyFile("/etc/hosts", "/etc/hosts-fish-source")
		if err != nil {
			log.Println(err)
			return
		} else {
			//log.Println("复制hosts文件 到 /etc/hosts-fish-source")
		}
	}

	fileAppend, err := os.Create("/etc/hosts-fish-append")
	if err != nil {
		log.Println("无法创建目标文件: ", err)
		return
	}
	defer fileAppend.Close()

	writer := bufio.NewWriter(fileAppend)
	defer writer.Flush()
	for _, line := range lines {
		_, err := writer.WriteString(line + "\n")
		if err != nil {
			log.Println("写入文件失败:", err)
			return
		}
	}
	writer.Flush()
	/*	buf2, err := os.ReadFile("/etc/hosts-fish-append")
		if err != nil {
			log.Println("文件读取失败", "append", err)
			return
		}
		log.Println("###---###内容 wjx  append", string(buf2)) // 将内容转换为字符串并打印
	*/
	//log.Println("新建文件 到 /etc/hosts-fish-append  准备合并")
	err = MergeFiles("/etc/hosts-fish-source", "/etc/hosts-fish-append", "/etc/hosts")
	if err != nil {
		log.Println("文件合并成功错误", err)
	} else {
		//log.Println("文件合并成功")
	}

}

func FileExists(filename string) bool {
	_, err := os.Stat(filename)
	if err == nil {
		return true // 文件存在
	}
	if os.IsNotExist(err) {
		return false // 文件不存在
	}
	return false // 其他错误,无法确定文件是否存在
}

func MergeFiles(file1, file2, outputFile string) error {
	err := ClearFile(outputFile)
	if err != nil {
		log.Println("文件清空失败", err)
		return err
	}
	/*	content, err := os.ReadFile(outputFile) // 读取文件内容
		if err != nil {
			log.Println("文件清空后重新读取失败", outputFile, err)
			return err
		}
		log.Println("###---###内容", outputFile, string(content)) // 将内容转换为字符串并打印
	*/
	buf1, err := os.ReadFile(file1)
	if err != nil {
		log.Println("文件读取失败", file1, err)
		return err
	}
	//log.Println("###---###内容", file1, string(buf1)) // 将内容转换为字符串并打印

	buf2, err := os.ReadFile(file2)
	if err != nil {
		log.Println("文件读取失败", file2, err)
		return err
	}
	//log.Println("###---###内容", file2, string(buf2)) // 将内容转换为字符串并打印

	file, err := os.OpenFile(outputFile, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
	if err != nil {
		log.Println("文件打开失败", outputFile, err)
		return err
	}
	defer file.Close()
	writer := bufio.NewWriter(file)
	writer.Write(buf1)
	writer.Write(buf2)
	writer.Flush()
	return nil
}

func ClearFile(filename string) error {
	// 打开文件并截断内容
	file, err := os.OpenFile(filename, os.O_RDWR|os.O_TRUNC, 0666)
	if err != nil {
		return err
	}
	defer file.Close()

	// 清空文件内容
	err = file.Truncate(0)
	if err != nil {
		return err
	}

	//log.Printf("文件 %s 已成功清空", filename)
	return nil
}
func CopyFile(sourceFile string, destinationFile string) error {
	src, err := os.Open(sourceFile)
	if err != nil {
		return fmt.Errorf("无法打开源文件 %s: %s", sourceFile, err)
	}
	defer src.Close()

	dst, err := os.Create(destinationFile)
	if err != nil {
		return fmt.Errorf("无法创建目标文件 %s: %s", destinationFile, err)
	}
	defer dst.Close()

	_, err = io.Copy(dst, src)
	if err != nil {
		return fmt.Errorf("复制文件时出错: %s", err)
	}

	return nil
}

评论