mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-31 14:08:06 +08:00
feat: 优化应用升级逻辑 (#1075)
This commit is contained in:
parent
72bc99bddc
commit
d20d5946f2
@ -59,14 +59,9 @@ type AppInstalledOperate struct {
|
||||
}
|
||||
|
||||
type AppInstalledUpdate struct {
|
||||
InstallId uint `json:"installId" validate:"required"`
|
||||
Params map[string]interface{} `json:"params" validate:"required"`
|
||||
Advanced bool `json:"advanced"`
|
||||
CpuQuota float64 `json:"cpuQuota"`
|
||||
MemoryLimit float64 `json:"memoryLimit"`
|
||||
MemoryUnit string `json:"memoryUnit"`
|
||||
ContainerName string `json:"containerName"`
|
||||
AllowPort bool `json:"allowPort"`
|
||||
InstallId uint `json:"installId" validate:"required"`
|
||||
Params map[string]interface{} `json:"params" validate:"required"`
|
||||
AppContainerConfig
|
||||
}
|
||||
|
||||
type PortUpdate struct {
|
||||
|
@ -2,6 +2,7 @@ package response
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
@ -84,10 +85,6 @@ type AppParam struct {
|
||||
}
|
||||
|
||||
type AppConfig struct {
|
||||
Params []AppParam `json:"params"`
|
||||
CpuQuota float64 `json:"cpuQuota"`
|
||||
MemoryLimit float64 `json:"memoryLimit"`
|
||||
MemoryUnit string `json:"memoryUnit"`
|
||||
ContainerName string `json:"containerName"`
|
||||
AllowPort bool `json:"allowPort"`
|
||||
Params []AppParam `json:"params"`
|
||||
request.AppContainerConfig
|
||||
}
|
||||
|
@ -5,25 +5,23 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"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/docker"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"gopkg.in/yaml.v3"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type AppService struct {
|
||||
@ -301,50 +299,11 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
||||
servicesMap[v] = servicesMap[k]
|
||||
delete(servicesMap, k)
|
||||
}
|
||||
serviceValue := servicesMap[appInstall.ServiceName].(map[string]interface{})
|
||||
if req.Advanced && (req.CpuQuota > 0 || req.MemoryLimit > 0) {
|
||||
deploy := map[string]interface{}{
|
||||
"resources": map[string]interface{}{
|
||||
"limits": map[string]interface{}{
|
||||
"cpus": "${CPUS}",
|
||||
"memory": "${MEMORY_LIMIT}",
|
||||
},
|
||||
},
|
||||
}
|
||||
req.Params[constant.CPUS] = "0"
|
||||
if req.CpuQuota > 0 {
|
||||
req.Params[constant.CPUS] = req.CpuQuota
|
||||
}
|
||||
req.Params[constant.MemoryLimit] = "0"
|
||||
if req.MemoryLimit > 0 {
|
||||
req.Params[constant.MemoryLimit] = strconv.FormatFloat(req.MemoryLimit, 'f', -1, 32) + req.MemoryUnit
|
||||
}
|
||||
serviceValue["deploy"] = deploy
|
||||
}
|
||||
|
||||
ports, ok := serviceValue["ports"].([]interface{})
|
||||
if ok {
|
||||
allowHost := "127.0.0.1"
|
||||
if req.AllowPort {
|
||||
allowHost = "0.0.0.0"
|
||||
}
|
||||
req.Params[constant.HostIP] = allowHost
|
||||
for i, port := range ports {
|
||||
portStr, portOK := port.(string)
|
||||
if !portOK {
|
||||
continue
|
||||
}
|
||||
portArray := strings.Split(portStr, ":")
|
||||
if len(portArray) == 2 {
|
||||
portArray = append([]string{"${HOST_IP}"}, portArray...)
|
||||
}
|
||||
ports[i] = strings.Join(portArray, ":")
|
||||
}
|
||||
serviceValue["ports"] = ports
|
||||
if err = addDockerComposeCommonParam(composeMap, appInstall.ServiceName, req.AppContainerConfig, req.Params); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
servicesMap[appInstall.ServiceName] = serviceValue
|
||||
|
||||
var (
|
||||
composeByte []byte
|
||||
paramByte []byte
|
||||
@ -379,14 +338,17 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
if err = downloadApp(app, appDetail, appInstall, req); err != nil {
|
||||
if err = copyData(app, appDetail, appInstall, req); err != nil {
|
||||
if appInstall.Status == constant.Installing {
|
||||
appInstall.Status = constant.Error
|
||||
appInstall.Message = err.Error()
|
||||
}
|
||||
_ = appInstallRepo.Save(ctx, appInstall)
|
||||
_ = appInstallRepo.Save(context.Background(), appInstall)
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
_, _ = http.Get(appDetail.DownloadCallBackUrl)
|
||||
}()
|
||||
upApp(appInstall)
|
||||
}()
|
||||
go updateToolApp(appInstall)
|
||||
|
@ -5,11 +5,11 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"gopkg.in/yaml.v3"
|
||||
"math"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -262,23 +262,17 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
|
||||
}
|
||||
}
|
||||
|
||||
backupDockerCompose := installed.DockerCompose
|
||||
if req.Advanced {
|
||||
if req.ContainerName != "" {
|
||||
req.Params[constant.ContainerName] = req.ContainerName
|
||||
composeMap := make(map[string]interface{})
|
||||
if err := addDockerComposeCommonParam(composeMap, installed.ServiceName, req.AppContainerConfig, req.Params); err != nil {
|
||||
return err
|
||||
}
|
||||
req.Params[constant.CPUS] = "0"
|
||||
if req.CpuQuota > 0 {
|
||||
req.Params[constant.CPUS] = req.CpuQuota
|
||||
composeByte, err := yaml.Marshal(composeMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Params[constant.MemoryLimit] = "0"
|
||||
if req.MemoryLimit > 0 {
|
||||
req.Params[constant.MemoryLimit] = strconv.FormatFloat(req.MemoryLimit, 'f', -1, 32) + req.MemoryUnit
|
||||
}
|
||||
allowHost := "127.0.0.1"
|
||||
if req.AllowPort {
|
||||
allowHost = "0.0.0.0"
|
||||
}
|
||||
req.Params[constant.HostIP] = allowHost
|
||||
installed.DockerCompose = string(composeByte)
|
||||
}
|
||||
|
||||
envPath := path.Join(installed.GetPath(), ".env")
|
||||
@ -286,6 +280,7 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
backupEnvMaps := oldEnvMaps
|
||||
handleMap(req.Params, oldEnvMaps)
|
||||
paramByte, err := json.Marshal(oldEnvMaps)
|
||||
if err != nil {
|
||||
@ -295,36 +290,42 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
|
||||
if err := env.Write(oldEnvMaps, envPath); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = appInstallRepo.Save(context.Background(), &installed)
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
_ = fileOp.WriteFile(installed.GetComposePath(), strings.NewReader(installed.DockerCompose), 0755)
|
||||
if err := rebuildApp(installed); err != nil {
|
||||
_ = env.Write(backupEnvMaps, envPath)
|
||||
_ = fileOp.WriteFile(installed.GetComposePath(), strings.NewReader(backupDockerCompose), 0755)
|
||||
return err
|
||||
}
|
||||
installed.Status = constant.Running
|
||||
_ = appInstallRepo.Save(context.Background(), &installed)
|
||||
|
||||
website, _ := websiteRepo.GetFirst(websiteRepo.WithAppInstallId(installed.ID))
|
||||
if changePort && website.ID != 0 && website.Status == constant.Running {
|
||||
nginxInstall, err := getNginxFull(&website)
|
||||
if err != nil {
|
||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
|
||||
}
|
||||
config := nginxInstall.SiteConfig.Config
|
||||
servers := config.FindServers()
|
||||
if len(servers) == 0 {
|
||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, errors.New("nginx config is not valid"))
|
||||
}
|
||||
server := servers[0]
|
||||
proxy := fmt.Sprintf("http://127.0.0.1:%d", installed.HttpPort)
|
||||
server.UpdateRootProxy([]string{proxy})
|
||||
|
||||
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
|
||||
}
|
||||
if err := nginxCheckAndReload(nginxInstall.SiteConfig.OldContent, config.FilePath, nginxInstall.Install.ContainerName); err != nil {
|
||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
|
||||
}
|
||||
}
|
||||
if changePort {
|
||||
go func() {
|
||||
_ = OperateFirewallPort(oldPorts, newPorts)
|
||||
nginxInstall, err := getNginxFull(&website)
|
||||
if err != nil {
|
||||
global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, err).Error())
|
||||
return
|
||||
}
|
||||
config := nginxInstall.SiteConfig.Config
|
||||
servers := config.FindServers()
|
||||
if len(servers) == 0 {
|
||||
global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, errors.New("nginx config is not valid")).Error())
|
||||
return
|
||||
}
|
||||
server := servers[0]
|
||||
proxy := fmt.Sprintf("http://127.0.0.1:%d", installed.HttpPort)
|
||||
server.UpdateRootProxy([]string{proxy})
|
||||
|
||||
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, err).Error())
|
||||
return
|
||||
}
|
||||
if err := nginxCheckAndReload(nginxInstall.SiteConfig.OldContent, config.FilePath, nginxInstall.Install.ContainerName); err != nil {
|
||||
global.LOG.Errorf(buserr.WithErr(constant.ErrUpdateBuWebsite, err).Error())
|
||||
return
|
||||
}
|
||||
}()
|
||||
}
|
||||
return nil
|
||||
@ -543,31 +544,10 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
|
||||
params = append(params, appParam)
|
||||
}
|
||||
}
|
||||
res.ContainerName = envs[constant.ContainerName].(string)
|
||||
res.AllowPort = envs[constant.HostIP].(string) == "0.0.0.0"
|
||||
numStr, ok := envs[constant.CPUS].(string)
|
||||
if ok {
|
||||
num, err := strconv.ParseFloat(numStr, 64)
|
||||
if err == nil {
|
||||
res.CpuQuota = num
|
||||
}
|
||||
}
|
||||
num64, ok := envs[constant.CPUS].(float64)
|
||||
if ok {
|
||||
res.CpuQuota = num64
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`(\d+(?:\.\d+)?)\s*([KMGT]?B)`)
|
||||
matches := re.FindStringSubmatch(envs[constant.MemoryLimit].(string))
|
||||
if len(matches) == 3 {
|
||||
num, err := strconv.ParseFloat(matches[1], 64)
|
||||
if err == nil {
|
||||
unit := matches[2]
|
||||
res.MemoryLimit = num
|
||||
res.MemoryUnit = unit
|
||||
}
|
||||
}
|
||||
config := getAppCommonConfig(envs)
|
||||
res.Params = params
|
||||
res.AppContainerConfig = config
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
|
@ -8,11 +8,14 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/subosito/gotenv"
|
||||
"gopkg.in/yaml.v3"
|
||||
"math"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -226,40 +229,110 @@ func upgradeInstall(installId uint, detailId uint) error {
|
||||
return err
|
||||
}
|
||||
|
||||
detailDir := path.Join(constant.ResourceDir, "apps", install.App.Resource, install.App.Key, detail.Version)
|
||||
if install.App.Resource == constant.AppResourceLocal {
|
||||
detailDir = path.Join(constant.ResourceDir, "localApps", strings.TrimPrefix(install.App.Key, "local"), "versions", detail.Version)
|
||||
}
|
||||
install.Status = constant.Upgrading
|
||||
|
||||
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
|
||||
}
|
||||
go func() {
|
||||
var upErr error
|
||||
defer func() {
|
||||
if upErr != nil {
|
||||
install.Status = constant.UpgradeErr
|
||||
install.Message = err.Error()
|
||||
_ = appInstallRepo.Save(context.Background(), &install)
|
||||
}
|
||||
}()
|
||||
|
||||
if out, err := compose.Down(install.GetComposePath()); err != nil {
|
||||
if out != "" {
|
||||
return errors.New(out)
|
||||
if upErr = downloadApp(install.App, detail, &install); upErr != nil {
|
||||
return
|
||||
}
|
||||
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)
|
||||
detailDir := path.Join(constant.ResourceDir, "apps", install.App.Resource, install.App.Key, detail.Version)
|
||||
if install.App.Resource == constant.AppResourceLocal {
|
||||
detailDir = path.Join(constant.ResourceDir, "apps", "local", strings.TrimPrefix(install.App.Key, "local"), detail.Version)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
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 {
|
||||
upErr = errors.New(string(stdout))
|
||||
return
|
||||
}
|
||||
upErr = err
|
||||
return
|
||||
}
|
||||
|
||||
composeMap := make(map[string]interface{})
|
||||
if upErr = yaml.Unmarshal([]byte(detail.DockerCompose), &composeMap); upErr != nil {
|
||||
return
|
||||
}
|
||||
value, ok := composeMap["services"]
|
||||
if !ok {
|
||||
upErr = buserr.New(constant.ErrFileParse)
|
||||
return
|
||||
}
|
||||
servicesMap := value.(map[string]interface{})
|
||||
index := 0
|
||||
oldServiceName := ""
|
||||
for k := range servicesMap {
|
||||
oldServiceName = k
|
||||
index++
|
||||
if index > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
servicesMap[install.ServiceName] = servicesMap[oldServiceName]
|
||||
delete(servicesMap, oldServiceName)
|
||||
|
||||
envs := make(map[string]interface{})
|
||||
if upErr = json.Unmarshal([]byte(install.Env), &envs); upErr != nil {
|
||||
return
|
||||
}
|
||||
config := getAppCommonConfig(envs)
|
||||
config.Advanced = true
|
||||
if upErr = addDockerComposeCommonParam(composeMap, install.ServiceName, config, envs); upErr != nil {
|
||||
return
|
||||
}
|
||||
paramByte, upErr := json.Marshal(envs)
|
||||
if upErr != nil {
|
||||
return
|
||||
}
|
||||
install.Env = string(paramByte)
|
||||
composeByte, upErr := yaml.Marshal(composeMap)
|
||||
if upErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
install.DockerCompose = string(composeByte)
|
||||
install.Version = detail.Version
|
||||
install.AppDetailId = detailId
|
||||
|
||||
go func() {
|
||||
_, _ = http.Get(detail.DownloadCallBackUrl)
|
||||
}()
|
||||
|
||||
if out, err := compose.Down(install.GetComposePath()); err != nil {
|
||||
if out != "" {
|
||||
upErr = errors.New(out)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
if upErr = fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); upErr != nil {
|
||||
return
|
||||
}
|
||||
if out, err := compose.Up(install.GetComposePath()); err != nil {
|
||||
if out != "" {
|
||||
upErr = errors.New(out)
|
||||
return
|
||||
}
|
||||
upErr = err
|
||||
return
|
||||
}
|
||||
install.Status = constant.Running
|
||||
_ = appInstallRepo.Save(context.Background(), &install)
|
||||
}()
|
||||
|
||||
return appInstallRepo.Save(context.Background(), &install)
|
||||
}
|
||||
|
||||
@ -332,32 +405,51 @@ func handleMap(params map[string]interface{}, envParams map[string]string) {
|
||||
}
|
||||
}
|
||||
|
||||
func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall, req request.AppInstallCreate) (err error) {
|
||||
func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall) (err error) {
|
||||
appResourceDir := path.Join(constant.AppResourceDir, app.Resource)
|
||||
if !appDetail.Update {
|
||||
return
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
appDownloadDir := path.Join(appResourceDir, app.Key)
|
||||
if !fileOp.Stat(appDownloadDir) {
|
||||
_ = fileOp.CreateDir(appDownloadDir, 0755)
|
||||
}
|
||||
appVersionDir := path.Join(appDownloadDir, appDetail.Version)
|
||||
if !fileOp.Stat(appVersionDir) {
|
||||
_ = fileOp.CreateDir(appVersionDir, 0755)
|
||||
}
|
||||
global.LOG.Infof("download app[%s] from %s", app.Name, appDetail.DownloadUrl)
|
||||
filePath := path.Join(appVersionDir, appDetail.Version+".tar.gz")
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
appInstall.Status = constant.DownloadErr
|
||||
appInstall.Message = err.Error()
|
||||
}
|
||||
}()
|
||||
|
||||
if err = fileOp.DownloadFile(appDetail.DownloadUrl, filePath); err != nil {
|
||||
global.LOG.Errorf("download app[%s] error %v", app.Name, err)
|
||||
return
|
||||
}
|
||||
if err = fileOp.Decompress(filePath, appVersionDir, files.TarGz); err != nil {
|
||||
global.LOG.Errorf("decompress app[%s] error %v", app.Name, err)
|
||||
return
|
||||
}
|
||||
_ = fileOp.DeleteFile(filePath)
|
||||
return
|
||||
}
|
||||
|
||||
func copyData(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall, req request.AppInstallCreate) (err error) {
|
||||
fileOp := files.NewFileOp()
|
||||
appResourceDir := path.Join(constant.AppResourceDir, app.Resource)
|
||||
|
||||
if app.Resource == constant.AppResourceRemote && appDetail.Update {
|
||||
appDownloadDir := path.Join(appResourceDir, app.Key)
|
||||
if !fileOp.Stat(appDownloadDir) {
|
||||
_ = fileOp.CreateDir(appDownloadDir, 0755)
|
||||
}
|
||||
appVersionDir := path.Join(appDownloadDir, appDetail.Version)
|
||||
if !fileOp.Stat(appVersionDir) {
|
||||
_ = fileOp.CreateDir(appVersionDir, 0755)
|
||||
}
|
||||
global.LOG.Infof("download app[%s] from %s", app.Name, appDetail.DownloadUrl)
|
||||
filePath := path.Join(appVersionDir, appDetail.Version+".tar.gz")
|
||||
if err = fileOp.DownloadFile(appDetail.DownloadUrl, filePath); err != nil {
|
||||
appInstall.Status = constant.DownloadErr
|
||||
global.LOG.Errorf("download app[%s] error %v", app.Name, err)
|
||||
if app.Resource == constant.AppResourceRemote {
|
||||
err = downloadApp(app, appDetail, appInstall)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = fileOp.Decompress(filePath, appVersionDir, files.TarGz); err != nil {
|
||||
global.LOG.Errorf("decompress app[%s] error %v", app.Name, err)
|
||||
appInstall.Status = constant.DownloadErr
|
||||
return
|
||||
}
|
||||
_ = fileOp.DeleteFile(filePath)
|
||||
}
|
||||
appKey := app.Key
|
||||
installAppDir := path.Join(constant.AppInstallDir, app.Key)
|
||||
@ -639,3 +731,108 @@ func updateToolApp(installed *model.AppInstall) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func addDockerComposeCommonParam(composeMap map[string]interface{}, serviceName string, req request.AppContainerConfig, params map[string]interface{}) error {
|
||||
services, serviceValid := composeMap["services"].(map[string]interface{})
|
||||
if !serviceValid {
|
||||
return buserr.New(constant.ErrFileParse)
|
||||
}
|
||||
service, serviceExist := services[serviceName]
|
||||
if !serviceExist {
|
||||
return buserr.New(constant.ErrFileParse)
|
||||
}
|
||||
serviceValue := service.(map[string]interface{})
|
||||
deploy := map[string]interface{}{
|
||||
"resources": map[string]interface{}{
|
||||
"limits": map[string]interface{}{
|
||||
"cpus": "${CPUS}",
|
||||
"memory": "${MEMORY_LIMIT}",
|
||||
},
|
||||
},
|
||||
}
|
||||
serviceValue["deploy"] = deploy
|
||||
|
||||
ports, ok := serviceValue["ports"].([]interface{})
|
||||
if ok {
|
||||
for i, port := range ports {
|
||||
portStr, portOK := port.(string)
|
||||
if !portOK {
|
||||
continue
|
||||
}
|
||||
portArray := strings.Split(portStr, ":")
|
||||
if len(portArray) == 2 {
|
||||
portArray = append([]string{"${HOST_IP}"}, portArray...)
|
||||
}
|
||||
ports[i] = strings.Join(portArray, ":")
|
||||
}
|
||||
serviceValue["ports"] = ports
|
||||
}
|
||||
|
||||
params[constant.CPUS] = "0"
|
||||
params[constant.MemoryLimit] = "0"
|
||||
if req.Advanced {
|
||||
if req.CpuQuota > 0 {
|
||||
params[constant.CPUS] = req.CpuQuota
|
||||
}
|
||||
if req.MemoryLimit > 0 {
|
||||
params[constant.MemoryLimit] = strconv.FormatFloat(req.MemoryLimit, 'f', -1, 32) + req.MemoryUnit
|
||||
}
|
||||
}
|
||||
_, portExist := serviceValue["ports"].([]interface{})
|
||||
if portExist {
|
||||
allowHost := "127.0.0.1"
|
||||
if req.Advanced && req.AllowPort {
|
||||
allowHost = "0.0.0.0"
|
||||
}
|
||||
params[constant.HostIP] = allowHost
|
||||
}
|
||||
services[serviceName] = serviceValue
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAppCommonConfig(envs map[string]interface{}) request.AppContainerConfig {
|
||||
config := request.AppContainerConfig{}
|
||||
|
||||
if hostIp, ok := envs[constant.HostIP]; ok {
|
||||
config.AllowPort = hostIp.(string) == "0.0.0.0"
|
||||
} else {
|
||||
config.AllowPort = true
|
||||
}
|
||||
if cpuCore, ok := envs[constant.CPUS]; ok {
|
||||
numStr, ok := cpuCore.(string)
|
||||
if ok {
|
||||
num, err := strconv.ParseFloat(numStr, 64)
|
||||
if err == nil {
|
||||
config.CpuQuota = num
|
||||
}
|
||||
} else {
|
||||
num64, flOk := cpuCore.(float64)
|
||||
if flOk {
|
||||
config.CpuQuota = num64
|
||||
}
|
||||
}
|
||||
} else {
|
||||
config.CpuQuota = 0
|
||||
}
|
||||
if memLimit, ok := envs[constant.MemoryLimit]; ok {
|
||||
re := regexp.MustCompile(`(\d+(?:\.\d+)?)\s*([KMGT]?B)`)
|
||||
matches := re.FindStringSubmatch(memLimit.(string))
|
||||
if len(matches) == 3 {
|
||||
num, err := strconv.ParseFloat(matches[1], 64)
|
||||
if err == nil {
|
||||
unit := matches[2]
|
||||
config.MemoryLimit = num
|
||||
config.MemoryUnit = unit
|
||||
}
|
||||
}
|
||||
} else {
|
||||
config.MemoryLimit = 0
|
||||
config.MemoryUnit = "M"
|
||||
}
|
||||
|
||||
if containerName, ok := envs[constant.ContainerName]; ok {
|
||||
config.ContainerName = containerName.(string)
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ const (
|
||||
Syncing = "Syncing"
|
||||
DownloadErr = "DownloadErr"
|
||||
DirNotFound = "DirNotFound"
|
||||
Upgrading = "Upgrading"
|
||||
UpgradeErr = "UpgradeErr"
|
||||
|
||||
ContainerPrefix = "1Panel-"
|
||||
|
||||
|
@ -5,31 +5,31 @@ import (
|
||||
)
|
||||
|
||||
func Up(filePath string) (string, error) {
|
||||
stdout, err := cmd.Execf("docker-compose -f %s up -d", filePath)
|
||||
stdout, err := cmd.Execf("docker compose -f %s up -d", filePath)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
func Down(filePath string) (string, error) {
|
||||
stdout, err := cmd.Execf("docker-compose -f %s down --remove-orphans", filePath)
|
||||
stdout, err := cmd.Execf("docker compose -f %s down --remove-orphans", filePath)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
func Start(filePath string) (string, error) {
|
||||
stdout, err := cmd.Execf("docker-compose -f %s start", filePath)
|
||||
stdout, err := cmd.Execf("docker compose -f %s start", filePath)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
func Stop(filePath string) (string, error) {
|
||||
stdout, err := cmd.Execf("docker-compose -f %s stop", filePath)
|
||||
stdout, err := cmd.Execf("docker compose -f %s stop", filePath)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
func Restart(filePath string) (string, error) {
|
||||
stdout, err := cmd.Execf("docker-compose -f %s restart", filePath)
|
||||
stdout, err := cmd.Execf("docker compose -f %s restart", filePath)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
func Operate(filePath, operation string) (string, error) {
|
||||
stdout, err := cmd.Execf("docker-compose -f %s %s", filePath, operation)
|
||||
stdout, err := cmd.Execf("docker compose -f %s %s", filePath, operation)
|
||||
return stdout, err
|
||||
}
|
||||
|
@ -477,12 +477,16 @@ func (f FileOp) Compress(srcRiles []string, dst string, name string, cType Compr
|
||||
return nil
|
||||
}
|
||||
|
||||
func isIgnoreFile(name string) bool {
|
||||
return strings.HasPrefix(name, "__MACOSX") || strings.HasSuffix(name, ".DS_Store") || strings.HasPrefix(name, "._")
|
||||
}
|
||||
|
||||
func (f FileOp) Decompress(srcFile string, dst string, cType CompressType) error {
|
||||
format := getFormat(cType)
|
||||
|
||||
handler := func(ctx context.Context, archFile archiver.File) error {
|
||||
info := archFile.FileInfo
|
||||
if strings.HasPrefix(archFile.NameInArchive, "__MACOSX") {
|
||||
if isIgnoreFile(archFile.Name()) {
|
||||
return nil
|
||||
}
|
||||
filePath := filepath.Join(dst, archFile.NameInArchive)
|
||||
|
@ -19,11 +19,12 @@ const props = defineProps({
|
||||
let status = ref('running');
|
||||
|
||||
const getType = (status: string) => {
|
||||
if (status.includes('error') || status.includes('err')) {
|
||||
return 'danger';
|
||||
}
|
||||
switch (status) {
|
||||
case 'running':
|
||||
return 'success';
|
||||
case 'error':
|
||||
return 'danger';
|
||||
case 'stopped':
|
||||
return 'danger';
|
||||
default:
|
||||
@ -31,7 +32,7 @@ const getType = (status: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
const loadingStatus = ['installing', 'building', 'restarting'];
|
||||
const loadingStatus = ['installing', 'building', 'restarting', 'upgrading'];
|
||||
|
||||
const loadingIcon = (status: string): boolean => {
|
||||
return loadingStatus.indexOf(status) > -1;
|
||||
|
@ -196,6 +196,8 @@ const message = {
|
||||
disabled: 'Disabled',
|
||||
normal: 'Normal',
|
||||
building: 'Building',
|
||||
downloaderr: 'Download Error',
|
||||
upgrading: 'Upgrading',
|
||||
},
|
||||
},
|
||||
menu: {
|
||||
@ -1091,6 +1093,7 @@ const message = {
|
||||
'Allowing external port access will release the firewall port, please do not release the php operating environment',
|
||||
appInstallWarn:
|
||||
'The application does not release the external access port by default, you can choose to release it in the advanced settings',
|
||||
upgradeStart: 'Start upgrading! Please refresh the page later',
|
||||
},
|
||||
website: {
|
||||
website: 'Website',
|
||||
|
@ -201,6 +201,8 @@ const message = {
|
||||
disabled: '已停止',
|
||||
normal: '正常',
|
||||
building: '制作镜像中',
|
||||
downloaderr: '下载失败',
|
||||
upgrading: '升级中',
|
||||
},
|
||||
units: {
|
||||
second: '秒',
|
||||
@ -1089,6 +1091,7 @@ const message = {
|
||||
allowPort: '端口外部访问',
|
||||
allowPortHelper: '允许外部端口访问会放开防火墙端口,php运行环境请勿放开',
|
||||
appInstallWarn: '应用默认不放开外部访问端口,可以在高级设置中选择放开',
|
||||
upgradeStart: '开始升级!请稍后刷新页面',
|
||||
},
|
||||
website: {
|
||||
website: '网站',
|
||||
|
@ -24,6 +24,9 @@
|
||||
float: right;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.msg {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.d-description {
|
||||
|
@ -179,7 +179,7 @@ const get = async () => {
|
||||
}
|
||||
paramModel.value.memoryLimit = res.data.memoryLimit;
|
||||
paramModel.value.cpuQuota = res.data.cpuQuota;
|
||||
paramModel.value.memoryUnit = res.data.memoryUnit;
|
||||
paramModel.value.memoryUnit = res.data.memoryUnit !== '' ? res.data.memoryUnit : 'MB';
|
||||
paramModel.value.allowPort = res.data.allowPort;
|
||||
paramModel.value.containerName = res.data.containerName;
|
||||
paramModel.value.advanced = false;
|
||||
|
@ -81,25 +81,25 @@
|
||||
<el-col :xs="21" :sm="21" :md="21" :lg="20" :xl="20">
|
||||
<div class="a-detail">
|
||||
<div class="d-name">
|
||||
<span class="name">{{ installed.name }}</span>
|
||||
<el-button link type="info">
|
||||
<span class="name">{{ installed.name }}</span>
|
||||
</el-button>
|
||||
|
||||
<span class="status">
|
||||
<Status :key="installed.status" :status="installed.status"></Status>
|
||||
</span>
|
||||
<span class="msg">
|
||||
<el-popover
|
||||
v-if="installed.status === 'Error'"
|
||||
v-if="isAppErr(installed)"
|
||||
placement="bottom"
|
||||
:width="400"
|
||||
trigger="hover"
|
||||
:content="installed.message"
|
||||
>
|
||||
<template #reference>
|
||||
<Status
|
||||
:key="installed.status"
|
||||
:status="installed.status"
|
||||
></Status>
|
||||
<el-button link type="primary">详情</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
<span v-else>
|
||||
<Status :key="installed.status" :status="installed.status"></Status>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<el-button
|
||||
@ -132,6 +132,7 @@
|
||||
plain
|
||||
round
|
||||
size="small"
|
||||
:disabled="installed.status === 'Upgrading'"
|
||||
@click="openOperate(installed, 'upgrade')"
|
||||
v-if="mode === 'upgrade'"
|
||||
>
|
||||
@ -337,18 +338,27 @@ const buttons = [
|
||||
click: (row: any) => {
|
||||
openOperate(row, 'sync');
|
||||
},
|
||||
disabled: (row: any) => {
|
||||
return row.status === 'DownloadErr' || row.status === 'Upgrading';
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('app.rebuild'),
|
||||
click: (row: any) => {
|
||||
openOperate(row, 'rebuild');
|
||||
},
|
||||
disabled: (row: any) => {
|
||||
return row.status === 'DownloadErr' || row.status === 'Upgrading';
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('app.restart'),
|
||||
click: (row: any) => {
|
||||
openOperate(row, 'restart');
|
||||
},
|
||||
disabled: (row: any) => {
|
||||
return row.status === 'DownloadErr' || row.status === 'Upgrading';
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('app.start'),
|
||||
@ -356,7 +366,12 @@ const buttons = [
|
||||
openOperate(row, 'start');
|
||||
},
|
||||
disabled: (row: any) => {
|
||||
return row.status === 'Running' || row.status === 'Error';
|
||||
return (
|
||||
row.status === 'Running' ||
|
||||
row.status === 'Error' ||
|
||||
row.status === 'DownloadErr' ||
|
||||
row.status === 'Upgrading'
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -365,7 +380,7 @@ const buttons = [
|
||||
openOperate(row, 'stop');
|
||||
},
|
||||
disabled: (row: any) => {
|
||||
return row.status !== 'Running';
|
||||
return row.status !== 'Running' || row.status === 'DownloadErr' || row.status === 'Upgrading';
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -379,6 +394,9 @@ const buttons = [
|
||||
click: (row: any) => {
|
||||
openParam(row.id);
|
||||
},
|
||||
disabled: (row: any) => {
|
||||
return row.status === 'DownloadErr' || row.status === 'Upgrading';
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@ -404,6 +422,10 @@ const openParam = (installId: number) => {
|
||||
appParamRef.value.acceptParams({ id: installId });
|
||||
};
|
||||
|
||||
const isAppErr = (row: any) => {
|
||||
return row.status.includes('Err') || row.status.includes('Error');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const path = router.currentRoute.value.path;
|
||||
if (path == '/apps/upgrade') {
|
||||
|
@ -83,7 +83,7 @@ const operate = async () => {
|
||||
loading.value = true;
|
||||
await InstalledOp(operateReq)
|
||||
.then(() => {
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
MsgSuccess(i18n.global.t('app.upgradeStart'));
|
||||
bus.emit('upgrade', true);
|
||||
handleClose();
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user