mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-27 16:00:17 +00:00
fix: docker image pull and stop (#14202)
This commit is contained in:
@@ -647,7 +647,7 @@ func (r *RuntimeService) Get(id uint) (*response.RuntimeDTO, error) {
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
|
||||
func (r *RuntimeService) Update(req request.RuntimeUpdate) (err error) {
|
||||
runtime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -656,6 +656,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
|
||||
runtime.Version = req.Version
|
||||
return runtimeRepo.Save(runtime)
|
||||
}
|
||||
oldRuntime := *runtime
|
||||
appDetail, err := getRuntimeUpdateAppDetail(runtime, req)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -700,6 +701,20 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
|
||||
if err = downloadApp(app, appDetail, nil, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
var snapshot *runtimeUpdateSnapshot
|
||||
if runtime.Type != constant.RuntimePHP {
|
||||
snapshot, err = snapshotRuntimeUpdate(&oldRuntime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if restoreErr := snapshot.restoreFiles(); restoreErr != nil {
|
||||
err = fmt.Errorf("%w; restore runtime configuration: %v", err, restoreErr)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
if versionChanged {
|
||||
if err = updateRuntimeVersionFiles(runtime, appVersionDir); err != nil {
|
||||
return err
|
||||
@@ -773,7 +788,13 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
|
||||
if err = runtimeRepo.Save(runtime); err != nil {
|
||||
return err
|
||||
}
|
||||
go reCreateRuntime(runtime)
|
||||
go func() {
|
||||
if err := prepareRuntimeUpdate(runtime, snapshot); err != nil {
|
||||
global.LOG.Errorf("prepare runtime [%s] update failed: %v", runtime.Name, err)
|
||||
return
|
||||
}
|
||||
reCreateRuntime(runtime)
|
||||
}()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||
@@ -12,6 +15,89 @@ import (
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Keep the on-disk configuration as well as the model: files may contain edits
|
||||
// that have not been reflected in the database.
|
||||
type runtimeUpdateSnapshot struct {
|
||||
runtime model.Runtime
|
||||
files []runtimeUpdateFile
|
||||
}
|
||||
|
||||
type runtimeUpdateFile struct {
|
||||
path string
|
||||
content []byte
|
||||
mode os.FileMode
|
||||
exists bool
|
||||
}
|
||||
|
||||
func snapshotRuntimeUpdate(runtime *model.Runtime) (*runtimeUpdateSnapshot, error) {
|
||||
snapshot := &runtimeUpdateSnapshot{runtime: *runtime}
|
||||
for _, name := range []string{"docker-compose.yml", ".env", "run.sh"} {
|
||||
file := runtimeUpdateFile{path: filepath.Join(runtime.GetPath(), name)}
|
||||
info, err := os.Stat(file.path)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
snapshot.files = append(snapshot.files, file)
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
file.content, err = os.ReadFile(file.path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
file.exists, file.mode = true, info.Mode().Perm()
|
||||
snapshot.files = append(snapshot.files, file)
|
||||
}
|
||||
return snapshot, nil
|
||||
}
|
||||
|
||||
func (s *runtimeUpdateSnapshot) restoreFiles() error {
|
||||
var result error
|
||||
for _, file := range s.files {
|
||||
var err error
|
||||
if file.exists {
|
||||
err = os.WriteFile(file.path, file.content, file.mode)
|
||||
if err == nil {
|
||||
err = os.Chmod(file.path, file.mode)
|
||||
}
|
||||
} else {
|
||||
err = os.Remove(file.path)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
result = errors.Join(result, fmt.Errorf("restore %s: %w", file.path, err))
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// This must finish before reCreateRuntime stops the existing container. A
|
||||
// failed preparation restores the previous version without touching Docker.
|
||||
func prepareRuntimeUpdate(runtime *model.Runtime, snapshot *runtimeUpdateSnapshot) error {
|
||||
if err := pullRuntimeComposeImages(runtime.GetComposePath(), false, nil); err != nil {
|
||||
result := fmt.Errorf("prepare runtime images: %w", err)
|
||||
restoreErr := snapshot.restoreFiles()
|
||||
*runtime = snapshot.runtime
|
||||
if restoreErr != nil {
|
||||
result = errors.Join(result, restoreErr)
|
||||
runtime.Status = constant.StatusError
|
||||
}
|
||||
runtime.Message = result.Error()
|
||||
if saveErr := runtimeRepo.Save(runtime); saveErr != nil {
|
||||
result = errors.Join(result, fmt.Errorf("restore runtime record: %w", saveErr))
|
||||
}
|
||||
logFile, logErr := os.OpenFile(runtime.GetLogPath(), os.O_CREATE|os.O_WRONLY|os.O_APPEND, constant.FilePerm)
|
||||
if logErr == nil {
|
||||
_, logErr = fmt.Fprintln(logFile, result.Error())
|
||||
logErr = errors.Join(logErr, logFile.Close())
|
||||
}
|
||||
return errors.Join(result, logErr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getRuntimeUpdateAppDetail(runtime *model.Runtime, req request.RuntimeUpdate) (model.AppDetail, error) {
|
||||
current, err := appDetailRepo.GetFirst(repo.WithByID(runtime.AppDetailID))
|
||||
if err != nil {
|
||||
|
||||
@@ -104,6 +104,7 @@ func pullComposeImages(filePath string, forcePull bool, task *task.Task, project
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dockerCLi.Close()
|
||||
for _, image := range images {
|
||||
if !forcePull {
|
||||
if exist, _ := dockerCLi.ImageExists(image); exist {
|
||||
|
||||
@@ -345,10 +345,12 @@ func PullImage(imageName string) error {
|
||||
if authStr, ok := loadRegistryAuthFromDockerConfig(imageName); ok {
|
||||
options.RegistryAuth = authStr
|
||||
}
|
||||
if _, err := cli.ImagePull(context.Background(), imageName, options); err != nil {
|
||||
out, err := cli.ImagePull(context.Background(), imageName, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
defer out.Close()
|
||||
return handlePullImageProcess(out, nil)
|
||||
}
|
||||
|
||||
func loadRegistryAuthFromDockerConfig(imageName string) (string, bool) {
|
||||
|
||||
Reference in New Issue
Block a user