mirror of
https://gitee.com/rainbond/Rainbond.git
synced 2024-11-30 02:38:17 +08:00
[REV] Add rest api to download file for export app in api project.
This commit is contained in:
parent
2483b97d6a
commit
b1409e44b8
@ -134,6 +134,8 @@ type SourcesInterface interface {
|
||||
|
||||
type AppInterface interface {
|
||||
ExportApp(w http.ResponseWriter, r *http.Request)
|
||||
Download(w http.ResponseWriter, r *http.Request)
|
||||
Upload(w http.ResponseWriter, r *http.Request)
|
||||
ImportApp(w http.ResponseWriter, r *http.Request)
|
||||
BackupApp(w http.ResponseWriter, r *http.Request)
|
||||
RecoverApp(w http.ResponseWriter, r *http.Request)
|
||||
|
@ -217,8 +217,13 @@ func (v2 *V2) appRouter() chi.Router {
|
||||
r := chi.NewRouter()
|
||||
r.Post("/export", controller.GetManager().ExportApp)
|
||||
r.Get("/export/{eventId}", controller.GetManager().ExportApp)
|
||||
|
||||
r.Get("/download/{format}/{fileName}", controller.GetManager().Download)
|
||||
r.Get("/upload/rainbond-app/{fileName}", controller.GetManager().Upload)
|
||||
|
||||
r.Post("/import", controller.GetManager().ImportApp)
|
||||
r.Get("/import/{eventId}", controller.GetManager().ExportApp)
|
||||
|
||||
r.Post("/backup", controller.GetManager().BackupApp)
|
||||
r.Post("/recover", controller.GetManager().RecoverApp)
|
||||
return r
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
dbmodel "github.com/goodrain/rainbond/db/model"
|
||||
"strings"
|
||||
"github.com/go-chi/chi"
|
||||
"os"
|
||||
)
|
||||
|
||||
type AppStruct struct {}
|
||||
@ -63,6 +64,26 @@ func (a *AppStruct) ExportApp(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
}
|
||||
|
||||
func (a *AppStruct) Download(w http.ResponseWriter, r *http.Request) {
|
||||
format := r.FormValue("format")
|
||||
fileName := r.FormValue("fileName")
|
||||
tarFile := fmt.Sprintf("%s/%s/%s", handler.GetAppHandler().GetStaticDir(), format, fileName)
|
||||
|
||||
_, err := os.Stat(tarFile)
|
||||
|
||||
// return status code 502 if the file not exists.
|
||||
if !os.IsExist(err) {
|
||||
httputil.ReturnError(r, w, 502, fmt.Sprintf("Not found export app tar file: %s", tarFile))
|
||||
return
|
||||
}
|
||||
|
||||
http.ServeFile(w, r, tarFile)
|
||||
}
|
||||
|
||||
func (a *AppStruct) Upload(w http.ResponseWriter, r *http.Request) {
|
||||
//TODO
|
||||
}
|
||||
|
||||
func (a *AppStruct) ImportApp(w http.ResponseWriter, r *http.Request) {
|
||||
//TODO
|
||||
}
|
||||
|
@ -18,12 +18,18 @@ import (
|
||||
)
|
||||
|
||||
type AppAction struct {
|
||||
MQClient pb.TaskQueueClient
|
||||
MQClient pb.TaskQueueClient
|
||||
staticDir string
|
||||
}
|
||||
|
||||
func (a *AppAction) GetStaticDir() string {
|
||||
return a.staticDir
|
||||
}
|
||||
|
||||
func CreateAppManager(mqClient pb.TaskQueueClient) *AppAction {
|
||||
return &AppAction{
|
||||
MQClient: mqClient,
|
||||
MQClient: mqClient,
|
||||
staticDir: "/grdata/app",
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,16 +48,18 @@ func (a *AppAction) Complete(tr *model.ExportAppStruct) error {
|
||||
}
|
||||
|
||||
appName = unicode2zh(appName)
|
||||
tr.SourceDir = fmt.Sprintf("/grdata/%s/%s-%s", tr.Body.Format, appName, tr.Body.Version)
|
||||
tr.SourceDir = fmt.Sprintf("%s/%s/%s-%s", a.staticDir, tr.Body.Format, appName, tr.Body.Version)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AppAction) ExportApp(tr *model.ExportAppStruct) error {
|
||||
// 保存元数据到组目录
|
||||
if err := saveMetadata(tr); err != nil {
|
||||
return util.CreateAPIHandleErrorFromDBError("Failed to export app", err)
|
||||
}
|
||||
|
||||
// 构建MQ事件对象
|
||||
mqBody, err := json.Marshal(model.BuildMQBodyFrom(tr))
|
||||
if err != nil {
|
||||
logrus.Error("Failed to encode json from ExportAppStruct:", err)
|
||||
@ -69,6 +77,7 @@ func (a *AppAction) ExportApp(tr *model.ExportAppStruct) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 写入事件到MQ中
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
_, err = a.MQClient.Enqueue(ctx, eq)
|
||||
cancel()
|
||||
@ -82,9 +91,10 @@ func (a *AppAction) ExportApp(tr *model.ExportAppStruct) error {
|
||||
}
|
||||
|
||||
func saveMetadata(tr *model.ExportAppStruct) error {
|
||||
os.RemoveAll(tr.SourceDir)
|
||||
// 创建应用组目录
|
||||
os.MkdirAll(tr.SourceDir, 0755)
|
||||
|
||||
// 写入元数据到文件
|
||||
err := ioutil.WriteFile(fmt.Sprintf("%s/metadata.json", tr.SourceDir), []byte(tr.Body.GroupMetadata), 0644)
|
||||
if err != nil {
|
||||
logrus.Error("Failed to save metadata", err)
|
||||
|
@ -22,7 +22,9 @@ import (
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"fmt"
|
||||
dbmodel "github.com/goodrain/rainbond/db/model"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//ServiceGetCommon path参数
|
||||
@ -1379,14 +1381,19 @@ type MQBody struct {
|
||||
}
|
||||
|
||||
func NewAppStatusFrom(app *ExportAppStruct) *dbmodel.AppStatus {
|
||||
tarFile := app.SourceDir + ".tar"
|
||||
fields := strings.Split(tarFile, "/")
|
||||
tarName := fields[len(fields)-1]
|
||||
tarFileHref := fmt.Sprintf("/v2/app/download/%s/%s", app.Body.Format, tarName)
|
||||
return &dbmodel.AppStatus{
|
||||
GroupKey: app.Body.GroupKey,
|
||||
Version: app.Body.Version,
|
||||
Format: app.Body.Format,
|
||||
EventID: app.Body.EventID,
|
||||
SourceDir: app.SourceDir,
|
||||
Status: "exporting",
|
||||
TarFile: app.SourceDir + ".tar",
|
||||
TimeStamp: time.Now().Nanosecond(),
|
||||
GroupKey: app.Body.GroupKey,
|
||||
Version: app.Body.Version,
|
||||
Format: app.Body.Format,
|
||||
EventID: app.Body.EventID,
|
||||
SourceDir: app.SourceDir,
|
||||
Status: "exporting",
|
||||
TarFile: tarFile,
|
||||
TarFileHref: tarFileHref,
|
||||
TimeStamp: time.Now().Nanosecond(),
|
||||
}
|
||||
}
|
||||
|
@ -98,6 +98,16 @@ func (i *ExportApp) Run(timeout time.Duration) error {
|
||||
|
||||
// 组目录命名规则,将组名中unicode转为中文,并去掉空格,"JAVA-ETCD\\u5206\\u4eab\\u7ec4" -> "JAVA-ETCD分享组"
|
||||
func (i *ExportApp) exportRainbondAPP() error {
|
||||
// 如果该应用已经打包过且是最新版,则跳过打包并返回成功
|
||||
if ok := i.isLatest(); ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 删除旧应用组目录,然后重新生成该应用包
|
||||
if err := i.CleanSourceDir(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 保存用应镜像和slug包
|
||||
if err := i.saveApps(); err != nil {
|
||||
return err
|
||||
@ -118,6 +128,16 @@ func (i *ExportApp) exportRainbondAPP() error {
|
||||
|
||||
// 组目录命名规则,将组名中unicode转为中文,并去掉空格,"JAVA-ETCD\\u5206\\u4eab\\u7ec4" -> "JAVA-ETCD分享组"
|
||||
func (i *ExportApp) exportDockerCompose() error {
|
||||
// 如果该应用已经打包过且是最新版,则跳过打包并返回成功
|
||||
if ok := i.isLatest(); ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 删除旧应用组目录,然后重新生成该应用包
|
||||
if err := i.CleanSourceDir(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 保存用应镜像和slug包
|
||||
if err := i.saveApps(); err != nil {
|
||||
return err
|
||||
@ -166,6 +186,44 @@ func (i *ExportApp) GetLogger() event.Logger {
|
||||
return i.Logger
|
||||
}
|
||||
|
||||
// isLatest 如果该应用已经打包过且是最新版则返回true
|
||||
func (i *ExportApp) isLatest() bool {
|
||||
md5File := fmt.Sprintf("%s/metadata.json.md5", i.SourceDir)
|
||||
_, err := os.Stat(md5File)
|
||||
if !os.IsExist(err) {
|
||||
logrus.Debug("The export app tar file is not found.")
|
||||
return false
|
||||
}
|
||||
|
||||
err = exec.Command("sh", "-c", fmt.Sprintf("cd %s ; md5sum -c metadata.json.md5", i.SourceDir)).Run()
|
||||
if err != nil {
|
||||
logrus.Debug("The export app tar file is not latest.")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (i *ExportApp) CleanSourceDir() error {
|
||||
metaFile := fmt.Sprintf("%s/metadata.json", i.SourceDir)
|
||||
|
||||
data, err := ioutil.ReadFile(metaFile)
|
||||
if err != nil {
|
||||
logrus.Error("Failed to read metadata file: ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
os.RemoveAll(i.SourceDir)
|
||||
os.MkdirAll(i.SourceDir, 0755)
|
||||
|
||||
if err := ioutil.WriteFile(metaFile, data, 0644); err != nil {
|
||||
logrus.Error("Failed to write metadata file: ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//parseApps get apps array from metadata.json
|
||||
func (i *ExportApp) parseApps() ([]gjson.Result, error) {
|
||||
i.Logger.Info("解析应用信息", map[string]string{"step": "export-app", "status": "success"})
|
||||
@ -173,7 +231,7 @@ func (i *ExportApp) parseApps() ([]gjson.Result, error) {
|
||||
data, err := ioutil.ReadFile(fmt.Sprintf("%s/metadata.json", i.SourceDir))
|
||||
if err != nil {
|
||||
i.Logger.Error("导出应用失败,没有找到应用信息", map[string]string{"step": "read-metadata", "status": "failure"})
|
||||
logrus.Error("Failed to export rainbond app: ", err)
|
||||
logrus.Error("Failed to read metadata file: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -579,11 +637,18 @@ func (i *ExportApp) updateStatus(status string) error {
|
||||
app.Status = status
|
||||
app.TimeStamp = time.Now().Nanosecond()
|
||||
|
||||
if db.GetManager().AppDao().UpdateModel(app); err != nil {
|
||||
if err := db.GetManager().AppDao().UpdateModel(app); err != nil {
|
||||
err = errors.New(fmt.Sprintf("Failed to update app %s: %v", i.GroupKey, err))
|
||||
return err
|
||||
}
|
||||
|
||||
// 生成MD5值并写入到文件,以便在下次收到该请求时决定是否该重新打包该应用
|
||||
metadataFile := fmt.Sprintf("%s/metadata.json", i.SourceDir)
|
||||
if err := exec.Command("sh", "-c", fmt.Sprintf("md5sum %s > %s.md5", metadataFile, metadataFile)).Run(); err != nil {
|
||||
err = errors.New(fmt.Sprintf("Failed to create md5 file: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -52,6 +52,7 @@ type AppDao interface {
|
||||
DelDao
|
||||
Get(groupKey, version string) (interface{}, error)
|
||||
GetByEventId(eventId string) (interface{}, error)
|
||||
DeleteModelByEventId(eventId string) error
|
||||
}
|
||||
|
||||
//LicenseDao LicenseDao
|
||||
|
@ -1,14 +1,15 @@
|
||||
package model
|
||||
|
||||
type AppStatus struct {
|
||||
EventID string `gorm:"column:event_id;size:32;primary_key" json:"event_id"`
|
||||
GroupKey string `gorm:"column:group_key;size:64" json:"group_key"`
|
||||
Version string `gorm:"column:version;size:32" json:"version"`
|
||||
Format string `gorm:"column:format;size:32" json:"format"` // only rainbond-app/docker-compose
|
||||
SourceDir string `gorm:"column:source_dir;size:255" json:"source_dir"`
|
||||
Status string `gorm:"column:status;size:32" json:"status"` // only exporting/importing/failed/success
|
||||
TarFile string `gorm:"column:tar_file;size:255" json:"tar_file"`
|
||||
TimeStamp int `gorm:"column:timestamp" json:"timestamp"`
|
||||
EventID string `gorm:"column:event_id;size:32;primary_key" json:"event_id"`
|
||||
GroupKey string `gorm:"column:group_key;size:64" json:"group_key"`
|
||||
Version string `gorm:"column:version;size:32" json:"version"`
|
||||
Format string `gorm:"column:format;size:32" json:"format"` // only rainbond-app/docker-compose
|
||||
SourceDir string `gorm:"column:source_dir;size:255" json:"source_dir"`
|
||||
Status string `gorm:"column:status;size:32" json:"status"` // only exporting/importing/failed/success
|
||||
TarFile string `gorm:"column:tar_file;size:255" json:"tar_file"`
|
||||
TarFileHref string `gorm:"column:tar_file_href;size:255" json:"tar_file_href"`
|
||||
TimeStamp int `gorm:"column:timestamp" json:"timestamp"`
|
||||
}
|
||||
|
||||
//TableName 表名
|
||||
|
@ -55,6 +55,15 @@ func (a *AppDaoImpl) DeleteModel(groupKey string, arg ...interface{}) error {
|
||||
return a.DB.Where("group_key = ? and version = ?", groupKey, version).Delete(&app).Error
|
||||
}
|
||||
|
||||
func (a *AppDaoImpl) DeleteModelByEventId(eventId string) error {
|
||||
var app model.AppStatus
|
||||
if ok := a.DB.Where("event_id = ?", eventId).Find(&app).RecordNotFound(); ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return a.DB.Where("event_id = ?", eventId).Delete(&app).Error
|
||||
}
|
||||
|
||||
func (a *AppDaoImpl) Get(groupKey, version string) (interface{}, error) {
|
||||
var app model.AppStatus
|
||||
err := a.DB.Where("group_key = ? and version = ?", groupKey, version).First(&app).Error
|
||||
|
Loading…
Reference in New Issue
Block a user