feat(distributed): 支持单主控多节点部署和停服迁移
复用现有 k3s 调度和 Job 生命周期,增加 worker 接入与批准、受保护节点身份、归档传输、持久迁移锁及活动 PVC 切换;同步管理员 API、CLI、面板和隔离规则。分布式模式默认关闭,保持单机兼容。 验证:Go 全量测试与 vet;面板 874 个测试、lint/build;Linux VM 安装器测试、清单服务端 dry-run、网络命名空间防火墙实测。A/B/C 三机 WireGuard、Velocity 和迁移验收仍待完成。
This commit is contained in:
77 files changed
+5224
-73
No files matched your search
+30
-2
@@ -1,14 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"felis.lolicon.best/internal/archivetransfer"
|
||||
"felis.lolicon.best/internal/backup"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
)
|
||||
|
||||
// cmdRestore is the in-Pod entrypoint the restore Job runs. internal/restore
|
||||
@@ -26,6 +30,9 @@ import (
|
||||
func cmdRestore(args []string, stdout, stderr io.Writer) int {
|
||||
fs := flag.NewFlagSet("restore", flag.ContinueOnError)
|
||||
fs.SetOutput(stderr)
|
||||
source := fs.String("source-url", "", "one-use archive download URL")
|
||||
sum := fs.String("sha256", "", "required digest for remote archive")
|
||||
limit := fs.Int64("max-bytes", archivetransfer.DefaultLimit, "maximum download size")
|
||||
server := fs.String("server", "", "server name being restored (for logging)")
|
||||
ref := fs.String("ref", "", "absolute path to the archive on the backup mount")
|
||||
store := fs.String("archive-store", "tarLocal", "archive backend (only tarLocal is implemented)")
|
||||
@@ -35,6 +42,22 @@ func cmdRestore(args []string, stdout, stderr io.Writer) int {
|
||||
return 2
|
||||
}
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer stop()
|
||||
if *source != "" {
|
||||
dir, err := os.MkdirTemp("/tmp", "felis-restore-")
|
||||
if err != nil {
|
||||
fmt.Fprintln(stderr, err)
|
||||
return 1
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
*backupRoot = dir
|
||||
*ref = filepath.Join(dir, "world.tar.gz")
|
||||
if err := archivetransfer.Fetch(ctx, *source, os.Getenv(archivetransfer.TokenEnv), *ref, *sum, *limit); err != nil {
|
||||
fmt.Fprintln(stderr, err)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
if *ref == "" {
|
||||
fmt.Fprintln(stderr, "felis restore: --ref is required")
|
||||
return 2
|
||||
@@ -62,11 +85,16 @@ func cmdRestore(args []string, stdout, stderr io.Writer) int {
|
||||
},
|
||||
}
|
||||
|
||||
ctx := ctrl.SetupSignalHandler()
|
||||
if err := archiver.Restore(ctx, backup.ArchiveRef(*ref), *server); err != nil {
|
||||
fmt.Fprintf(stderr, "felis restore: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
if *source != "" {
|
||||
if err := backup.VerifyRestored(ctx, *ref, *worldsRoot); err != nil {
|
||||
fmt.Fprintln(stderr, err)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(stdout, "felis restore: server=%s restored from %s into %s\n", *server, *ref, *worldsRoot)
|
||||
return 0
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user