From 5c2621aaa6091754c726264ad91042b0640c336c Mon Sep 17 00:00:00 2001 From: Jacky Date: Mon, 5 May 2025 12:22:44 +0000 Subject: [PATCH] feat(upgrader): add test commit and restart functionality --- app/src/views/system/Upgrade.vue | 5 ++ go.mod | 2 +- go.sum | 6 +- internal/upgrader/binary.go | 17 +++- internal/upgrader/test_commit_restart.go | 98 ++++++++++++++++++++++++ 5 files changed, 120 insertions(+), 8 deletions(-) create mode 100644 internal/upgrader/test_commit_restart.go diff --git a/app/src/views/system/Upgrade.vue b/app/src/views/system/Upgrade.vue index 483cdb64..22af217a 100644 --- a/app/src/views/system/Upgrade.vue +++ b/app/src/views/system/Upgrade.vue @@ -67,6 +67,10 @@ const dryRun = computed(() => { return !!route.query.dry_run }) +const testCommitAndRestart = computed(() => { + return !!route.query.test_commit_and_restart +}) + async function performUpgrade() { progressStatus.value = 'active' modalClosable.value = false @@ -84,6 +88,7 @@ async function performUpgrade() { ws.send(JSON.stringify({ dry_run: dryRun.value, channel: channel.value, + test_commit_and_restart: testCommitAndRestart.value, })) } diff --git a/go.mod b/go.mod index 4a19e464..cd359c51 100644 --- a/go.mod +++ b/go.mod @@ -313,7 +313,7 @@ require ( ) replace ( - code.pfad.fr/risefront => github.com/nginxui/risefront v1.2.0 + code.pfad.fr/risefront => github.com/nginxui/risefront v1.2.1 github.com/tufanbarisyildirim/gonginx => github.com/0xJacky/gonginx v0.0.0-20250420001451-d120448e89a4 gorm.io/gorm => gorm.io/gorm v1.25.12 gorm.io/plugin/dbresolver => gorm.io/plugin/dbresolver v1.5.3 diff --git a/go.sum b/go.sum index b4c4acd0..6d8aeab8 100644 --- a/go.sum +++ b/go.sum @@ -1520,10 +1520,8 @@ github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1t github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= -github.com/nginxui/risefront v1.1.10 h1:8gy6fs89nha3TIZWFSkoMn6KZ6Qn3dFj3Jop3+E1tsc= -github.com/nginxui/risefront v1.1.10/go.mod h1:QX3OyvazX3Mi/X2NZKl9ylDrFVUeaogwSMKyEsnRCHE= -github.com/nginxui/risefront v1.2.0 h1:r/C5+mPs1V76EKEQl966Kl0UpbJnnC/a38ZLYtlvnT4= -github.com/nginxui/risefront v1.2.0/go.mod h1:QX3OyvazX3Mi/X2NZKl9ylDrFVUeaogwSMKyEsnRCHE= +github.com/nginxui/risefront v1.2.1 h1:CXu4c+bM6iBRVZHXQTUy9ceQa2p3YqIPNEdTPeCjIQ0= +github.com/nginxui/risefront v1.2.1/go.mod h1:QX3OyvazX3Mi/X2NZKl9ylDrFVUeaogwSMKyEsnRCHE= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nikoksr/notify v1.3.0 h1:UxzfxzAYGQD9a5JYLBTVx0lFMxeHCke3rPCkfWdPgLs= github.com/nikoksr/notify v1.3.0/go.mod h1:Xor2hMmkvrCfkCKvXGbcrESez4brac2zQjhd6U2BbeM= diff --git a/internal/upgrader/binary.go b/internal/upgrader/binary.go index bfb45a53..525c817d 100644 --- a/internal/upgrader/binary.go +++ b/internal/upgrader/binary.go @@ -9,8 +9,9 @@ import ( ) type Control struct { - DryRun bool `json:"dry_run"` - Channel string `json:"channel"` + DryRun bool `json:"dry_run"` + TestCommitAndRestart bool `json:"test_commit_and_restart"` + Channel string `json:"channel"` } // BinaryUpgrade Upgrade the binary @@ -48,6 +49,16 @@ func BinaryUpgrade(ws *websocket.Conn, control *Control) { } }() + if control.TestCommitAndRestart { + err = u.TestCommitAndRestart() + if err != nil { + _ = ws.WriteJSON(CoreUpgradeResp{ + Status: UpgradeStatusError, + Message: "Test commit and restart error", + }) + } + } + tarName, err := u.DownloadLatestRelease(progressChan) if err != nil { _ = ws.WriteJSON(CoreUpgradeResp{ @@ -71,7 +82,7 @@ func BinaryUpgrade(ws *websocket.Conn, control *Control) { Message: "Performing core upgrade", }) - if settings.NodeSettings.Demo { + if control.DryRun || settings.NodeSettings.Demo { return } diff --git a/internal/upgrader/test_commit_restart.go b/internal/upgrader/test_commit_restart.go new file mode 100644 index 00000000..1993c9fa --- /dev/null +++ b/internal/upgrader/test_commit_restart.go @@ -0,0 +1,98 @@ +package upgrader + +import ( + "io" + "os" + "path/filepath" + "runtime" + "strconv" + "time" + + "code.pfad.fr/risefront" + "github.com/minio/selfupdate" + "github.com/pkg/errors" +) + +func (u *Upgrader) TestCommitAndRestart() error { + // Get the directory of the current executable + exDir := filepath.Dir(u.ExPath) + testBinaryPath := filepath.Join(exDir, "nginx-ui.test") + + // Create temporary old file path + oldExe := filepath.Join(exDir, ".nginx-ui.old."+strconv.FormatInt(time.Now().Unix(), 10)) + + // Setup update options + opts := selfupdate.Options{ + OldSavePath: oldExe, + } + + // Check permissions + if err := opts.CheckPermissions(); err != nil { + return err + } + + // Copy current executable to test file + srcFile, err := os.Open(u.ExPath) + if err != nil { + return errors.Wrap(err, "failed to open source executable") + } + defer srcFile.Close() + + // Create destination file + destFile, err := os.Create(testBinaryPath) + if err != nil { + return errors.Wrap(err, "failed to create test executable") + } + defer destFile.Close() + + // Copy file content + _, err = io.Copy(destFile, srcFile) + if err != nil { + return errors.Wrap(err, "failed to copy executable content") + } + + // Set executable permissions + if err = destFile.Chmod(0755); err != nil { + return errors.Wrap(err, "failed to set executable permission") + } + + // Reopen file for selfupdate + srcFile.Close() + srcFile, err = os.Open(testBinaryPath) + if err != nil { + return errors.Wrap(err, "failed to open test executable for update") + } + defer srcFile.Close() + + // Prepare and check binary + if err = selfupdate.PrepareAndCheckBinary(srcFile, opts); err != nil { + var pathErr *os.PathError + if errors.As(err, &pathErr) { + return pathErr.Err + } + return err + } + + // Commit binary update + if err = selfupdate.CommitBinary(opts); err != nil { + if rerr := selfupdate.RollbackError(err); rerr != nil { + return rerr + } + var pathErr *os.PathError + if errors.As(err, &pathErr) { + return pathErr.Err + } + return err + } + + if runtime.GOOS != "windows" { + _ = os.Remove(oldExe) + } + + // Wait for file to be written + time.Sleep(1 * time.Second) + + // Gracefully restart + risefront.Restart() + return nil +}