mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-27 01:05:57 +08:00
feat: 优化应用升级逻辑 (#2943)
This commit is contained in:
parent
9d637c0397
commit
ff6ede9429
3 changed files with 71 additions and 61 deletions
|
|
@ -336,7 +336,6 @@ func deleteAppInstall(install model.AppInstall, deleteBackup bool, forceDelete b
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteLink(ctx context.Context, install *model.AppInstall, deleteDB bool, forceDelete bool, deleteBackup bool) error {
|
func deleteLink(ctx context.Context, install *model.AppInstall, deleteDB bool, forceDelete bool, deleteBackup bool) error {
|
||||||
|
|
||||||
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
|
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
|
||||||
if len(resources) == 0 {
|
if len(resources) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -383,33 +382,66 @@ func upgradeInstall(installId uint, detailId uint, backup bool) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var upErr error
|
var upErr error
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if upErr != nil {
|
if upErr != nil {
|
||||||
|
if backup {
|
||||||
|
if err := NewIBackupService().AppRecover(dto.CommonRecover{Name: install.App.Key, DetailName: install.Name, Type: "app", Source: constant.ResourceLocal}); err != nil {
|
||||||
|
global.LOG.Errorf("recover app [%s] [%s] failed %v", install.App.Key, install.Name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
install.Status = constant.UpgradeErr
|
install.Status = constant.UpgradeErr
|
||||||
install.Message = upErr.Error()
|
install.Message = upErr.Error()
|
||||||
_ = appInstallRepo.Save(context.Background(), &install)
|
_ = appInstallRepo.Save(context.Background(), &install)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
fileOp := files.NewFileOp()
|
||||||
detailDir := path.Join(constant.ResourceDir, "apps", install.App.Resource, install.App.Key, detail.Version)
|
detailDir := path.Join(constant.ResourceDir, "apps", install.App.Resource, install.App.Key, detail.Version)
|
||||||
if install.App.Resource == constant.AppResourceRemote {
|
if install.App.Resource == constant.AppResourceRemote {
|
||||||
if upErr = downloadApp(install.App, detail, &install); upErr != nil {
|
if upErr = downloadApp(install.App, detail, &install); upErr != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if detail.DockerCompose == "" {
|
||||||
|
composeDetail, err := fileOp.GetContent(path.Join(detailDir, "docker-compose.yml"))
|
||||||
|
if err != nil {
|
||||||
|
upErr = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
detail.DockerCompose = string(composeDetail)
|
||||||
|
_ = appDetailRepo.Update(context.Background(), detail)
|
||||||
|
}
|
||||||
go func() {
|
go func() {
|
||||||
_, _ = http.Get(detail.DownloadCallBackUrl)
|
_, _ = http.Get(detail.DownloadCallBackUrl)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
if install.App.Resource == constant.AppResourceLocal {
|
if install.App.Resource == constant.AppResourceLocal {
|
||||||
detailDir = path.Join(constant.ResourceDir, "apps", "local", strings.TrimPrefix(install.App.Key, "local"), detail.Version)
|
detailDir = path.Join(constant.ResourceDir, "apps", "local", strings.TrimPrefix(install.App.Key, "local"), detail.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
images, err := composeV2.GetDockerComposeImages([]byte(detail.DockerCompose))
|
||||||
|
if err != nil {
|
||||||
|
upErr = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dockerCli, err := composeV2.NewClient()
|
||||||
|
if err != nil {
|
||||||
|
upErr = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, image := range images {
|
||||||
|
if err = dockerCli.PullImage(image, true); err != nil {
|
||||||
|
upErr = buserr.WithNameAndErr("ErrDockerPullImage", "", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
command := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rn %s/* %s || true", detailDir, install.GetPath()))
|
command := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rn %s/* %s || true", detailDir, install.GetPath()))
|
||||||
stdout, _ := command.CombinedOutput()
|
stdout, _ := command.CombinedOutput()
|
||||||
if stdout != nil {
|
if stdout != nil {
|
||||||
global.LOG.Infof("upgrade app [%s] [%s] cp file log : %s ", install.App.Key, install.Name, string(stdout))
|
global.LOG.Infof("upgrade app [%s] [%s] cp file log : %s ", install.App.Key, install.Name, string(stdout))
|
||||||
}
|
}
|
||||||
fileOp := files.NewFileOp()
|
|
||||||
sourceScripts := path.Join(detailDir, "scripts")
|
sourceScripts := path.Join(detailDir, "scripts")
|
||||||
if fileOp.Stat(sourceScripts) {
|
if fileOp.Stat(sourceScripts) {
|
||||||
dstScripts := path.Join(install.GetPath(), "scripts")
|
dstScripts := path.Join(install.GetPath(), "scripts")
|
||||||
|
|
@ -419,16 +451,6 @@ func upgradeInstall(installId uint, detailId uint, backup bool) error {
|
||||||
_, _ = scriptCmd.CombinedOutput()
|
_, _ = scriptCmd.CombinedOutput()
|
||||||
}
|
}
|
||||||
|
|
||||||
if detail.DockerCompose == "" {
|
|
||||||
composeDetail, err := fileOp.GetContent(path.Join(detailDir, "docker-compose.yml"))
|
|
||||||
if err != nil {
|
|
||||||
upErr = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
detail.DockerCompose = string(composeDetail)
|
|
||||||
_ = appDetailRepo.Update(context.Background(), detail)
|
|
||||||
}
|
|
||||||
|
|
||||||
composeMap := make(map[string]interface{})
|
composeMap := make(map[string]interface{})
|
||||||
if upErr = yaml.Unmarshal([]byte(detail.DockerCompose), &composeMap); upErr != nil {
|
if upErr = yaml.Unmarshal([]byte(detail.DockerCompose), &composeMap); upErr != nil {
|
||||||
return
|
return
|
||||||
|
|
@ -481,24 +503,6 @@ func upgradeInstall(installId uint, detailId uint, backup bool) error {
|
||||||
install.Version = detail.Version
|
install.Version = detail.Version
|
||||||
install.AppDetailId = detailId
|
install.AppDetailId = detailId
|
||||||
|
|
||||||
images, err := getImages(install)
|
|
||||||
if err != nil {
|
|
||||||
upErr = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
dockerCli, err := composeV2.NewClient()
|
|
||||||
if err != nil {
|
|
||||||
upErr = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, image := range images {
|
|
||||||
if err = dockerCli.PullImage(image, true); err != nil {
|
|
||||||
upErr = buserr.WithNameAndErr("ErrDockerPullImage", "", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if out, err := compose.Down(install.GetComposePath()); err != nil {
|
if out, err := compose.Down(install.GetComposePath()); err != nil {
|
||||||
if out != "" {
|
if out != "" {
|
||||||
upErr = errors.New(out)
|
upErr = errors.New(out)
|
||||||
|
|
@ -560,26 +564,6 @@ func getContainerNames(install model.AppInstall) ([]string, error) {
|
||||||
return containerNames, nil
|
return containerNames, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getImages(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
|
|
||||||
}
|
|
||||||
imagesMap := make(map[string]struct{})
|
|
||||||
for _, service := range project.AllServices() {
|
|
||||||
imagesMap[service.Image] = struct{}{}
|
|
||||||
}
|
|
||||||
var images []string
|
|
||||||
for k := range imagesMap {
|
|
||||||
images = append(images, k)
|
|
||||||
}
|
|
||||||
return images, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func coverEnvJsonToStr(envJson string) (string, error) {
|
func coverEnvJsonToStr(envJson string) (string, error) {
|
||||||
envMap := make(map[string]interface{})
|
envMap := make(map[string]interface{})
|
||||||
_ = json.Unmarshal([]byte(envJson), &envMap)
|
_ = json.Unmarshal([]byte(envJson), &envMap)
|
||||||
|
|
@ -897,28 +881,28 @@ func handleLocalAppDetail(versionDir string, appDetail *model.AppDetail) error {
|
||||||
fileOp := files.NewFileOp()
|
fileOp := files.NewFileOp()
|
||||||
dockerComposePath := path.Join(versionDir, "docker-compose.yml")
|
dockerComposePath := path.Join(versionDir, "docker-compose.yml")
|
||||||
if !fileOp.Stat(dockerComposePath) {
|
if !fileOp.Stat(dockerComposePath) {
|
||||||
return errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "docker-compose.yml"}))
|
return buserr.WithName(constant.ErrFileNotFound, "docker-compose.yml")
|
||||||
}
|
}
|
||||||
dockerComposeByte, _ := fileOp.GetContent(dockerComposePath)
|
dockerComposeByte, _ := fileOp.GetContent(dockerComposePath)
|
||||||
if dockerComposeByte == nil {
|
if dockerComposeByte == nil {
|
||||||
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "docker-compose.yml"}))
|
return buserr.WithName(constant.ErrFileParseApp, "docker-compose.yml")
|
||||||
}
|
}
|
||||||
appDetail.DockerCompose = string(dockerComposeByte)
|
appDetail.DockerCompose = string(dockerComposeByte)
|
||||||
paramPath := path.Join(versionDir, "data.yml")
|
paramPath := path.Join(versionDir, "data.yml")
|
||||||
if !fileOp.Stat(paramPath) {
|
if !fileOp.Stat(paramPath) {
|
||||||
return errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "data.yml"}))
|
return buserr.WithName(constant.ErrFileNotFound, "data.yml")
|
||||||
}
|
}
|
||||||
paramByte, _ := fileOp.GetContent(paramPath)
|
paramByte, _ := fileOp.GetContent(paramPath)
|
||||||
if paramByte == nil {
|
if paramByte == nil {
|
||||||
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml"}))
|
return buserr.WithName(constant.ErrFileNotFound, "data.yml")
|
||||||
}
|
}
|
||||||
appParamConfig := dto.LocalAppParam{}
|
appParamConfig := dto.LocalAppParam{}
|
||||||
if err := yaml.Unmarshal(paramByte, &appParamConfig); err != nil {
|
if err := yaml.Unmarshal(paramByte, &appParamConfig); err != nil {
|
||||||
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml"}))
|
return buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
|
||||||
}
|
}
|
||||||
dataJson, err := json.Marshal(appParamConfig.AppParams)
|
dataJson, err := json.Marshal(appParamConfig.AppParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml", "err": err.Error()}))
|
return buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
|
||||||
}
|
}
|
||||||
appDetail.Params = string(dataJson)
|
appDetail.Params = string(dataJson)
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -928,22 +912,22 @@ func handleLocalApp(appDir string) (app *model.App, err error) {
|
||||||
fileOp := files.NewFileOp()
|
fileOp := files.NewFileOp()
|
||||||
configYamlPath := path.Join(appDir, "data.yml")
|
configYamlPath := path.Join(appDir, "data.yml")
|
||||||
if !fileOp.Stat(configYamlPath) {
|
if !fileOp.Stat(configYamlPath) {
|
||||||
err = errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "data.yml"}))
|
err = buserr.WithName(constant.ErrFileNotFound, "data.yml")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
iconPath := path.Join(appDir, "logo.png")
|
iconPath := path.Join(appDir, "logo.png")
|
||||||
if !fileOp.Stat(iconPath) {
|
if !fileOp.Stat(iconPath) {
|
||||||
err = errors.New(i18n.GetMsgWithMap("ErrFileNotFound", map[string]interface{}{"name": "logo.png"}))
|
err = buserr.WithName(constant.ErrFileNotFound, "logo.png")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
configYamlByte, err := fileOp.GetContent(configYamlPath)
|
configYamlByte, err := fileOp.GetContent(configYamlPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml", "err": err.Error()}))
|
err = buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
localAppDefine := dto.LocalAppAppDefine{}
|
localAppDefine := dto.LocalAppAppDefine{}
|
||||||
if err = yaml.Unmarshal(configYamlByte, &localAppDefine); err != nil {
|
if err = yaml.Unmarshal(configYamlByte, &localAppDefine); err != nil {
|
||||||
err = errors.New(i18n.GetMsgWithMap("ErrFileParseApp", map[string]interface{}{"name": "data.yml", "err": err.Error()}))
|
err = buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
app = &localAppDefine.AppProperty
|
app = &localAppDefine.AppProperty
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,8 @@ var (
|
||||||
ErrInstallDirNotFound = "ErrInstallDirNotFound"
|
ErrInstallDirNotFound = "ErrInstallDirNotFound"
|
||||||
ErrContainerName = "ErrContainerName"
|
ErrContainerName = "ErrContainerName"
|
||||||
ErrAppNameExist = "ErrAppNameExist"
|
ErrAppNameExist = "ErrAppNameExist"
|
||||||
|
ErrFileNotFound = "ErrFileNotFound"
|
||||||
|
ErrFileParseApp = "ErrFileParseApp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// website
|
// website
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
"github.com/docker/compose/v2/pkg/api"
|
"github.com/docker/compose/v2/pkg/api"
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -89,7 +90,7 @@ func GetComposeProject(projectName, workDir string, yml []byte, env []byte, skip
|
||||||
projectName = strings.ToLower(projectName)
|
projectName = strings.ToLower(projectName)
|
||||||
reg, _ := regexp.Compile(`[^a-z0-9_-]+`)
|
reg, _ := regexp.Compile(`[^a-z0-9_-]+`)
|
||||||
projectName = reg.ReplaceAllString(projectName, "")
|
projectName = reg.ReplaceAllString(projectName, "")
|
||||||
project, err := loader.Load(details, func(options *loader.Options) {
|
project, err := loader.LoadWithContext(context.Background(), details, func(options *loader.Options) {
|
||||||
options.SetProjectName(projectName, true)
|
options.SetProjectName(projectName, true)
|
||||||
options.ResolvePaths = true
|
options.ResolvePaths = true
|
||||||
options.SkipNormalization = skipNormalization
|
options.SkipNormalization = skipNormalization
|
||||||
|
|
@ -105,3 +106,26 @@ func getComposeTimeout() *time.Duration {
|
||||||
timeout := time.Minute * time.Duration(10)
|
timeout := time.Minute * time.Duration(10)
|
||||||
return &timeout
|
return &timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ComposeProject struct {
|
||||||
|
Version string
|
||||||
|
Services map[string]Service `yaml:"services"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
Image string `yaml:"image"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDockerComposeImages(data []byte) ([]string, error) {
|
||||||
|
var dc ComposeProject
|
||||||
|
err := yaml.Unmarshal(data, &dc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var images []string
|
||||||
|
for _, service := range dc.Services {
|
||||||
|
images = append(images, service.Image)
|
||||||
|
}
|
||||||
|
return images, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue