Loading deploy/crd/felis.lolicon.best_minecraftservers.yaml +8 −0 Changes for deploy/crd/felis.lolicon.best_minecraftservers.yaml: 8 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -433,6 +433,14 @@ spec: because the two endpoints fall in different reconcile passes. format: date-time type: string stopNoticeAt: description: |- StopNoticeAt is when the operator told the players on a server that it is about to stop (desiredState flipped to Stopped with players online). The stop itself waits until StopNoticeWindow has passed since then; the stamp is cleared once the server is scaled down, or when desiredState goes back to Running first. format: date-time type: string type: object type: object served: true Loading docs/troubleshooting.md +12 −1 Changes for docs/troubleshooting.md: 12 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -284,8 +284,19 @@ panel shows `Stopped` means the game pod is still terminating (its shutdown save can take a while); retry once `kubectl -n minecraft get pods -l felis.lolicon.best/server=<name>` shows nothing. A stop that leaves the server `Running` for about 30 s is the players' warning. When desiredState turns `Stopped` and the RCON tally does not say the server is empty, the operator sends everyone online a yellow chat line (`tellraw @a`), stamps `status.stopNoticeAt`, records a `StopNotice` Event, and scales down once `StopNoticeWindow` (30 s) has passed, with a last "stopping now" line before the save. Starting the server again inside the window calls the stop off, and the players are told so. An empty server, RCON off, or a probe or broadcast that fails stops at once. Idle auto-stop only fires on an empty server, so it never waits. [GO-TESTED: `internal/maintenance`, `k8scluster_maintenance_test.go`, `handlers_maintenance_test.go`, operator `maintenance_test.go`.] `handlers_maintenance_test.go`, operator `maintenance_test.go`, `stopnotice_test.go`.] --- Loading internal/apis/felis/v1alpha1/minecraftserver_types.go +5 −0 Changes for internal/apis/felis/v1alpha1/minecraftserver_types.go: 5 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -342,6 +342,11 @@ type MinecraftServerStatus struct { // or the server stops, so the empty-duration counter starts fresh each time // the server becomes unoccupied. EmptySince *metav1.Time `json:"emptySince,omitempty"` // StopNoticeAt is when the operator told the players on a server that it is // about to stop (desiredState flipped to Stopped with players online). The stop // itself waits until StopNoticeWindow has passed since then; the stamp is cleared // once the server is scaled down, or when desiredState goes back to Running first. StopNoticeAt *metav1.Time `json:"stopNoticeAt,omitempty"` // AutoRestarts counts how often the operator recreated the pod of a start // that timed out (at most MaxAutoRestarts, with a doubling backoff); reaching Ready or // stopping resets it. Loading internal/apis/felis/v1alpha1/zz_generated.deepcopy.go +3 −0 Changes for internal/apis/felis/v1alpha1/zz_generated.deepcopy.go: 3 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -121,6 +121,9 @@ func (in *MinecraftServerStatus) DeepCopyInto(out *MinecraftServerStatus) { if in.EmptySince != nil { out.EmptySince = in.EmptySince.DeepCopy() } if in.StopNoticeAt != nil { out.StopNoticeAt = in.StopNoticeAt.DeepCopy() } if in.LastAutoRestartAt != nil { out.LastAutoRestartAt = in.LastAutoRestartAt.DeepCopy() } Loading internal/operator/autorestart_test.go +2 −0 Changes for internal/operator/autorestart_test.go: 2 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -122,6 +122,8 @@ func (p switchProber) Probe(context.Context, string, string) (operator.PlayerCou func (switchProber) Save(context.Context, string, string) error { return nil } func (switchProber) Broadcast(context.Context, string, string, string) error { return nil } // A Running server keeps its phase and endpoint through two missed probes, // re-probing every 10s; the third consecutive miss degrades it to Starting, // and any success in between starts the count over. Loading Loading
deploy/crd/felis.lolicon.best_minecraftservers.yaml +8 −0 Changes for deploy/crd/felis.lolicon.best_minecraftservers.yaml: 8 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -433,6 +433,14 @@ spec: because the two endpoints fall in different reconcile passes. format: date-time type: string stopNoticeAt: description: |- StopNoticeAt is when the operator told the players on a server that it is about to stop (desiredState flipped to Stopped with players online). The stop itself waits until StopNoticeWindow has passed since then; the stamp is cleared once the server is scaled down, or when desiredState goes back to Running first. format: date-time type: string type: object type: object served: true Loading
docs/troubleshooting.md +12 −1 Changes for docs/troubleshooting.md: 12 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -284,8 +284,19 @@ panel shows `Stopped` means the game pod is still terminating (its shutdown save can take a while); retry once `kubectl -n minecraft get pods -l felis.lolicon.best/server=<name>` shows nothing. A stop that leaves the server `Running` for about 30 s is the players' warning. When desiredState turns `Stopped` and the RCON tally does not say the server is empty, the operator sends everyone online a yellow chat line (`tellraw @a`), stamps `status.stopNoticeAt`, records a `StopNotice` Event, and scales down once `StopNoticeWindow` (30 s) has passed, with a last "stopping now" line before the save. Starting the server again inside the window calls the stop off, and the players are told so. An empty server, RCON off, or a probe or broadcast that fails stops at once. Idle auto-stop only fires on an empty server, so it never waits. [GO-TESTED: `internal/maintenance`, `k8scluster_maintenance_test.go`, `handlers_maintenance_test.go`, operator `maintenance_test.go`.] `handlers_maintenance_test.go`, operator `maintenance_test.go`, `stopnotice_test.go`.] --- Loading
internal/apis/felis/v1alpha1/minecraftserver_types.go +5 −0 Changes for internal/apis/felis/v1alpha1/minecraftserver_types.go: 5 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -342,6 +342,11 @@ type MinecraftServerStatus struct { // or the server stops, so the empty-duration counter starts fresh each time // the server becomes unoccupied. EmptySince *metav1.Time `json:"emptySince,omitempty"` // StopNoticeAt is when the operator told the players on a server that it is // about to stop (desiredState flipped to Stopped with players online). The stop // itself waits until StopNoticeWindow has passed since then; the stamp is cleared // once the server is scaled down, or when desiredState goes back to Running first. StopNoticeAt *metav1.Time `json:"stopNoticeAt,omitempty"` // AutoRestarts counts how often the operator recreated the pod of a start // that timed out (at most MaxAutoRestarts, with a doubling backoff); reaching Ready or // stopping resets it. Loading
internal/apis/felis/v1alpha1/zz_generated.deepcopy.go +3 −0 Changes for internal/apis/felis/v1alpha1/zz_generated.deepcopy.go: 3 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -121,6 +121,9 @@ func (in *MinecraftServerStatus) DeepCopyInto(out *MinecraftServerStatus) { if in.EmptySince != nil { out.EmptySince = in.EmptySince.DeepCopy() } if in.StopNoticeAt != nil { out.StopNoticeAt = in.StopNoticeAt.DeepCopy() } if in.LastAutoRestartAt != nil { out.LastAutoRestartAt = in.LastAutoRestartAt.DeepCopy() } Loading
internal/operator/autorestart_test.go +2 −0 Changes for internal/operator/autorestart_test.go: 2 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -122,6 +122,8 @@ func (p switchProber) Probe(context.Context, string, string) (operator.PlayerCou func (switchProber) Save(context.Context, string, string) error { return nil } func (switchProber) Broadcast(context.Context, string, string, string) error { return nil } // A Running server keeps its phase and endpoint through two missed probes, // re-probing every 10s; the third consecutive miss degrades it to Starting, // and any success in between starts the count over. Loading