github.com/TeaOSLab/EdgeNode@v1.3.8/internal/nodes/upgrade_manager.go (about) 1 // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved. 2 3 package nodes 4 5 import ( 6 "crypto/md5" 7 "fmt" 8 "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" 9 teaconst "github.com/TeaOSLab/EdgeNode/internal/const" 10 "github.com/TeaOSLab/EdgeNode/internal/events" 11 "github.com/TeaOSLab/EdgeNode/internal/goman" 12 "github.com/TeaOSLab/EdgeNode/internal/remotelogs" 13 "github.com/TeaOSLab/EdgeNode/internal/rpc" 14 "github.com/TeaOSLab/EdgeNode/internal/utils" 15 executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec" 16 "github.com/iwind/TeaGo/Tea" 17 stringutil "github.com/iwind/TeaGo/utils/string" 18 "github.com/iwind/gosock/pkg/gosock" 19 "os" 20 "path/filepath" 21 "runtime" 22 "time" 23 ) 24 25 var sharedUpgradeManager = NewUpgradeManager() 26 27 // UpgradeManager 节点升级管理器 28 // TODO 需要在集群中设置是否自动更新 29 type UpgradeManager struct { 30 isInstalling bool 31 lastFile string 32 exe string 33 } 34 35 // NewUpgradeManager 获取新对象 36 func NewUpgradeManager() *UpgradeManager { 37 return &UpgradeManager{} 38 } 39 40 // Start 启动升级 41 func (this *UpgradeManager) Start() { 42 // 必须放在文件解压之前读取可执行文件路径,防止解析之后,当前的可执行文件路径发生改变 43 exe, err := os.Executable() 44 if err != nil { 45 remotelogs.Error("UPGRADE_MANAGER", "can not find current executable file name") 46 return 47 } 48 this.exe = exe 49 50 // 测试环境下不更新 51 if Tea.IsTesting() { 52 return 53 } 54 55 if this.isInstalling { 56 return 57 } 58 this.isInstalling = true 59 60 remotelogs.Println("UPGRADE_MANAGER", "upgrading node ...") 61 err = this.install() 62 if err != nil { 63 remotelogs.Error("UPGRADE_MANAGER", "download failed: "+err.Error()) 64 65 this.isInstalling = false 66 return 67 } 68 69 remotelogs.Println("UPGRADE_MANAGER", "upgrade successfully") 70 71 goman.New(func() { 72 err = this.restart() 73 if err != nil { 74 remotelogs.Error("UPGRADE_MANAGER", err.Error()) 75 } 76 77 this.isInstalling = false 78 }) 79 } 80 81 // IsInstalling 检查是否正在安装 82 func (this *UpgradeManager) IsInstalling() bool { 83 return this.isInstalling 84 } 85 86 func (this *UpgradeManager) install() error { 87 // 检查是否有已下载但未安装成功的 88 if len(this.lastFile) > 0 { 89 _, err := os.Stat(this.lastFile) 90 if err == nil { 91 err = this.unzip(this.lastFile) 92 if err != nil { 93 return err 94 } 95 this.lastFile = "" 96 return nil 97 } 98 } 99 100 // 创建临时文件 101 var dir = Tea.Root + "/tmp" 102 _, err := os.Stat(dir) 103 if err != nil { 104 if os.IsNotExist(err) { 105 err = os.Mkdir(dir, 0777) 106 if err != nil { 107 return err 108 } 109 } else { 110 return err 111 } 112 } 113 114 remotelogs.Println("UPGRADE_MANAGER", "downloading new node ...") 115 116 var path = dir + "/edge-node.tmp" 117 fp, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0777) 118 if err != nil { 119 return err 120 } 121 isClosed := false 122 defer func() { 123 if !isClosed { 124 _ = fp.Close() 125 } 126 }() 127 128 client, err := rpc.SharedRPC() 129 if err != nil { 130 return err 131 } 132 133 var offset int64 134 var h = md5.New() 135 var sum = "" 136 var filename = "" 137 for { 138 resp, err := client.NodeRPC.DownloadNodeInstallationFile(client.Context(), &pb.DownloadNodeInstallationFileRequest{ 139 Os: runtime.GOOS, 140 Arch: runtime.GOARCH, 141 ChunkOffset: offset, 142 }) 143 if err != nil { 144 return err 145 } 146 if len(resp.Sum) == 0 { 147 return nil 148 } 149 sum = resp.Sum 150 filename = resp.Filename 151 if stringutil.VersionCompare(resp.Version, teaconst.Version) <= 0 { 152 return nil 153 } 154 if len(resp.ChunkData) == 0 { 155 break 156 } 157 158 // 写入文件 159 _, err = fp.Write(resp.ChunkData) 160 if err != nil { 161 return err 162 } 163 _, err = h.Write(resp.ChunkData) 164 if err != nil { 165 return err 166 } 167 168 offset = resp.Offset 169 } 170 171 if len(filename) == 0 { 172 return nil 173 } 174 175 isClosed = true 176 err = fp.Close() 177 if err != nil { 178 return err 179 } 180 181 if fmt.Sprintf("%x", h.Sum(nil)) != sum { 182 _ = os.Remove(path) 183 return nil 184 } 185 186 // 改成zip 187 zipPath := dir + "/" + filename 188 err = os.Rename(path, zipPath) 189 if err != nil { 190 return err 191 } 192 this.lastFile = zipPath 193 194 // 解压 195 err = this.unzip(zipPath) 196 if err != nil { 197 return err 198 } 199 200 return nil 201 } 202 203 // 解压 204 func (this *UpgradeManager) unzip(zipPath string) error { 205 var isOk = false 206 defer func() { 207 if isOk { 208 // 只有解压并覆盖成功后才会删除 209 _ = os.Remove(zipPath) 210 } 211 }() 212 213 // 解压 214 var target = Tea.Root 215 if Tea.IsTesting() { 216 // 测试环境下只解压在tmp目录 217 target = Tea.Root + "/tmp" 218 } 219 220 // 先改先前的可执行文件 221 err := os.Rename(target+"/bin/"+teaconst.ProcessName, target+"/bin/."+teaconst.ProcessName+".dist") 222 var hasBackup = err == nil 223 defer func() { 224 if !isOk && hasBackup { 225 // 失败时还原 226 _ = os.Rename(target+"/bin/."+teaconst.ProcessName+".dist", target+"/bin/"+teaconst.ProcessName) 227 } 228 }() 229 230 var unzip = utils.NewUnzip(zipPath, target, "edge-node/") 231 err = unzip.Run() 232 if err != nil { 233 return err 234 } 235 236 isOk = true 237 238 return nil 239 } 240 241 // 重启 242 func (this *UpgradeManager) restart() error { 243 // 关闭当前sock,防止无法重启 244 _ = gosock.NewTmpSock(teaconst.ProcessName).Close() 245 246 // 重新启动 247 if DaemonIsOn && DaemonPid == os.Getppid() { 248 utils.Exit() // TODO 试着更优雅重启 249 } else { 250 // quit 251 events.Notify(events.EventQuit) 252 253 // terminated 254 events.Notify(events.EventTerminated) 255 256 // 启动 257 var exe = filepath.Dir(this.exe) + "/" + teaconst.ProcessName 258 var cmd = executils.NewCmd(exe, "start") 259 err := cmd.Start() 260 if err != nil { 261 return err 262 } 263 264 // 退出当前进程 265 time.Sleep(1 * time.Second) 266 os.Exit(0) 267 } 268 return nil 269 }