golang 维护 hosts,兼容阿里云函数计算
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
}