mirror of
https://gitee.com/rainbond/Rainbond.git
synced 2024-12-02 03:37:46 +08:00
[ADD] Add new feature to export app with docker-compose format.
This commit is contained in:
parent
597957d4fc
commit
b857175b05
@ -7,6 +7,7 @@ import (
|
||||
"github.com/goodrain/rainbond/api/handler"
|
||||
httputil "github.com/goodrain/rainbond/util/http"
|
||||
"github.com/goodrain/rainbond/api/model"
|
||||
"github.com/goodrain/rainbond/db"
|
||||
)
|
||||
|
||||
type AppStruct struct {}
|
||||
@ -18,12 +19,22 @@ func (a *AppStruct) ExportApp(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := handler.GetAppHandler().Complete(&tr); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err := handler.GetAppHandler().ExportApp(&tr)
|
||||
if err != nil {
|
||||
httputil.ReturnError(r, w, 500, fmt.Sprintf("Failed to export app: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
app := model.NewAppStatusFrom(&tr)
|
||||
if err := db.GetManager().AppDao().AddModel(app); err != nil {
|
||||
httputil.ReturnError(r, w, 500, fmt.Sprintf("Failed to export app %s: %v", app.GroupKey, err))
|
||||
return
|
||||
}
|
||||
|
||||
httputil.ReturnSuccess(r, w, nil)
|
||||
return
|
||||
}
|
||||
@ -32,8 +43,28 @@ func (a *AppStruct) ImportApp(w http.ResponseWriter, r *http.Request) {
|
||||
//TODO
|
||||
}
|
||||
|
||||
// TODO 目前与ExportApp函数一致,以后可能会合并
|
||||
func (a *AppStruct) ExportRunnableApp(w http.ResponseWriter, r *http.Request) {
|
||||
//TODO
|
||||
var tr model.ExportAppStruct
|
||||
ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &tr.Body, nil)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
err := handler.GetAppHandler().ExportRunnableApp(&tr)
|
||||
if err != nil {
|
||||
httputil.ReturnError(r, w, 500, fmt.Sprintf("Failed to export runnable app: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
app := model.NewAppStatusFrom(&tr)
|
||||
if err := db.GetManager().AppDao().AddModel(app); err != nil {
|
||||
httputil.ReturnError(r, w, 500, fmt.Sprintf("Failed to export runnable app %s: %v", app.GroupKey, err))
|
||||
return
|
||||
}
|
||||
|
||||
httputil.ReturnSuccess(r, w, nil)
|
||||
return
|
||||
}
|
||||
|
||||
func (a *AppStruct) BackupApp(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -21,13 +21,64 @@ type AppAction struct {
|
||||
MQClient pb.TaskQueueClient
|
||||
}
|
||||
|
||||
func CreateAppManager(mqClient pb.TaskQueueClient) AppAction {
|
||||
return AppAction{
|
||||
func CreateAppManager(mqClient pb.TaskQueueClient) *AppAction {
|
||||
return &AppAction{
|
||||
MQClient: mqClient,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AppAction) Complete(tr *model.ExportAppStruct) error {
|
||||
appName := gjson.Get(tr.Body.GroupMetadata, ".group_name").String()
|
||||
if appName == "" {
|
||||
err := errors.New("Failed to get group name form metadata.")
|
||||
logrus.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
tr.Body.GroupName = appName
|
||||
appName = unicode2zh(appName)
|
||||
tr.SourceDir = fmt.Sprintf("/grdata/export-app/%s-%s", 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)
|
||||
}
|
||||
|
||||
mqBody, err := json.Marshal(BuildExportAppBody(tr))
|
||||
if err != nil {
|
||||
logrus.Error("Failed to encode json from ExportAppStruct:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
ts := &db.BuildTaskStruct{
|
||||
TaskType: "export_app",
|
||||
TaskBody: mqBody,
|
||||
}
|
||||
|
||||
eq, err := db.BuildTaskBuild(ts)
|
||||
if err != nil {
|
||||
logrus.Error("Failed to BuildTaskBuild for ExportApp:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
_, err = a.MQClient.Enqueue(ctx, eq)
|
||||
cancel()
|
||||
if err != nil {
|
||||
logrus.Error("Failed to Enqueue MQ for ExportApp:", err)
|
||||
return err
|
||||
}
|
||||
logrus.Debugf("equeue mq build plugin from image success")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
// TODO 与ExportApp函数唯一不同的是导出目录,以后有可能合并
|
||||
func (a *AppAction) ExportRunnableApp(tr *model.ExportAppStruct) error {
|
||||
appName := gjson.Get(tr.Body.GroupMetadata, ".group_name").String()
|
||||
if appName == "" {
|
||||
err := errors.New("Failed to get group name form metadata.")
|
||||
@ -37,7 +88,7 @@ func (a *AppAction) ExportApp(tr *model.ExportAppStruct) error {
|
||||
|
||||
appName = unicode2zh(appName)
|
||||
|
||||
tr.SourceDir = fmt.Sprintf("/grdata/export-app/%s-%s", appName, tr.Body.Version)
|
||||
tr.SourceDir = fmt.Sprintf("/grdata/export-runnable-app/%s-%s", appName, tr.Body.Version)
|
||||
|
||||
if err := saveMetadata(tr); err != nil {
|
||||
return util.CreateAPIHandleErrorFromDBError("Failed to export app", err)
|
||||
@ -89,13 +140,10 @@ type ExportAppBody struct {
|
||||
}
|
||||
|
||||
func saveMetadata(tr *model.ExportAppStruct) error {
|
||||
err := os.MkdirAll(tr.SourceDir, os.ModePerm)
|
||||
if err != nil {
|
||||
logrus.Error("Failed to save metadata", err)
|
||||
return err
|
||||
}
|
||||
os.RemoveAll(tr.SourceDir)
|
||||
os.MkdirAll(tr.SourceDir, 0755)
|
||||
|
||||
err = ioutil.WriteFile(fmt.Sprintf("%s/metadata.json", tr.SourceDir), []byte(tr.Body.GroupMetadata), 0644)
|
||||
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)
|
||||
return err
|
||||
|
@ -89,8 +89,6 @@ func GetServiceManager() ServiceHandler {
|
||||
|
||||
var defaultPluginHandler PluginHandler
|
||||
|
||||
var defaultAppHandler AppAction
|
||||
|
||||
//GetPluginManager get manager
|
||||
func GetPluginManager() PluginHandler {
|
||||
return defaultPluginHandler
|
||||
@ -131,6 +129,8 @@ func GetEventHandler() EventHandler {
|
||||
return defaultEventHandler
|
||||
}
|
||||
|
||||
func GetAppHandler() AppAction {
|
||||
var defaultAppHandler *AppAction
|
||||
|
||||
func GetAppHandler() *AppAction {
|
||||
return defaultAppHandler
|
||||
}
|
@ -23,6 +23,7 @@ import (
|
||||
"time"
|
||||
|
||||
dbmodel "github.com/goodrain/rainbond/db/model"
|
||||
"github.com/goodrain/rainbond/db/mysql/dao"
|
||||
)
|
||||
|
||||
//ServiceGetCommon path参数
|
||||
@ -1354,8 +1355,23 @@ type ExportAppStruct struct {
|
||||
Body struct {
|
||||
EventID string `json:"event_id"`
|
||||
GroupKey string `json:"group_key"`
|
||||
GroupName string `json:"group_name"`
|
||||
Version string `json:"version"`
|
||||
Format string `json:"format"`
|
||||
Format string `json:"format"` // only rainbond-app/docker-compose
|
||||
GroupMetadata string `json:"group_metadata"`
|
||||
}
|
||||
}
|
||||
|
||||
func NewAppStatusFrom(exportApp *ExportAppStruct) *dao.AppStatus {
|
||||
return &dao.AppStatus{
|
||||
GroupKey: exportApp.Body.GroupKey,
|
||||
GroupName: exportApp.Body.GroupName, // TODO 以后可能会去掉
|
||||
Version: exportApp.Body.Version,
|
||||
Format: exportApp.Body.Format,
|
||||
EventID: exportApp.Body.EventID,
|
||||
SourceDir: exportApp.SourceDir,
|
||||
Status: "exporting",
|
||||
TarFile: exportApp.SourceDir + ".tar",
|
||||
TimeStamp: time.Now().Nanosecond(),
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,8 @@ import (
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"github.com/goodrain/rainbond/db/mysql/dao"
|
||||
"github.com/goodrain/rainbond/db"
|
||||
)
|
||||
|
||||
//ExportApp Export app to specified format(rainbond-app or dockercompose)
|
||||
@ -70,7 +72,7 @@ func NewExportApp(in []byte) TaskWorker {
|
||||
func (i *ExportApp) Run(timeout time.Duration) error {
|
||||
if i.Format == "rainbond-app" {
|
||||
return i.exportRainbondAPP()
|
||||
} else if i.Format == "dockercompose" {
|
||||
} else if i.Format == "docker-compose" {
|
||||
return i.exportDockerCompose()
|
||||
}
|
||||
return nil
|
||||
@ -88,6 +90,11 @@ func (i *ExportApp) exportRainbondAPP() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新应用状态
|
||||
if err := i.updateStatus(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -118,6 +125,11 @@ func (i *ExportApp) exportDockerCompose() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新应用状态
|
||||
if err := i.updateStatus(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -463,7 +475,7 @@ func (i *ExportApp) generateTarFile() error {
|
||||
// /grdata/export-app/myapp-1.0 -> myapp-1.0
|
||||
baseName := path.Base(i.SourceDir)
|
||||
// 打包整个目录为tar包
|
||||
err := exec.Command(fmt.Sprintf("cd %s ; tar -cf %s.tar %s", dirName, baseName, baseName)).Run()
|
||||
err := exec.Command(fmt.Sprintf("cd %s ; rm -rf %s.tar ; tar -cf %s.tar %s", dirName, baseName, baseName, baseName)).Run()
|
||||
if err != nil {
|
||||
i.Logger.Error("打包应用失败", map[string]string{"step": "export-app", "status": "failure"})
|
||||
logrus.Errorf("Failed to create tar file for group key %s: %v", i.GroupKey, err)
|
||||
@ -475,6 +487,24 @@ func (i *ExportApp) generateTarFile() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ExportApp) updateStatus() error {
|
||||
res, err := db.GetManager().AppDao().Get(i.GroupKey, i.Version)
|
||||
if err != nil {
|
||||
err = errors.New(fmt.Sprintf("Failed to get app %s from db: %v", i.GroupKey, err))
|
||||
return err
|
||||
}
|
||||
|
||||
app := res.(*dao.AppStatus)
|
||||
app.Status = "success"
|
||||
|
||||
if db.GetManager().AppDao().UpdateModel(app); err != nil {
|
||||
err = errors.New(fmt.Sprintf("Failed to update app %s: %v", i.GroupKey, err))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 只保留"/"后面的部分,并去掉不合法字符
|
||||
func buildToLinuxFileName(fileName string) string {
|
||||
if fileName == "" {
|
||||
|
@ -42,6 +42,13 @@ type TenantDao interface {
|
||||
GetTenantIDsByNames(names []string) ([]string, error)
|
||||
}
|
||||
|
||||
//TenantDao tenant dao
|
||||
type AppDao interface {
|
||||
Dao
|
||||
DelDao
|
||||
Get(groupKey, version string) (interface{}, error)
|
||||
}
|
||||
|
||||
//LicenseDao LicenseDao
|
||||
type LicenseDao interface {
|
||||
Dao
|
||||
|
1
db/db.go
1
db/db.go
@ -33,6 +33,7 @@ type Manager interface {
|
||||
CloseManager() error
|
||||
Begin() *gorm.DB
|
||||
LicenseDao() dao.LicenseDao
|
||||
AppDao() dao.AppDao
|
||||
TenantDao() dao.TenantDao
|
||||
TenantDaoTransactions(db *gorm.DB) dao.TenantDao
|
||||
EventLogDao() dao.EventLogDao
|
||||
|
76
db/mysql/dao/app.go
Normal file
76
db/mysql/dao/app.go
Normal file
@ -0,0 +1,76 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/goodrain/rainbond/db/model"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type AppStatus struct {
|
||||
GroupKey string `gorm:"column:group_key;size:64;primary_key"`
|
||||
GroupName string `gorm:"column:group_name;size:64"`
|
||||
Version string `gorm:"column:version;size:32"`
|
||||
Format string `gorm:"column:format;size:32"` // only rainbond-app/docker-compose
|
||||
EventID string `gorm:"column:event_id;size:32"`
|
||||
SourceDir string `gorm:"column:source_dir;size:255"`
|
||||
Status string `gorm:"column:status;size:32"` // only exporting/importing/failed/success
|
||||
TarFile string `gorm:"column:tar_file;size:255"`
|
||||
TimeStamp int `gorm:"column:timestamp"`
|
||||
}
|
||||
|
||||
//TableName 表名
|
||||
func (t *AppStatus) TableName() string {
|
||||
return "app_status"
|
||||
}
|
||||
|
||||
type AppDaoImpl struct {
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
func (a *AppDaoImpl) AddModel(mo model.Interface) error {
|
||||
app, ok := mo.(*AppStatus)
|
||||
if !ok {
|
||||
return errors.New("Failed to convert interface to AppStatus")
|
||||
}
|
||||
|
||||
var old AppStatus
|
||||
if ok := a.DB.Where("group_key = ? and version = ?", app.GroupKey, app.Version).Find(&old).RecordNotFound(); ok {
|
||||
if err := a.DB.Create(app).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AppDaoImpl) UpdateModel(mo model.Interface) error {
|
||||
app, ok := mo.(*AppStatus)
|
||||
if !ok {
|
||||
return errors.New("Failed to convert interface to AppStatus")
|
||||
}
|
||||
|
||||
return a.DB.Table(app.TableName()).
|
||||
Where("group_key = ? and version = ?", app.GroupKey, app.Version).
|
||||
Update(app).Error
|
||||
}
|
||||
|
||||
func (a *AppDaoImpl) DeleteModel(groupKey string, arg ...interface{}) error {
|
||||
if len(arg) < 1 {
|
||||
return errors.New("Must define version for delete AppStatus in mysql.")
|
||||
}
|
||||
|
||||
version, ok := arg[0].(string)
|
||||
if !ok {
|
||||
return errors.New("Failed to convert interface to string")
|
||||
}
|
||||
|
||||
var app AppStatus
|
||||
return a.DB.Where("group_key = ? and version = ?", groupKey, version).Delete(&app).Error
|
||||
}
|
||||
|
||||
func (a *AppDaoImpl) Get(groupKey, version string) (interface{}, error) {
|
||||
var app AppStatus
|
||||
err := a.DB.Where("group_key = ? and version = ?", groupKey, version).First(&app).Error
|
||||
|
||||
return &app, err
|
||||
}
|
@ -437,3 +437,10 @@ func (m *Manager) RegionProcotolsDaoTransactions(db *gorm.DB) dao.RegionProcotol
|
||||
DB: db,
|
||||
}
|
||||
}
|
||||
|
||||
//AppDao 应用导入导出数据
|
||||
func (m *Manager) AppDao() dao.AppDao {
|
||||
return &mysqldao.AppDaoImpl{
|
||||
DB: m.db,
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user