原生版以非 root 的 havline 用户运行,而 app.New 会无条件调用 updater.EnsureToken, 去写默认的 /run/havline-updater/updater.token:MkdirAll 因权限不足失败 → app.New 返回错误 → main 里 log.Fatalf → 进程秒退,飞牛应用中心报「本地应用启动失败」。 该逻辑由 v1.9.2 引入,v1.9.1 及之前不受影响。 - 新增 updater.EnsureTokenIfEnabled:updater 未启用(socket 为空)时直接返回, 完全不触碰 Token 文件 - app.New 改用该函数,只有真正启用一键升级 sidecar 时才准备共享 Token - 补充回归测试:socket 为空不创建文件、socket 非空照旧创建
This commit is contained in:
+3
-1
@@ -70,7 +70,9 @@ func New(cfg config.Config, staticFS fs.FS, migrationsDir string) (*App, error)
|
|||||||
}
|
}
|
||||||
logger.Info("会话密钥已就绪", "module", "SYSTEM", "source", secretSource)
|
logger.Info("会话密钥已就绪", "module", "SYSTEM", "source", secretSource)
|
||||||
|
|
||||||
updaterToken, updaterTokenSource, err := updater.EnsureToken(cfg.UpdaterToken, cfg.UpdaterTokenFile)
|
// updater Token 只给「一键升级」sidecar 用;原生版与飞牛 Docker 版都显式把 socket 置空,
|
||||||
|
// 此时不能去写 /run/havline-updater:原生版以非 root 运行,权限不足会让应用直接起不来。
|
||||||
|
updaterToken, updaterTokenSource, err := updater.EnsureTokenIfEnabled(cfg.UpdaterSocket, cfg.UpdaterToken, cfg.UpdaterTokenFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,20 @@ func EnsureToken(token, tokenFile string) (string, string, error) {
|
|||||||
return value, path, nil
|
return value, path, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EnsureTokenIfEnabled 只在 updater 启用(socket 非空)时准备共享 Token。
|
||||||
|
//
|
||||||
|
// updater Token 只服务于「一键升级」sidecar,而 sidecar 与主程序是通过共享卷里的
|
||||||
|
// Token 文件 + Unix Socket 对接的。原生版(飞牛 Native)与飞牛 Docker 版都显式把
|
||||||
|
// socket 置空表示「不启用升级」,此时根本不需要这个文件:
|
||||||
|
// 原生版以非 root 用户(havline)运行,去写 /run/havline-updater 会因权限不足报错,
|
||||||
|
// 而启动期的这个错误会让整个应用起不来。
|
||||||
|
func EnsureTokenIfEnabled(socketPath, token, tokenFile string) (string, string, error) {
|
||||||
|
if strings.TrimSpace(socketPath) == "" {
|
||||||
|
return "", "", nil
|
||||||
|
}
|
||||||
|
return EnsureToken(token, tokenFile)
|
||||||
|
}
|
||||||
|
|
||||||
// WaitForToken 等待主程序在共享卷中创建 Token 文件;环境变量已设置时立即返回。
|
// WaitForToken 等待主程序在共享卷中创建 Token 文件;环境变量已设置时立即返回。
|
||||||
func WaitForToken(token, tokenFile string, timeout time.Duration) (string, string, error) {
|
func WaitForToken(token, tokenFile string, timeout time.Duration) (string, string, error) {
|
||||||
if timeout <= 0 {
|
if timeout <= 0 {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package updater
|
package updater
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -105,3 +106,36 @@ func TestWaitForTokenTimesOut(t *testing.T) {
|
|||||||
t.Fatal("文件不存在时应等待超时")
|
t.Fatal("文件不存在时应等待超时")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updater 未启用(socket 为空)时不能去碰 Token 文件:
|
||||||
|
// 原生版以非 root 运行,写 /run/havline-updater 会失败并拖垮整个启动。
|
||||||
|
func TestEnsureTokenIfEnabledSkipsWhenSocketDisabled(t *testing.T) {
|
||||||
|
tokenFile := filepath.Join(t.TempDir(), "nested", "updater.token")
|
||||||
|
|
||||||
|
token, source, err := EnsureTokenIfEnabled("", "", tokenFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("socket 未启用时不应报错:%v", err)
|
||||||
|
}
|
||||||
|
if token != "" || source != "" {
|
||||||
|
t.Fatalf("socket 未启用时应返回空 Token,实际 token=%q source=%q", token, source)
|
||||||
|
}
|
||||||
|
if _, statErr := os.Stat(tokenFile); !errors.Is(statErr, os.ErrNotExist) {
|
||||||
|
t.Fatalf("socket 未启用时不应创建 Token 文件(statErr=%v)", statErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对照组:updater 启用(socket 非空)时仍照旧生成并落盘。
|
||||||
|
func TestEnsureTokenIfEnabledCreatesWhenSocketSet(t *testing.T) {
|
||||||
|
tokenFile := filepath.Join(t.TempDir(), "nested", "updater.token")
|
||||||
|
|
||||||
|
token, source, err := EnsureTokenIfEnabled("/run/havline-updater/updater.sock", "", tokenFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("socket 启用时应生成 Token:%v", err)
|
||||||
|
}
|
||||||
|
if len(token) != 64 || source != tokenFile {
|
||||||
|
t.Fatalf("生成的 Token 不正确,实际 token=%q source=%q", token, source)
|
||||||
|
}
|
||||||
|
if _, statErr := os.Stat(tokenFile); statErr != nil {
|
||||||
|
t.Fatalf("socket 启用时应写入 Token 文件:%v", statErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user