Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions api/internal/handlers/recovery_policies_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package handlers

import (
"encoding/json"
"net/http"
"testing"
)

func TestRecoveryPoliciesRoundTripThroughGenericAPI(t *testing.T) {
for _, resource := range []struct{ path, kind string }{{"backups", "Backup"}, {"schedules", "BackupSchedule"}} {
t.Run(resource.path, func(t *testing.T) {
router := mountResourcesRouter(fakeKubeClient())
body := map[string]any{"apiVersion": "gameplane.local/v1alpha1", "kind": resource.kind, "metadata": map[string]any{"name": "recovery"}, "spec": map[string]any{"serverRef": map[string]any{"name": "game"}, "repoRef": map[string]any{"name": "repo", "key": "repo"}, "schedule": "* * * * *", "strategy": "restic-snapshot", "powerPolicy": "stop-copy-restart", "volumePolicy": "all-data"}}
rr := do(t, router, http.MethodPost, "/"+resource.path+"/", body)
if rr.Code != http.StatusCreated {
t.Fatalf("create: %d %s", rr.Code, rr.Body)
}
rr = do(t, router, http.MethodGet, "/"+resource.path+"/recovery", nil)
var got struct {
Spec struct{ PowerPolicy, VolumePolicy string }
}
if err := json.Unmarshal(rr.Body.Bytes(), &got); err != nil {
t.Fatal(err)
}
if got.Spec.PowerPolicy != "stop-copy-restart" || got.Spec.VolumePolicy != "all-data" {
t.Fatalf("policies lost: %+v", got.Spec)
}
})
}
}
2 changes: 1 addition & 1 deletion assets/design-export/json/bHJy6.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"fill": "$muted",
"textGrowth": "fixed-width",
"width": "fill_container",
"content": "Only the primary data claim is copied. Rebuildable is a declaration, not proof that recovery can reconstruct its contents.",
"content": "Included claims are copied at the recorded archive roots. Primary-only backups exclude extra data volumes and separate mod claims. Rebuildable volumes are excluded. Check recovery prerequisites before restarting.",
"lineHeight": 1.333333,
"fontFamily": "$typography/font-sans",
"fontSize": 12,
Expand Down
Binary file modified assets/design-export/screenshots/bHJy6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
gameplane.local/crd-bundle-sha256: bf84afb28a947929dcc56cef655364589a4be2e498da9eafb35363eda630676c
gameplane.local/crd-bundle-sha256: e900c220cd57442f03e3264da6ad0a2e746450523b88edf38affff441b150072
controller-gen.kubebuilder.io/version: v0.22.0
name: backups.gameplane.local
spec:
Expand Down Expand Up @@ -58,6 +58,13 @@ spec:
spec:
description: BackupSpec is the desired state of a one-shot backup.
properties:
powerPolicy:
description: PowerPolicy preserves power by default; stop-copy-restart
temporarily stops under lifecycle ownership.
enum:
- preserve
- stop-copy-restart
type: string
quiesce:
default: true
description: |-
Expand Down Expand Up @@ -113,6 +120,13 @@ spec:
items:
type: string
type: array
volumePolicy:
description: VolumePolicy captures primary-only by default; all-data
includes classified persistent data.
enum:
- primary-only
- all-data
type: string
volumeSnapshotClassName:
description: |-
VolumeSnapshotClassName names the VolumeSnapshotClass to use when
Expand All @@ -126,6 +140,10 @@ spec:
x-kubernetes-validations:
- message: repoRef is required for the restic-snapshot strategy
rule: self.strategy == 'volume-snapshot' || has(self.repoRef)
- message: stopped and all-data policies require restic-snapshot
rule: self.strategy != 'volume-snapshot' || ((!has(self.powerPolicy)
|| self.powerPolicy == 'preserve') && (!has(self.volumePolicy) ||
self.volumePolicy == 'primary-only'))
status:
description: BackupStatus is the observed state of a Backup.
properties:
Expand All @@ -135,6 +153,13 @@ spec:
It describes persistent-volume coverage independently of copy success.
Missing on pre-upgrade backups; never reconstruct it from current layout.
properties:
formatVersion:
description: FormatVersion 2 uses validated disjoint roots. Absent
means legacy /data.
format: int32
type: integer
gameVersion:
type: string
layoutSource:
description: LayoutSource identifies the frozen workload used
for mount paths.
Expand All @@ -159,6 +184,12 @@ spec:
type: string
strategy:
type: string
templateName:
type: string
templateRevision:
type: string
volumePolicy:
type: string
volumes:
items:
description: |-
Expand All @@ -182,12 +213,24 @@ spec:
type: string
included:
type: boolean
logicalName:
description: LogicalName identifies the primary, declared
extra, or active loader volume.
type: string
mountPaths:
items:
type: string
type: array
reason:
type: string
recoveryPrerequisite:
description: RecoveryPrerequisite explains excluded content
that must be reconstructed.
type: string
snapshotRoot:
description: SnapshotRoot is the explicit disjoint archive
root; never a glob.
type: string
required:
- claimName
- classification
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
gameplane.local/crd-bundle-sha256: bf84afb28a947929dcc56cef655364589a4be2e498da9eafb35363eda630676c
gameplane.local/crd-bundle-sha256: e900c220cd57442f03e3264da6ad0a2e746450523b88edf38affff441b150072
controller-gen.kubebuilder.io/version: v0.22.0
name: backupschedules.gameplane.local
spec:
Expand Down Expand Up @@ -72,6 +72,13 @@ spec:
- Forbid
- Replace
type: string
powerPolicy:
description: PowerPolicy preserves power by default; stop-copy-restart
temporarily stops under lifecycle ownership.
enum:
- preserve
- stop-copy-restart
type: string
quiesce:
default: true
description: |-
Expand Down Expand Up @@ -163,13 +170,24 @@ spec:
default: false
description: Suspend pauses the schedule without deleting it.
type: boolean
volumePolicy:
description: VolumePolicy captures primary-only by default; all-data
includes classified persistent data.
enum:
- primary-only
- all-data
type: string
required:
- schedule
- serverRef
type: object
x-kubernetes-validations:
- message: repoRef is required for the restic-snapshot strategy
rule: self.strategy == 'volume-snapshot' || has(self.repoRef)
- message: stopped and all-data policies require restic-snapshot
rule: self.strategy != 'volume-snapshot' || ((!has(self.powerPolicy)
|| self.powerPolicy == 'preserve') && (!has(self.volumePolicy) ||
self.volumePolicy == 'primary-only'))
status:
description: BackupScheduleStatus is the observed state of a BackupSchedule.
properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
gameplane.local/crd-bundle-sha256: bf84afb28a947929dcc56cef655364589a4be2e498da9eafb35363eda630676c
gameplane.local/crd-bundle-sha256: e900c220cd57442f03e3264da6ad0a2e746450523b88edf38affff441b150072
controller-gen.kubebuilder.io/version: v0.22.0
name: clusters.gameplane.local
spec:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
gameplane.local/crd-bundle-sha256: bf84afb28a947929dcc56cef655364589a4be2e498da9eafb35363eda630676c
gameplane.local/crd-bundle-sha256: e900c220cd57442f03e3264da6ad0a2e746450523b88edf38affff441b150072
controller-gen.kubebuilder.io/version: v0.22.0
name: gameservers.gameplane.local
spec:
Expand Down Expand Up @@ -978,6 +978,11 @@ spec:
the operator creates/maintains a BackupSchedule owned by this
GameServer. Remove to stop scheduled backups.
properties:
powerPolicy:
enum:
- preserve
- stop-copy-restart
type: string
repoRef:
description: |-
RepoRef points at a BackupRepo (cluster-scoped resource, TBD) or
Expand Down Expand Up @@ -1028,6 +1033,11 @@ spec:
type: string
suspend:
type: boolean
volumePolicy:
enum:
- primary-only
- all-data
type: string
required:
- repoRef
- schedule
Expand Down Expand Up @@ -2222,10 +2232,10 @@ spec:
properties:
backupRole:
description: |-
BackupRole describes recovery requirements without adding this volume
to a backup. "data" needs a separate copy; "rebuildable" may be recreated.
Unset remains unclassified and not covered. Both backup strategies still
capture only the primary data claim.
BackupRole declares recovery scope. all-data Restic backups include data
extras and report rebuildable extras as excluded reconstruction prerequisites.
Unset is unclassified and rejects all-data admission. Default primary-only
backups and CSI snapshots still capture only the primary data claim.
enum:
- data
- rebuildable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
gameplane.local/crd-bundle-sha256: bf84afb28a947929dcc56cef655364589a4be2e498da9eafb35363eda630676c
gameplane.local/crd-bundle-sha256: e900c220cd57442f03e3264da6ad0a2e746450523b88edf38affff441b150072
controller-gen.kubebuilder.io/version: v0.22.0
name: gametemplates.gameplane.local
spec:
Expand Down Expand Up @@ -2209,10 +2209,10 @@ spec:
properties:
backupRole:
description: |-
BackupRole describes recovery requirements without adding this volume
to a backup. "data" needs a separate copy; "rebuildable" may be recreated.
Unset remains unclassified and not covered. Both backup strategies still
capture only the primary data claim.
BackupRole declares recovery scope. all-data Restic backups include data
extras and report rebuildable extras as excluded reconstruction prerequisites.
Unset is unclassified and rejects all-data admission. Default primary-only
backups and CSI snapshots still capture only the primary data claim.
enum:
- data
- rebuildable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
gameplane.local/crd-bundle-sha256: bf84afb28a947929dcc56cef655364589a4be2e498da9eafb35363eda630676c
gameplane.local/crd-bundle-sha256: e900c220cd57442f03e3264da6ad0a2e746450523b88edf38affff441b150072
controller-gen.kubebuilder.io/version: v0.22.0
name: modules.gameplane.local
spec:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
gameplane.local/crd-bundle-sha256: bf84afb28a947929dcc56cef655364589a4be2e498da9eafb35363eda630676c
gameplane.local/crd-bundle-sha256: e900c220cd57442f03e3264da6ad0a2e746450523b88edf38affff441b150072
controller-gen.kubebuilder.io/version: v0.22.0
name: modulesources.gameplane.local
spec:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
gameplane.local/crd-bundle-sha256: bf84afb28a947929dcc56cef655364589a4be2e498da9eafb35363eda630676c
gameplane.local/crd-bundle-sha256: e900c220cd57442f03e3264da6ad0a2e746450523b88edf38affff441b150072
controller-gen.kubebuilder.io/version: v0.22.0
name: networkcaptures.gameplane.local
spec:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
gameplane.local/crd-bundle-sha256: bf84afb28a947929dcc56cef655364589a4be2e498da9eafb35363eda630676c
gameplane.local/crd-bundle-sha256: e900c220cd57442f03e3264da6ad0a2e746450523b88edf38affff441b150072
controller-gen.kubebuilder.io/version: v0.22.0
name: restores.gameplane.local
spec:
Expand Down Expand Up @@ -171,6 +171,87 @@ spec:
- Succeeded
- Failed
type: string
recoveryPlan:
description: RestoreRecoveryPlan pins a validated target mapping before
any power mutation.
properties:
admittedGeneration:
format: int64
type: integer
admittedIdle:
type: string
admittedSuspend:
description: Admitted power intent is informational; cleanup never
overwrites newer user intent.
type: boolean
backupUID:
type: string
gameVersion:
type: string
manifestDigest:
type: string
targetUID:
type: string
templateRevision:
type: string
volumes:
items:
description: |-
BackupVolumeCoverage describes one persistent claim. Extra and loader/mod
claims remain uncovered even when mounted beneath the primary data directory.
properties:
claimName:
type: string
claimUID:
description: ClaimUID is empty if the claim did not yet
exist at admission.
type: string
classification:
description: |-
Classification records the module/server declaration at admission;
it does not prove that the volume can actually be reconstructed.
enum:
- data
- rebuildable
- unclassified
type: string
included:
type: boolean
logicalName:
description: LogicalName identifies the primary, declared
extra, or active loader volume.
type: string
mountPaths:
items:
type: string
type: array
reason:
type: string
recoveryPrerequisite:
description: RecoveryPrerequisite explains excluded content
that must be reconstructed.
type: string
snapshotRoot:
description: SnapshotRoot is the explicit disjoint archive
root; never a glob.
type: string
required:
- claimName
- classification
- included
- reason
type: object
type: array
required:
- admittedGeneration
- admittedSuspend
- backupUID
- gameVersion
- manifestDigest
- targetUID
- templateRevision
- volumes
type: object
snapshotID:
description: |-
SnapshotID is the restic snapshot used. Resolved from the
Expand Down
Loading
Loading