carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package host
2
3import (
4 "context"
5 "errors"
6 "reflect"
7 "time"
8
9 "github.com/carlosframework/platform/internal/hostactors"
10 "github.com/carlosframework/platform/internal/hostbox"
11 "github.com/carlosframework/platform/internal/hostguard"
12 "github.com/carlosframework/platform/internal/release"
13)
14
15// Only a checked root journal reaches this collector. The legacy importer still
16// requires its independently signed inventory proof before writing that journal.
17// Offline migration observes closed, settled writers; it never kills from a
18// legacy candidate or treats reboot alone as producer completion.
19type migrationStopKernel struct {
20 StopKernelVerifier
21 launches *hostbox.LaunchJournal
22 inventory *hostbox.StopInventoryDB
23 roots map[string]string
24}
25
26func (k migrationStopKernel) RecoverStopped(ctx context.Context, target hostguard.FenceTarget, inventory StopInventory) (StopKernelEvidence, DrainAcknowledgement, error) {
27 fail := func(err error) (StopKernelEvidence, DrainAcknowledgement, error) {
28 return StopKernelEvidence{}, DrainAcknowledgement{}, err
29 }
30 launch, err := k.launches.Read(ctx, target)
31 if err != nil {
32 return fail(err)
33 }
34 before, err := k.launches.ReadStopKernelJournal(ctx, target)
35 if err != nil {
36 return fail(err)
37 }
38 boot, err := recoveryControllerAt("/proc", target, before)
39 if err != nil {
40 return fail(err)
41 }
42 if err := verifyMigrationStopped(ctx, k.launches, k.inventory, launch, k.roots); err != nil {
43 return fail(err)
44 }
45 actual, err := k.inventory.ReadExactStopInventory(ctx, target)
46 if err != nil {
47 return fail(err)
48 }
49 if !reflect.DeepEqual(actual, inventory) {
50 return fail(ErrClaimStopInventory)
51 }
52 var groups []string
53 if boot == target.BootID {
54 groups, err = scanStopCgroups(ctx, "/sys/fs/cgroup", before.Cgroups)
55 } else {
56 groups, err = scanRebootCgroups(ctx, "/sys/fs/cgroup", before.Cgroups, target.BootID, boot)
57 }
58 if err != nil {
59 return fail(err)
60 }
61 after, err := k.launches.ReadStopKernelJournal(ctx, target)
62 if err != nil {
63 return fail(err)
64 }
65 if !reflect.DeepEqual(before, after) {
66 return fail(ErrClaimStopInventory)
67 }
68 ack := DrainClaimAcknowledgement(target.FenceID, target.ControllerID, before.ProcessStart, before.ProducerIDs, before.Completed)
69 return StopKernelEvidence{ObservedBootID: boot, InventorySHA256: digestStop(actual), LocalJournalSHA256: digestStop(after), CgroupIdentities: groups}, ack, nil
70}
71
72func recoverMigrationStop(ctx context.Context, c ClaimStopCoordinator, store release.Store, directory hostactors.Directory, launches *hostbox.LaunchJournal, inventory *hostbox.StopInventoryDB, launch hostbox.LaunchInventory, claimETag string, roots map[string]string) (StopResult, error) {
73 result, err := readMigrationStop(ctx, store, directory, launches, inventory, launch)
74 fresh := errors.Is(err, hostbox.ErrLegacyJournalProof)
75 if err != nil && !fresh {
76 return StopResult{}, err
77 }
78 target := result.Target
79 if fresh {
80 target = launch.Target
81 target.FenceID = c.NewID()
82 } else if err := verifyMigrationStopped(ctx, launches, inventory, launch, roots); err != nil {
83 return StopResult{}, err
84 }
85 target.ClaimETag = claimETag
86 target, err = c.Guard.BeginFence(ctx, target)
87 if err != nil {
88 return StopResult{}, err
89 }
90 if receipt, err := c.Guard.FenceReceiptFor(ctx, target); err == nil {
91 return StopResult{Target: target, Bytes: receipt.Bytes, EvidenceAt: receipt.EvidenceAt}, nil
92 } else if !errors.Is(err, hostguard.ErrNoFenceReceipt) {
93 return StopResult{}, err
94 }
95 if fresh {
96 c.Kernel = migrationStopKernel{launches: launches, inventory: inventory, roots: roots}
97 return c.RecoverStopped(ctx, target)
98 }
99 if err := c.Guard.CompleteFence(ctx, target, result.Bytes, result.EvidenceAt, func(context.Context, hostguard.FenceTarget, []byte, time.Time) error { return nil }); err != nil {
100 return StopResult{}, err
101 }
102 result.Target = target
103 return result, nil
104}
105