feat(operator): 状态迁移、超时、自动重建、空闲停机与 RCON Secret 创建写 Event 和结构化日志,RBAC 增加 events create/patch

This commit is contained in:
Lemon-miaow committed 2026-09-25 19:32:25 +08:00
1 parent a977e229ca
commit b7d4275ca9
5 files changed
+206 -9

No files matched your search

+7 -2
View File
@@ -155,8 +155,9 @@ func APIBuildRole(p Params) *rbacv1.Role {
// the operator checks that no restore/backup/file-write Job holds its world
// (internal/maintenance). Pods are delete-only: a start that timed out is retried
// by deleting its pod for the StatefulSet to recreate (bounded, three attempts;
// internal/operator.recoverFailedStart). It never touches PVCs, Events, or
// finalizers, so none appear here.
// internal/operator.recoverFailedStart). Events are create/patch only, for the
// timeline it records on each server. It never touches PVCs or finalizers, so
// none appear here.
func OperatorRole(p Params) *rbacv1.Role {
p = p.withDefaults()
return role(p.MinecraftNamespace, "felis-operator", ComponentOperator, []rbacv1.PolicyRule{
@@ -177,6 +178,10 @@ func OperatorRole(p Params) *rbacv1.Role {
// delete only, through the direct client: no read of pods is needed to
// remove the one named <server>-0.
rule([]string{groupCore}, []string{"pods"}, []string{"delete"}),
// The Events the reconciler records on MinecraftServers (phase changes,
// pod recreation, idle stop): the recorder creates one and patches its
// count when the same Event repeats.
rule([]string{groupCore}, []string{"events"}, []string{"create", "patch"}),
})
}
+12 -3
View File
@@ -189,9 +189,18 @@ func TestOperatorRole_ScopeExact(t *testing.T) {
}
}
// Hard exclusions.
for _, res := range []string{"persistentvolumeclaims", "events"} {
if grantsResource(op, groupCore, res) {
t.Errorf("operator must NOT touch core/%s", res)
if grantsResource(op, groupCore, "persistentvolumeclaims") {
t.Error("operator must NOT touch core/persistentvolumeclaims")
}
// Events: the recorder creates one and patches its count on a repeat.
for _, v := range []string{"create", "patch"} {
if !hasRule(op, groupCore, "events", v) {
t.Errorf("operator must have events:%s (the server timeline)", v)
}
}
for _, v := range []string{"get", "list", "watch", "update", "delete", "deletecollection", "*"} {
if hasRule(op, groupCore, "events", v) {
t.Errorf("operator events rule must be create+patch only, found %s", v)
}
}
// RCON Secrets are read by name through the uncached reader and created once;