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
|
}
|