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 "flag"
7 "log"
8 "os"
9 "os/signal"
10 "path/filepath"
11 "syscall"
12
13 "github.com/carlosframework/platform/internal/hostbox"
14 "github.com/carlosframework/platform/internal/hostwrite"
15 "github.com/carlosframework/platform/internal/release"
16)
17
18// checkClaimJournalMigration is root startup's only contact with the legacy
19// claim journals under the agent's data directory. It never imports and never
20// quiesces: legacy files the agent changes after the marker are reported and
21// ignored, so they cannot stop root's control plane.
22func checkClaimJournalMigration(ctx context.Context, journals *hostbox.JournalService, local string) error {
23 alert, err := journals.CheckLegacyMigration(ctx, local)
24 if alert != nil {
25 log.Printf("claim journal migration: %v", alert)
26 }
27 return err
28}
29
30// RunSystemJournalMigrate is the offline operator step that imports legacy
31// claim journals into root's journal: carlos system journal-migrate, run as
32// root with the edge and the agent stopped. SIGTERM and interrupt cancel the
33// import, so the edge's runtime mask is lifted on the way out.
34func RunSystemJournalMigrate(args []string) {
35 fs := flag.NewFlagSet("system journal-migrate", flag.ExitOnError)
36 window := fs.String("window", "", journalMigrateWindowFlag)
37 _ = fs.Parse(args)
38 if os.Geteuid() != 0 || fs.NArg() != 0 {
39 log.Fatal(hostwrite.ErrUnavailable)
40 }
41 ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, os.Interrupt)
42 defer stop()
43 ctx, err := claimEntryContext(ctx, os.Getenv, openDeployStore)
44 if err != nil {
45 log.Fatal(err)
46 }
47 if !hostwrite.Enabled(claimEntryEnv(ctx)) {
48 log.Fatal(hostwrite.ErrUnavailable)
49 }
50 reg, err := openServiceRegistry(filepath.Join(dataDir(), "registry.db"))
51 if err != nil {
52 log.Fatal(err)
53 }
54 defer reg.Close()
55 provider := productionClaimOwners()
56 if provider == nil {
57 provider = ProductionClaimOwnerProvider{}
58 }
59 r := ClaimRuntimeResources{Mode: "instance-control", Registry: reg, DataDir: dataDir(), OptDir: release.OptDir(), UnitsDir: "/etc/systemd/system", MigrationWindow: *window}
60 // The window is checked before anything is written; the census checks
61 // its masks again when it walks the unit directory.
62 if _, err = verifyJournalMigrateWindow(r); err != nil {
63 log.Fatal(err)
64 }
65 if err = migrateClaimJournalsWith(ctx, claimStore(ctx), r, provider, true); err != nil {
66 log.Fatal(err)
67 }
68}
69
70// journalMigrateWindowFlag is --window's help.
71const journalMigrateWindowFlag = "the attended window's directory: its held directory keeps the original of each window unit the window masked, so the migration census accepts those masks"
72
73func migrateClaimJournalsWith(ctx context.Context, store release.Store, r ClaimRuntimeResources, provider ClaimOwnerProvider, census bool) (resultErr error) {
74 if store == nil || provider == nil || r.Mode != "instance-control" || !filepath.IsAbs(r.DataDir) {
75 return hostwrite.ErrUnavailable
76 }
77 if census {
78 if err := claimActivationPreflight(r.Registry); err != nil {
79 return err
80 }
81 }
82 if census {
83 if err := claimActivationVerified(ctx, store, r); err != nil {
84 return err
85 }
86 }
87 owners, err := provider.OpenClaimOwners(ctx, store, r)
88 if err != nil {
89 return err
90 }
91 if owners.Close == nil {
92 return hostwrite.ErrUnavailable
93 }
94 defer func() { resultErr = errors.Join(resultErr, owners.Close(context.WithoutCancel(ctx))) }()
95 if owners.RetirementAuthority == nil {
96 return hostwrite.ErrUnavailable
97 }
98 actor, err := owners.actorForConstruction(ctx, r.Mode)
99 if err != nil {
100 return err
101 }
102 launches, err := openClaimRootJournal()
103 if err != nil {
104 return err
105 }
106 defer func() { resultErr = errors.Join(resultErr, launches.Close()) }()
107 journals, err := hostbox.NewJournalService(launches)
108 if err != nil {
109 return err
110 }
111 if census {
112 return migrateRootClaimAuthority(ctx, store, r, owners, actor, journals, launches)
113 }
114 return journals.MigrateLegacy(ctx, filepath.Join(r.DataDir, ".hostclaims"), actor.Box, actor.Enrollment, quiesceLegacyClaimJournals, legacyJournalProof(store, owners.RetirementAuthority.Directory), recoverLegacyClaimEdgeMask)
115}
116
117var openClaimRootJournal = func() (*hostbox.LaunchJournal, error) {
118 if err := hostbox.PrepareRootJournalDirectory("/var/lib/carlos-claims"); err != nil {
119 return nil, err
120 }
121 return hostbox.OpenLaunchJournal("/var/lib/carlos-claims/journals.db")
122}
123