Repository navigation
feat(storage): activate qualified temporary V2 storage #5827
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
devantler
wants to merge
55
commits into
main
Choose a base branch
from
codex/longhorn-v2-proof-5809
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+2,317
−32
Draft
Changes from all commits
Commits
Show all changes
55 commits
Select commit
Hold shift + click to select a range
b5d0f86
feat(storage): inspect native huge-page allocation without host writes
devantler ab20e77
feat(storage): define evidence for unused temporary worker preparation
devantler a800721
feat(storage): bracket empty temporary observations with native block…
devantler 55d7c8b
feat(storage): bind temporary inventory to its owned preparation cordon
devantler d967e09
feat(storage): prepare empty temporary workers with a separate recove…
devantler 2f4e81b
docs(storage): distinguish huge-page requests from allocation proof
devantler 2bbaf9b
chore(storage): integrate reviewed storage-root observation
devantler 09501cb
feat(storage): inspect bounded native worker block layouts
devantler 376561a
fix(storage): check capacity before temporary worker resets
devantler b28b779
chore(storage): integrate reviewed migration prerequisites
devantler 5caf484
chore(storage): integrate reviewed native prerequisite readers
devantler 3e5c4fc
Merge remote-tracking branch 'origin/main' into codex/longhorn-unused…
devantler 2ee2962
fix(storage): qualify unused workers from stable parent inventory
devantler 03f41a4
fix(storage): recover incomplete huge-page allocations
devantler 92d1d1a
Merge remote-tracking branch 'origin/main' into codex/longhorn-host-r…
devantler b17edf2
Merge remote-tracking branch 'origin/main' into codex/longhorn-unused…
devantler daf8ff3
fix(storage): honor the host recovery deadline
devantler 8def995
docs(storage): explain the host reboot safety boundaries
devantler 2164091
fix(storage): qualify failed unallocated raw requests
devantler 6121b34
docs(storage): explain unused worker preparation invariants
devantler e7a4c1f
fix(storage): bind Talos parameters and reject busy peers before jour…
devantler 10b343a
docs(storage): clarify migration admission and inventory binding
devantler 428a0bc
Merge remote-tracking branch 'origin/main' into codex/longhorn-unused…
devantler 3508c8a
merge: integrate reviewed host recovery into temporary preparation
devantler af20318
fix(storage): wait for database replacement after host drain
devantler 8df3f06
Merge remote-tracking branch 'origin/main' into codex/longhorn-unused…
devantler 4437f86
docs(storage): describe bounded database recovery after drain
devantler 4275b3e
fix(storage): preserve runtime through host reboot recovery
devantler 4edce78
Merge reviewed main into Longhorn preparation
devantler e99be98
fix(storage): admit verified recovery states in platform gates
devantler 05be2b2
Merge reviewed main into Longhorn recovery gates
devantler d66a8cb
fix(storage): wait for verified post-drain attachment transitions
devantler 735f0f9
Merge reviewed main into Longhorn attachment recovery
devantler 93005ad
feat(storage): qualify temporary V2 storage with recovery probes
devantler 845c598
fix(storage): bind activation to successful disk preparation
devantler e784b1f
Merge remote-tracking branch 'origin/main' into codex/longhorn-v2-pro…
devantler a13ee4f
fix(storage): recognize completed raw pool transitions
devantler 71f7d98
chore: merge reviewed main into V2 recovery proofs
devantler 0723c2b
test(storage): keep activation OS metadata synthetic
devantler 1cb334e
fix(storage): restore Kopia canaries onto independent volumes
devantler 591b226
refactor(storage): separate V2 activation from recovery probes
devantler cd06651
chore(storage): qualify V2 activation against current main
devantler a0a4502
feat(storage): fit platform volumes on larger staging disks
devantler 66b4357
fix(storage): rebind main before staging writes
devantler 20e87d4
fix(storage): protect all current databases during worker reboots
devantler f11596d
test(storage): integrate activation with reviewed platform main
devantler 7f69b1f
test(storage): integrate larger staging preparation
devantler cb40563
test(storage): integrate current database reboot guard
devantler 8866386
fix(storage): qualify the larger staging disks before activation
devantler d29475d
fix(storage): verify the retained Longhorn partition extent
devantler a23212d
fix(storage): bind V2 activation to the preserved staging layout
devantler 57c038b
fix(storage): bind retained raw and Talos state extents
devantler 5f009f7
feat(storage): prepare the third migration worker without losing its …
devantler 29159d2
test(storage): revalidate V2 activation on current migration main
devantler 871bc2a
chore(storage): reconcile V2 activation with current migration operat…
devantler File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| package longhornmigration | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "encoding/json" | ||
| "errors" | ||
| "regexp" | ||
| "strings" | ||
|
|
||
| "github.com/devantler-tech/platform/internal/strictjson" | ||
| ) | ||
|
|
||
| const ( | ||
| // ActivationJournal is the fixed three-worker raw registration receipt. | ||
| ActivationJournal = "longhorn-v2-temporary-activation" | ||
| // ActivationOwner binds registered storage to that receipt's Kubernetes UID. | ||
| ActivationOwner = "storage.platform.devantler.tech/v2-activation" | ||
| ) | ||
|
|
||
| // ActivationBinding retains the native identities verified before registration. | ||
| type ActivationBinding struct { | ||
| Selection string `json:"selection"` | ||
| Name string `json:"name"` | ||
| NodeUID string `json:"nodeUID"` | ||
| ProviderID string `json:"providerID"` | ||
| MachineUUID string `json:"machineUUID"` | ||
| BootID string `json:"bootID"` | ||
| OSImage string `json:"osImage"` | ||
| LonghornUID string `json:"longhornUID"` | ||
| EphemeralUUID string `json:"ephemeralUUID"` | ||
| RawPartitionUUID string `json:"rawPartitionUUID"` | ||
| PreparationUID string `json:"preparationUID,omitempty"` | ||
| } | ||
|
|
||
| // SameStorageIdentity compares the durable worker, partition and preparation | ||
| // identities. Boot and OS observations describe the original read bracket; a | ||
| // later caller must freshly prove its own bracket rather than reuse those values. | ||
| func (b ActivationBinding) SameStorageIdentity(other ActivationBinding) bool { | ||
| b.BootID, b.OSImage = "", "" | ||
| other.BootID, other.OSImage = "", "" | ||
| return b == other | ||
| } | ||
|
|
||
| // ActivationRecord is shared by the registering writer and publication readers. | ||
| type ActivationRecord struct { | ||
| Version int `json:"version"` | ||
| Source string `json:"source"` | ||
| OwnerRun string `json:"ownerRun"` | ||
| Targets map[string]ActivationBinding `json:"targets"` | ||
| Completed bool `json:"completed"` | ||
| } | ||
|
|
||
| var errActivation = errors.New("temporary raw storage identity or registration ownership is unproven") | ||
|
|
||
| // ActivationSelections returns the fixed roster without exposing mutable state. | ||
| func ActivationSelections() [3]string { | ||
| return [3]string{"temporary-v1-1", "temporary-v1-2", "temporary-v2-1"} | ||
| } | ||
|
|
||
| // DecodeActivationRecord rejects oversized, duplicate, aliased and unknown JSON. | ||
| func DecodeActivationRecord(data []byte) (ActivationRecord, error) { | ||
| if len(data) == 0 || len(data) > 32<<10 { | ||
| return ActivationRecord{}, errActivation | ||
| } | ||
| canonical := map[string][]string{"": {"version", "source", "ownerRun", "targets", "completed"}} | ||
| fields := []string{"selection", "name", "nodeUID", "providerID", "machineUUID", "bootID", "osImage", "longhornUID", "ephemeralUUID", "rawPartitionUUID", "preparationUID"} | ||
| for _, selection := range ActivationSelections() { | ||
| canonical["targets."+selection] = fields | ||
| } | ||
| if strictjson.Validate(data, canonical) != nil { | ||
| return ActivationRecord{}, errActivation | ||
| } | ||
| var record ActivationRecord | ||
| decoder := json.NewDecoder(bytes.NewReader(data)) | ||
| decoder.DisallowUnknownFields() | ||
| if decoder.Decode(&record) != nil || record.Validate() != nil { | ||
| return ActivationRecord{}, errActivation | ||
| } | ||
| return record, nil | ||
| } | ||
|
|
||
| // Validate requires the three distinct workers, partitions and preparation UIDs. | ||
| // It validates receipt structure; native callers still establish live acceptance. | ||
| func (r ActivationRecord) Validate() error { | ||
| if r.Version != 1 || !regexp.MustCompile(`^[0-9a-f]{40}$`).MatchString(r.Source) || !regexp.MustCompile(`^[1-9][0-9]*$`).MatchString(r.OwnerRun) || len(r.Targets) != 3 { | ||
| return errActivation | ||
| } | ||
| uuid := regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`) | ||
| seen := [7]map[string]bool{} | ||
| preparations := map[string]bool{} | ||
| for i := range seen { | ||
| seen[i] = map[string]bool{} | ||
| } | ||
| for _, selection := range ActivationSelections() { | ||
| b, ok := r.Targets[selection] | ||
| if !ok || b.Selection != selection || !regexp.MustCompile(`^autoscale-longhorn-(v[12])-[0-9a-f]{16}$`).MatchString(b.Name) || | ||
| !strings.HasPrefix(b.Name, "autoscale-longhorn-"+strings.Split(selection, "-")[1]+"-") || | ||
| b.NodeUID == "" || !regexp.MustCompile(`^hcloud://[1-9][0-9]*$`).MatchString(b.ProviderID) || b.MachineUUID == "" || | ||
| !uuid.MatchString(b.BootID) || b.OSImage == "" || b.LonghornUID == "" || b.EphemeralUUID == "" || | ||
| !uuid.MatchString(strings.ToLower(b.RawPartitionUUID)) || strings.EqualFold(b.RawPartitionUUID, b.EphemeralUUID) { | ||
| return errActivation | ||
| } | ||
| if b.PreparationUID == "" || preparations[b.PreparationUID] { | ||
| return errActivation | ||
| } | ||
| preparations[b.PreparationUID] = true | ||
| for i, identity := range []string{b.Name, b.NodeUID, b.ProviderID, strings.ToLower(b.MachineUUID), b.LonghornUID, strings.ToLower(b.EphemeralUUID), strings.ToLower(b.RawPartitionUUID)} { | ||
| if seen[i][identity] { | ||
| return errActivation | ||
| } | ||
| seen[i][identity] = true | ||
| } | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| // registeredRawTransition checks stable ownership for the read-only pool | ||
| // exception. A later normal reboot or OS upgrade does not revoke registration; | ||
| // destructive operations continue to require their separate native boot proof. | ||
| func registeredRawTransition(n, lh, cm Object) bool { | ||
| if text(cm, "metadata", "name") != ActivationJournal || text(cm, "metadata", "namespace") != "longhorn-system" || | ||
| text(cm, "metadata", "uid") == "" || value(cm, "metadata", "deletionTimestamp") != nil || | ||
| text(lh, "metadata", "annotations", ActivationOwner) != text(cm, "metadata", "uid") { | ||
| return false | ||
| } | ||
| r, err := DecodeActivationRecord([]byte(text(cm, "data", "record"))) | ||
| if err != nil || !r.Completed { | ||
| return false | ||
| } | ||
| for _, b := range r.Targets { | ||
| if b.Name == text(n, "metadata", "name") { | ||
| return b.NodeUID == text(n, "metadata", "uid") && b.ProviderID == text(n, "spec", "providerID") && | ||
| strings.EqualFold(b.MachineUUID, text(n, "status", "nodeInfo", "systemUUID")) && b.LonghornUID == text(lh, "metadata", "uid") | ||
| } | ||
| } | ||
| return false | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
19 changes: 19 additions & 0 deletions
19
k8s/providers/hetzner/infrastructure/controllers/longhorn/storage-class-longhorn-v2.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| --- | ||
| # Opt-in migration destination. The V1 default and existing claims remain until | ||
| # each application's recovered bytes and service behavior have been verified. | ||
| apiVersion: storage.k8s.io/v1 | ||
| kind: StorageClass | ||
| metadata: | ||
| name: longhorn-v2 | ||
| provisioner: driver.longhorn.io | ||
| allowVolumeExpansion: true | ||
| reclaimPolicy: Retain | ||
| volumeBindingMode: WaitForFirstConsumer | ||
| parameters: | ||
| numberOfReplicas: "3" | ||
| staleReplicaTimeout: "30" | ||
| fsType: ext4 | ||
| dataLocality: disabled | ||
| unmapMarkSnapChainRemoved: disabled | ||
| dataEngine: v2 | ||
| backupTargetName: default |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.