mirror of https://github.com/1Panel-dev/1Panel
appstorecrontabdatabasedockerdocker-composedocker-containerdocker-imagedocker-uifilemanagerlamplnmppanel
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
666 lines
18 KiB
666 lines
18 KiB
package service |
|
|
|
import ( |
|
"context" |
|
"encoding/json" |
|
"fmt" |
|
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper" |
|
"github.com/compose-spec/compose-go/types" |
|
"github.com/subosito/gotenv" |
|
"math" |
|
"os" |
|
"os/exec" |
|
"path" |
|
"reflect" |
|
"strconv" |
|
"strings" |
|
|
|
"github.com/1Panel-dev/1Panel/backend/app/repo" |
|
"github.com/1Panel-dev/1Panel/backend/utils/env" |
|
|
|
"github.com/1Panel-dev/1Panel/backend/app/dto/response" |
|
"github.com/1Panel-dev/1Panel/backend/buserr" |
|
|
|
"github.com/1Panel-dev/1Panel/backend/app/dto" |
|
"github.com/1Panel-dev/1Panel/backend/app/model" |
|
"github.com/1Panel-dev/1Panel/backend/constant" |
|
"github.com/1Panel-dev/1Panel/backend/global" |
|
"github.com/1Panel-dev/1Panel/backend/utils/common" |
|
"github.com/1Panel-dev/1Panel/backend/utils/compose" |
|
composeV2 "github.com/1Panel-dev/1Panel/backend/utils/docker" |
|
"github.com/1Panel-dev/1Panel/backend/utils/files" |
|
"github.com/pkg/errors" |
|
) |
|
|
|
type DatabaseOp string |
|
|
|
var ( |
|
Add DatabaseOp = "add" |
|
Delete DatabaseOp = "delete" |
|
) |
|
|
|
func checkPort(key string, params map[string]interface{}) (int, error) { |
|
port, ok := params[key] |
|
if ok { |
|
portN := int(math.Ceil(port.(float64))) |
|
|
|
oldInstalled, _ := appInstallRepo.ListBy(appInstallRepo.WithPort(portN)) |
|
if len(oldInstalled) > 0 { |
|
var apps []string |
|
for _, install := range oldInstalled { |
|
apps = append(apps, install.App.Name) |
|
} |
|
return portN, buserr.WithMap(constant.ErrPortInOtherApp, map[string]interface{}{"port": portN, "apps": apps}, nil) |
|
} |
|
if common.ScanPort(portN) { |
|
return portN, buserr.WithDetail(constant.ErrPortInUsed, portN, nil) |
|
} else { |
|
return portN, nil |
|
} |
|
} |
|
return 0, nil |
|
} |
|
|
|
func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall, params map[string]interface{}) error { |
|
var dbConfig dto.AppDatabase |
|
if app.Type == "runtime" { |
|
var authParam dto.AuthParam |
|
paramByte, err := json.Marshal(params) |
|
if err != nil { |
|
return err |
|
} |
|
if err := json.Unmarshal(paramByte, &authParam); err != nil { |
|
return err |
|
} |
|
if authParam.RootPassword != "" { |
|
authByte, err := json.Marshal(authParam) |
|
if err != nil { |
|
return err |
|
} |
|
appInstall.Param = string(authByte) |
|
} |
|
} |
|
if app.Type == "website" || app.Type == "tool" { |
|
paramByte, err := json.Marshal(params) |
|
if err != nil { |
|
return err |
|
} |
|
if err := json.Unmarshal(paramByte, &dbConfig); err != nil { |
|
return err |
|
} |
|
} |
|
|
|
if !reflect.DeepEqual(dbConfig, dto.AppDatabase{}) && dbConfig.ServiceName != "" { |
|
dbInstall, err := appInstallRepo.GetFirst(appInstallRepo.WithServiceName(dbConfig.ServiceName)) |
|
if err != nil { |
|
return err |
|
} |
|
var resourceId uint |
|
if dbConfig.DbName != "" && dbConfig.DbUser != "" && dbConfig.Password != "" { |
|
iMysqlRepo := repo.NewIMysqlRepo() |
|
oldMysqlDb, _ := iMysqlRepo.Get(commonRepo.WithByName(dbConfig.DbName)) |
|
resourceId = oldMysqlDb.ID |
|
if oldMysqlDb.ID > 0 { |
|
if oldMysqlDb.Username != dbConfig.DbUser || oldMysqlDb.Password != dbConfig.Password { |
|
return buserr.New(constant.ErrDbUserNotValid) |
|
} |
|
} else { |
|
var createMysql dto.MysqlDBCreate |
|
createMysql.Name = dbConfig.DbName |
|
createMysql.Username = dbConfig.DbUser |
|
createMysql.Format = "utf8mb4" |
|
createMysql.Permission = "%" |
|
createMysql.Password = dbConfig.Password |
|
mysqldb, err := NewIMysqlService().Create(ctx, createMysql) |
|
if err != nil { |
|
return err |
|
} |
|
resourceId = mysqldb.ID |
|
} |
|
} |
|
var installResource model.AppInstallResource |
|
installResource.ResourceId = resourceId |
|
installResource.AppInstallId = appInstall.ID |
|
installResource.LinkId = dbInstall.ID |
|
installResource.Key = dbInstall.App.Key |
|
if err := appInstallResourceRepo.Create(ctx, &installResource); err != nil { |
|
return err |
|
} |
|
} |
|
return nil |
|
} |
|
|
|
func handleAppInstallErr(ctx context.Context, install *model.AppInstall) error { |
|
op := files.NewFileOp() |
|
appDir := install.GetPath() |
|
dir, _ := os.Stat(appDir) |
|
if dir != nil { |
|
_, _ = compose.Down(install.GetComposePath()) |
|
if err := op.DeleteDir(appDir); err != nil { |
|
return err |
|
} |
|
} |
|
if err := deleteLink(ctx, install, true, true); err != nil { |
|
return err |
|
} |
|
return nil |
|
} |
|
|
|
func deleteAppInstall(install model.AppInstall, deleteBackup bool, forceDelete bool, deleteDB bool) error { |
|
op := files.NewFileOp() |
|
appDir := install.GetPath() |
|
dir, _ := os.Stat(appDir) |
|
if dir != nil { |
|
out, err := compose.Down(install.GetComposePath()) |
|
if err != nil && !forceDelete { |
|
return handleErr(install, err, out) |
|
} |
|
} |
|
tx, ctx := helper.GetTxAndContext() |
|
defer tx.Rollback() |
|
if err := appInstallRepo.Delete(ctx, install); err != nil { |
|
return err |
|
} |
|
if err := deleteLink(ctx, &install, deleteDB, forceDelete); err != nil && !forceDelete { |
|
return err |
|
} |
|
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType("app"), commonRepo.WithByName(install.App.Key), backupRepo.WithByDetailName(install.Name)) |
|
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType(install.App.Key)) |
|
if install.App.Key == constant.AppMysql { |
|
_ = mysqlRepo.DeleteAll(ctx) |
|
} |
|
uploadDir := fmt.Sprintf("%s/1panel/uploads/app/%s/%s", global.CONF.System.BaseDir, install.App.Key, install.Name) |
|
if _, err := os.Stat(uploadDir); err == nil { |
|
_ = os.RemoveAll(uploadDir) |
|
} |
|
if deleteBackup { |
|
localDir, _ := loadLocalDir() |
|
backupDir := fmt.Sprintf("%s/app/%s/%s", localDir, install.App.Key, install.Name) |
|
if _, err := os.Stat(backupDir); err == nil { |
|
_ = os.RemoveAll(backupDir) |
|
} |
|
global.LOG.Infof("delete app %s-%s backups successful", install.App.Key, install.Name) |
|
} |
|
_ = op.DeleteDir(appDir) |
|
tx.Commit() |
|
return nil |
|
} |
|
|
|
func deleteLink(ctx context.Context, install *model.AppInstall, deleteDB bool, forceDelete bool) error { |
|
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID)) |
|
if len(resources) == 0 { |
|
return nil |
|
} |
|
for _, re := range resources { |
|
mysqlService := NewIMysqlService() |
|
if re.Key == "mysql" && deleteDB { |
|
database, _ := mysqlRepo.Get(commonRepo.WithByID(re.ResourceId)) |
|
if reflect.DeepEqual(database, model.DatabaseMysql{}) { |
|
continue |
|
} |
|
if err := mysqlService.Delete(ctx, dto.MysqlDBDelete{ |
|
ID: database.ID, |
|
ForceDelete: forceDelete, |
|
}); err != nil && !forceDelete { |
|
return err |
|
} |
|
} |
|
} |
|
return appInstallResourceRepo.DeleteBy(ctx, appInstallResourceRepo.WithAppInstallId(install.ID)) |
|
} |
|
|
|
func upgradeInstall(installId uint, detailId uint) error { |
|
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId)) |
|
if err != nil { |
|
return err |
|
} |
|
detail, err := appDetailRepo.GetFirst(commonRepo.WithByID(detailId)) |
|
if err != nil { |
|
return err |
|
} |
|
if install.Version == detail.Version { |
|
return errors.New("two version is same") |
|
} |
|
if err := NewIBackupService().AppBackup(dto.CommonBackup{Name: install.App.Key, DetailName: install.Name}); err != nil { |
|
return err |
|
} |
|
|
|
detailDir := path.Join(constant.ResourceDir, "apps", install.App.Key, "versions", detail.Version) |
|
if install.App.Resource == constant.AppResourceLocal { |
|
detailDir = path.Join(constant.ResourceDir, "localApps", strings.TrimPrefix(install.App.Key, "local"), "versions", detail.Version) |
|
} |
|
|
|
cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rf %s/* %s", detailDir, install.GetPath())) |
|
stdout, err := cmd.CombinedOutput() |
|
if err != nil { |
|
if stdout != nil { |
|
return errors.New(string(stdout)) |
|
} |
|
return err |
|
} |
|
|
|
if out, err := compose.Down(install.GetComposePath()); err != nil { |
|
if out != "" { |
|
return errors.New(out) |
|
} |
|
return err |
|
} |
|
install.DockerCompose = detail.DockerCompose |
|
install.Version = detail.Version |
|
install.AppDetailId = detailId |
|
|
|
fileOp := files.NewFileOp() |
|
if err := fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); err != nil { |
|
return err |
|
} |
|
if out, err := compose.Up(install.GetComposePath()); err != nil { |
|
if out != "" { |
|
return errors.New(out) |
|
} |
|
return err |
|
} |
|
return appInstallRepo.Save(context.Background(), &install) |
|
} |
|
|
|
func getContainerNames(install model.AppInstall) ([]string, error) { |
|
envStr, err := coverEnvJsonToStr(install.Env) |
|
if err != nil { |
|
return nil, err |
|
} |
|
project, err := composeV2.GetComposeProject(install.Name, install.GetPath(), []byte(install.DockerCompose), []byte(envStr), true) |
|
if err != nil { |
|
return nil, err |
|
} |
|
containerMap := make(map[string]struct{}) |
|
containerMap[install.ContainerName] = struct{}{} |
|
for _, service := range project.AllServices() { |
|
if service.ContainerName == "${CONTAINER_NAME}" || service.ContainerName == "" { |
|
continue |
|
} |
|
containerMap[service.ContainerName] = struct{}{} |
|
} |
|
var containerNames []string |
|
for k := range containerMap { |
|
containerNames = append(containerNames, k) |
|
} |
|
return containerNames, nil |
|
} |
|
|
|
func coverEnvJsonToStr(envJson string) (string, error) { |
|
envMap := make(map[string]interface{}) |
|
_ = json.Unmarshal([]byte(envJson), &envMap) |
|
newEnvMap := make(map[string]string, len(envMap)) |
|
handleMap(envMap, newEnvMap) |
|
envStr, err := gotenv.Marshal(newEnvMap) |
|
if err != nil { |
|
return "", err |
|
} |
|
return envStr, nil |
|
} |
|
|
|
func checkLimit(app model.App) error { |
|
if app.Limit > 0 { |
|
installs, err := appInstallRepo.ListBy(appInstallRepo.WithAppId(app.ID)) |
|
if err != nil { |
|
return err |
|
} |
|
if len(installs) >= app.Limit { |
|
return buserr.New(constant.ErrAppLimit) |
|
} |
|
} |
|
return nil |
|
} |
|
|
|
func checkRequiredAndLimit(app model.App) error { |
|
if err := checkLimit(app); err != nil { |
|
return err |
|
} |
|
if app.Required != "" { |
|
var requiredArray []string |
|
if err := json.Unmarshal([]byte(app.Required), &requiredArray); err != nil { |
|
return err |
|
} |
|
for _, key := range requiredArray { |
|
if key == "" { |
|
continue |
|
} |
|
requireApp, err := appRepo.GetFirst(appRepo.WithKey(key)) |
|
if err != nil { |
|
return err |
|
} |
|
details, err := appDetailRepo.GetBy(appDetailRepo.WithAppId(requireApp.ID)) |
|
if err != nil { |
|
return err |
|
} |
|
var detailIds []uint |
|
for _, d := range details { |
|
detailIds = append(detailIds, d.ID) |
|
} |
|
|
|
_, err = appInstallRepo.GetFirst(appInstallRepo.WithDetailIdsIn(detailIds)) |
|
if err != nil { |
|
return buserr.WithDetail(constant.ErrAppRequired, requireApp.Name, nil) |
|
} |
|
} |
|
} |
|
return nil |
|
} |
|
|
|
func handleMap(params map[string]interface{}, envParams map[string]string) { |
|
for k, v := range params { |
|
switch t := v.(type) { |
|
case string: |
|
envParams[k] = t |
|
case float64: |
|
envParams[k] = strconv.FormatFloat(t, 'f', -1, 32) |
|
default: |
|
envParams[k] = t.(string) |
|
} |
|
} |
|
} |
|
|
|
func copyAppData(key, version, installName string, params map[string]interface{}, isLocal bool) (err error) { |
|
fileOp := files.NewFileOp() |
|
appResourceDir := constant.AppResourceDir |
|
installAppDir := path.Join(constant.AppInstallDir, key) |
|
appKey := key |
|
if isLocal { |
|
appResourceDir = constant.LocalAppResourceDir |
|
appKey = strings.TrimPrefix(key, "local") |
|
installAppDir = path.Join(constant.LocalAppInstallDir, appKey) |
|
} |
|
resourceDir := path.Join(appResourceDir, appKey, "versions", version) |
|
|
|
if !fileOp.Stat(installAppDir) { |
|
if err = fileOp.CreateDir(installAppDir, 0755); err != nil { |
|
return |
|
} |
|
} |
|
appDir := path.Join(installAppDir, installName) |
|
if fileOp.Stat(appDir) { |
|
if err = fileOp.DeleteDir(appDir); err != nil { |
|
return |
|
} |
|
} |
|
if err = fileOp.Copy(resourceDir, installAppDir); err != nil { |
|
return |
|
} |
|
versionDir := path.Join(installAppDir, version) |
|
if err = fileOp.Rename(versionDir, appDir); err != nil { |
|
return |
|
} |
|
envPath := path.Join(appDir, ".env") |
|
|
|
envParams := make(map[string]string, len(params)) |
|
handleMap(params, envParams) |
|
if err = env.Write(envParams, envPath); err != nil { |
|
return |
|
} |
|
return |
|
} |
|
|
|
// 处理文件夹权限等问题 |
|
func upAppPre(app model.App, appInstall *model.AppInstall) error { |
|
if app.Key == "nexus" { |
|
dataPath := path.Join(appInstall.GetPath(), "data") |
|
if err := files.NewFileOp().Chown(dataPath, 200, 0); err != nil { |
|
return err |
|
} |
|
} |
|
return nil |
|
} |
|
|
|
func getServiceFromInstall(appInstall *model.AppInstall) (service *composeV2.ComposeService, err error) { |
|
var ( |
|
project *types.Project |
|
envStr string |
|
) |
|
envStr, err = coverEnvJsonToStr(appInstall.Env) |
|
if err != nil { |
|
return |
|
} |
|
project, err = composeV2.GetComposeProject(appInstall.Name, appInstall.GetPath(), []byte(appInstall.DockerCompose), []byte(envStr), true) |
|
if err != nil { |
|
return |
|
} |
|
service, err = composeV2.NewComposeService() |
|
if err != nil { |
|
return |
|
} |
|
service.SetProject(project) |
|
return |
|
} |
|
|
|
func upApp(appInstall *model.AppInstall) { |
|
upProject := func(appInstall *model.AppInstall) (err error) { |
|
if err == nil { |
|
var composeService *composeV2.ComposeService |
|
composeService, err = getServiceFromInstall(appInstall) |
|
if err != nil { |
|
return err |
|
} |
|
err = composeService.ComposeUp() |
|
if err != nil { |
|
return err |
|
} |
|
return |
|
} else { |
|
return |
|
} |
|
} |
|
if err := upProject(appInstall); err != nil { |
|
appInstall.Status = constant.Error |
|
appInstall.Message = err.Error() |
|
} else { |
|
appInstall.Status = constant.Running |
|
} |
|
exist, _ := appInstallRepo.GetFirst(commonRepo.WithByID(appInstall.ID)) |
|
if exist.ID > 0 { |
|
_ = appInstallRepo.Save(context.Background(), appInstall) |
|
} |
|
} |
|
|
|
func rebuildApp(appInstall model.AppInstall) error { |
|
dockerComposePath := appInstall.GetComposePath() |
|
out, err := compose.Down(dockerComposePath) |
|
if err != nil { |
|
return handleErr(appInstall, err, out) |
|
} |
|
out, err = compose.Up(dockerComposePath) |
|
if err != nil { |
|
return handleErr(appInstall, err, out) |
|
} |
|
return syncById(appInstall.ID) |
|
} |
|
|
|
func getAppDetails(details []model.AppDetail, versions []string) map[string]model.AppDetail { |
|
appDetails := make(map[string]model.AppDetail, len(details)) |
|
for _, old := range details { |
|
old.Status = constant.AppTakeDown |
|
appDetails[old.Version] = old |
|
} |
|
|
|
for _, v := range versions { |
|
detail, ok := appDetails[v] |
|
if ok { |
|
detail.Status = constant.AppNormal |
|
appDetails[v] = detail |
|
} else { |
|
appDetails[v] = model.AppDetail{ |
|
Version: v, |
|
Status: constant.AppNormal, |
|
} |
|
} |
|
} |
|
return appDetails |
|
} |
|
|
|
func getApps(oldApps []model.App, items []dto.AppDefine, isLocal bool) map[string]model.App { |
|
apps := make(map[string]model.App, len(oldApps)) |
|
for _, old := range oldApps { |
|
old.Status = constant.AppTakeDown |
|
apps[old.Key] = old |
|
} |
|
for _, item := range items { |
|
key := item.Key |
|
if isLocal { |
|
key = "local" + key |
|
} |
|
app, ok := apps[key] |
|
if !ok { |
|
app = model.App{} |
|
} |
|
if isLocal { |
|
app.Resource = constant.AppResourceLocal |
|
} else { |
|
app.Resource = constant.AppResourceRemote |
|
} |
|
app.Name = item.Name |
|
app.Limit = item.Limit |
|
app.Key = key |
|
app.ShortDescZh = item.ShortDescZh |
|
app.ShortDescEn = item.ShortDescEn |
|
app.Website = item.Website |
|
app.Document = item.Document |
|
app.Github = item.Github |
|
app.Type = item.Type |
|
app.CrossVersionUpdate = item.CrossVersionUpdate |
|
app.Required = item.GetRequired() |
|
app.Status = constant.AppNormal |
|
apps[key] = app |
|
} |
|
return apps |
|
} |
|
|
|
func handleErr(install model.AppInstall, err error, out string) error { |
|
reErr := err |
|
install.Message = err.Error() |
|
if out != "" { |
|
install.Message = out |
|
reErr = errors.New(out) |
|
install.Status = constant.Error |
|
} |
|
_ = appInstallRepo.Save(context.Background(), &install) |
|
return reErr |
|
} |
|
|
|
func getAppFromRepo(downloadPath, version string) error { |
|
downloadUrl := downloadPath |
|
appDir := constant.AppResourceDir |
|
|
|
global.LOG.Infof("download file from %s", downloadUrl) |
|
fileOp := files.NewFileOp() |
|
if _, err := fileOp.CopyAndBackup(appDir); err != nil { |
|
return err |
|
} |
|
packagePath := path.Join(constant.ResourceDir, path.Base(downloadUrl)) |
|
if err := fileOp.DownloadFile(downloadUrl, packagePath); err != nil { |
|
return err |
|
} |
|
if err := fileOp.Decompress(packagePath, constant.ResourceDir, files.TarGz); err != nil { |
|
return err |
|
} |
|
_ = NewISettingService().Update("AppStoreVersion", version) |
|
defer func() { |
|
_ = fileOp.DeleteFile(packagePath) |
|
}() |
|
return nil |
|
} |
|
|
|
func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]response.AppInstalledDTO, error) { |
|
var res []response.AppInstalledDTO |
|
for _, installed := range appInstallList { |
|
installDTO := response.AppInstalledDTO{ |
|
AppInstall: installed, |
|
} |
|
app, err := appRepo.GetFirst(commonRepo.WithByID(installed.AppId)) |
|
if err != nil { |
|
return nil, err |
|
} |
|
details, err := appDetailRepo.GetBy(appDetailRepo.WithAppId(app.ID)) |
|
if err != nil { |
|
return nil, err |
|
} |
|
var versions []string |
|
for _, detail := range details { |
|
versions = append(versions, detail.Version) |
|
} |
|
versions = common.GetSortedVersions(versions) |
|
lastVersion := versions[0] |
|
if common.IsCrossVersion(installed.Version, lastVersion) { |
|
installDTO.CanUpdate = app.CrossVersionUpdate |
|
} else { |
|
installDTO.CanUpdate = common.CompareVersion(lastVersion, installed.Version) |
|
} |
|
if updated { |
|
if installDTO.CanUpdate { |
|
res = append(res, installDTO) |
|
} |
|
} else { |
|
res = append(res, installDTO) |
|
} |
|
} |
|
return res, nil |
|
} |
|
|
|
func getAppInstallByKey(key string) (model.AppInstall, error) { |
|
app, err := appRepo.GetFirst(appRepo.WithKey(key)) |
|
if err != nil { |
|
return model.AppInstall{}, err |
|
} |
|
appInstall, err := appInstallRepo.GetFirst(appInstallRepo.WithAppId(app.ID)) |
|
if err != nil { |
|
return model.AppInstall{}, err |
|
} |
|
return appInstall, nil |
|
} |
|
|
|
func updateToolApp(installed *model.AppInstall) { |
|
tooKey, ok := dto.AppToolMap[installed.App.Key] |
|
if !ok { |
|
return |
|
} |
|
toolInstall, _ := getAppInstallByKey(tooKey) |
|
if reflect.DeepEqual(toolInstall, model.AppInstall{}) { |
|
return |
|
} |
|
paramMap := make(map[string]string) |
|
_ = json.Unmarshal([]byte(installed.Param), ¶mMap) |
|
envMap := make(map[string]interface{}) |
|
_ = json.Unmarshal([]byte(toolInstall.Env), &envMap) |
|
if password, ok := paramMap["PANEL_DB_ROOT_PASSWORD"]; ok { |
|
envMap["PANEL_DB_ROOT_PASSWORD"] = password |
|
} |
|
if _, ok := envMap["PANEL_REDIS_HOST"]; ok { |
|
envMap["PANEL_REDIS_HOST"] = installed.ServiceName |
|
} |
|
if _, ok := envMap["PANEL_DB_HOST"]; ok { |
|
envMap["PANEL_DB_HOST"] = installed.ServiceName |
|
} |
|
|
|
envPath := path.Join(toolInstall.GetPath(), ".env") |
|
contentByte, err := json.Marshal(envMap) |
|
if err != nil { |
|
global.LOG.Errorf("update tool app [%s] error : %s", toolInstall.Name, err.Error()) |
|
return |
|
} |
|
envFileMap := make(map[string]string) |
|
handleMap(envMap, envFileMap) |
|
if err = env.Write(envFileMap, envPath); err != nil { |
|
global.LOG.Errorf("update tool app [%s] error : %s", toolInstall.Name, err.Error()) |
|
return |
|
} |
|
toolInstall.Env = string(contentByte) |
|
if err := appInstallRepo.Save(context.Background(), &toolInstall); err != nil { |
|
global.LOG.Errorf("update tool app [%s] error : %s", toolInstall.Name, err.Error()) |
|
return |
|
} |
|
if out, err := compose.Down(toolInstall.GetComposePath()); err != nil { |
|
global.LOG.Errorf("update tool app [%s] error : %s", toolInstall.Name, out) |
|
return |
|
} |
|
if out, err := compose.Up(toolInstall.GetComposePath()); err != nil { |
|
global.LOG.Errorf("update tool app [%s] error : %s", toolInstall.Name, out) |
|
return |
|
} |
|
}
|
|
|