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:
Lemon-miaow committed 2026-10-01 19:48:37 +08:00
1 parent 26e817f2c9
commit adf563ffe3
77 files changed
+5224 -73

No files matched your search

+34
View File
@@ -7,9 +7,12 @@ import (
"sort"
"strings"
"testing"
"time"
"felis.lolicon.best/internal/apis/felis/v1alpha1"
"felis.lolicon.best/internal/maintenance"
"felis.lolicon.best/internal/naming"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -19,6 +22,37 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client/fake"
)
func TestPersistentMigrationBlocksWakeAndWorldOperations(t *testing.T) {
scheme := runtime.NewScheme()
v1alpha1.AddToScheme(scheme)
corev1.AddToScheme(scheme)
batchv1.AddToScheme(scheme)
s := &v1alpha1.MinecraftServer{ObjectMeta: metav1.ObjectMeta{Name: "survival", Namespace: "minecraft", Annotations: map[string]string{maintenance.Annotation: maintenance.LockValue(maintenance.KindMigration, time.Now().Add(-24*time.Hour))}}, Spec: v1alpha1.MinecraftServerSpec{DesiredState: v1alpha1.DesiredStopped}, Status: v1alpha1.MinecraftServerStatus{Phase: v1alpha1.PhaseStopped}}
s.Spec.Storage.ClaimName = "world-survival-migrated"
c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(s, &corev1.PersistentVolumeClaim{ObjectMeta: metav1.ObjectMeta{Name: s.WorldPVC(), Namespace: s.Namespace}}).Build()
k := NewK8sCluster(c, s.Namespace)
ctx := context.Background()
if exists, err := k.WorldVolumeExists(ctx, s.Name); err != nil || !exists {
t.Fatal("active volume ignored", err)
}
if err := k.SetDesiredState(ctx, s.Name, v1alpha1.DesiredRunning); !errors.Is(err, ErrMaintenanceInProgress) {
t.Fatal("migration admitted wake", err)
}
for _, kind := range []string{maintenance.KindBackup, maintenance.KindFileWrite, maintenance.KindReap} {
if err := k.AcquireMaintenance(ctx, s.Name, kind); !errors.Is(err, ErrMaintenanceInProgress) {
t.Fatal("migration admitted", kind, err)
}
}
if err := k.ReleaseMaintenance(ctx, s.Name); err != nil {
t.Fatal(err)
}
var current v1alpha1.MinecraftServer
c.Get(ctx, types.NamespacedName{Namespace: s.Namespace, Name: s.Name}, &current)
if current.Annotations[maintenance.Annotation] == "" {
t.Fatal("normal release cleared persistent migration lock")
}
}
// K8sCluster is documented as integration-tested against a live cluster rather
// than covered by the hermetic suite, and for most of it that is the right call —
// merge-patch semantics are not worth faking. This one test departs from it