package upgrade import ( "basic.com/valib/version.git" "context" "basic.com/valib/logger.git" "encoding/json" "errors" "fmt" "github.com/gin-gonic/gin" "gopkg.in/ini.v1" "io" "io/ioutil" "net/http" "os" "path" "sync" "time" "vamicro/update-service/models" "vamicro/update-service/utils" ) type ( UpgradeReply struct { Status int Body map[string]interface{} } UpgradePara struct { Platform string ArchiveFile *os.File ReplyCh chan<- *UpgradeReply Version string Intro string } ) var ( msgCH chan *UpgradePara ) func init() { msgCH = make(chan *UpgradePara) } func Initialize(ctx context.Context, wg *sync.WaitGroup) { wg.Add(1) go msgLoop(ctx, wg) } func PUshMsg(msg *UpgradePara) { msgCH <- msg } func msgLoop(ctx context.Context, wg *sync.WaitGroup) { for { select { case <-ctx.Done(): logger.Info("upgrade msg loop canceled by user") wg.Done() return case msg := <- msgCH: code, body := doUpgrade(msg.Platform, msg.ArchiveFile, msg.Version, msg.Intro) reply := &UpgradeReply { Status: code, Body: body, } msg.ReplyCh <- reply } } } func doUpgrade(arch string, f *os.File, version string, intro string) (int, map[string]interface{}) { tempDir, err := ioutil.TempDir("", "dist-") if nil != err { body := map[string]interface{} { "code": http.StatusInternalServerError, "msg": err.Error(), "data": []*models.Program{}, } logger.Error("doUpgrade create temp dire failed, err:", err.Error()) return http.StatusInternalServerError, body } logger.Info("doUpgrade tempDir:", tempDir) //reopen later after debug defer func() { err = os.RemoveAll(tempDir) }() err = utils.DeCompressZip(f.Name(), tempDir) if nil != err { body := map[string]interface{} { "code": http.StatusBadRequest, "msg": err.Error(), "data": []*models.Program{}, } logger.Error("doUpgrade decompress failed, err:", err.Error()) return http.StatusBadRequest, body } newPrograms, err := packageAnalyze(tempDir) if nil != err { body := map[string]interface{} { "code": http.StatusBadRequest, "msg": err.Error(), "data": []*models.Program{}, } logger.Error("doUpgrade packageAnalyze failed, err:", err.Error()) return http.StatusBadRequest, body } if len(newPrograms) < 1 { body := map[string]interface{} { "code": http.StatusBadRequest, "msg": "No valid programs to be upgrade", "data": []*models.Program{}, } logger.Error("doUpgrade No valid programs to be upgrade") return http.StatusBadRequest, body } b, err := json.Marshal(newPrograms) if nil == err { logger.Info("doUpgrade marshal ok:", len(newPrograms), string(b)) } else { logger.Info("doUpgrade marshal failed") } err = models.UpgradePrograms(arch, newPrograms) if nil != err { body := map[string]interface{} { "code": http.StatusInternalServerError, "msg": err.Error(), "data": []*models.Program{}, } logger.Error("doUpgrade commit updated failed, err:", err.Error()) return http.StatusInternalServerError, body } logger.Info("doUpgrade update db done") updatedPrograms, err := models.FindAllPrograms(arch) if nil != err { body := map[string]interface{} { "code": http.StatusInternalServerError, "msg": err.Error(), "data": &struct { }{}, } logger.Error("doUpgrade get old programs failed, err:", err.Error()) return http.StatusInternalServerError, body } b, err = json.Marshal(updatedPrograms) if nil == err { logger.Info("doUpgrade updatedPrograms marshal ok:", string(b)) } else { logger.Info("doUpgrade updatedPrograms marshal failed") } distPath := utils.GetDistPath(arch) tmpFile, err := ioutil.TempFile(distPath, "dist-*.zip") if nil != err { body := map[string]interface{} { "code": http.StatusInternalServerError, "msg": err.Error(), "data": &struct { }{}, } logger.Error("doUpgrade create temp file failed, err:", err.Error()) return http.StatusInternalServerError, body } defer tmpFile.Close() _, err = io.Copy(tmpFile, f) if nil != err { body := map[string]interface{} { "code": http.StatusInternalServerError, "msg": err.Error(), "data": &struct { }{}, } logger.Error("doUpgrade create copy file failed, err:", err.Error()) return http.StatusInternalServerError, body } dist := &models.DistFile{ Platform: arch, Timestamp: time.Now().UnixNano() / 1e6, Path: tmpFile.Name(), Version: version, Intro: intro, } err = dist.Insert(models.GetDB()) if nil != err { body := map[string]interface{} { "code": http.StatusInternalServerError, "msg": err.Error(), "data": &struct { }{}, } logger.Error("doUpgrade insert db failed, err:", err.Error()) return http.StatusInternalServerError, body } return http.StatusOK, gin.H { "code": http.StatusOK, "msg": "ok", "data": updatedPrograms, } } func packageAnalyze(dir string) ([]*models.Program, error) { manifestPath := path.Join(dir, "dist/bin/manifest.ini") if _, err := os.Stat(manifestPath); os.IsNotExist(err) { logger.Error("packageAnalyze manifest.ini not exist", err) return nil, errors.New("failed to find manifest.ini in the archive file") } cfg, err := ini.Load(manifestPath) if nil != err { logger.Error("packageAnalyze failed to load manifest.ini", err) return nil, errors.New("manifest.ini is invalid in the archive file") } sections := cfg.Sections() if len(sections) < 1 { logger.Error("No sections found in manifest.ini") return nil, errors.New("No sections found in manifest.ini") } var arr []*models.Program for _, s := range sections { t := s.Key("Type").String() if t != "app" { continue } v := s.Key("Version").String() _, err := vaversion.VersionName2VaVersion(v) if nil != err { msg := fmt.Sprint("invalid version in manifest.ini, ", "section:", s.Name(), "version:", v, err) logger.Error("packageAnalyze version check msg:", msg) return nil, errors.New(msg) } app := path.Join(dir, "dist/bin", s.Name()) if _, err := os.Stat(app); os.IsNotExist(err) { msg := fmt.Sprint("app not exits in dist/bin, ", "app:", s.Name()) logger.Error("packageAnalyze app check msg:", msg) return nil, errors.New(msg) } md5, err := utils.Md5sum(app) if nil != err { msg := fmt.Sprint("calculate md5sum failed, ", "app:", s.Name()) logger.Error("packageAnalyze md5sum check msg:", msg) return nil, errors.New(msg) } buildTime := s.Key("BuildTime").String() commitSha1 := s.Key("Commit").String() p := &models.Program{ Name: s.Name(), Version: v, Md5sum: md5, BuildTime: buildTime, CommitSha1: commitSha1, } arr = append(arr, p) } return arr, nil }