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 "github.com/carlosframework/platform/internal/hostfloor"
7 "github.com/carlosframework/platform/internal/release"
8 "log"
9 "os"
10 "path/filepath"
11 "runtime"
12 "syscall"
13 "time"
14)
15
16func floorDisk(optDir string) (hostfloor.Disk, error) {
17 pub, err := release.ParsePublicKey(os.Getenv("CARLOS_RELEASE_PUBKEY"), "CARLOS_RELEASE_PUBKEY")
18 if err != nil {
19 return hostfloor.Disk{}, err
20 }
21 bucket := os.Getenv("CARLOS_DEPLOYMENT_BUCKET")
22 if bucket == "" {
23 return hostfloor.Disk{}, hostfloor.ErrFloor
24 }
25 return hostfloor.Disk{Dir: filepath.Join(optDir, "system", "guard"), Bucket: bucket, PublicKey: pub}, nil
26}
27func RunSystemGuardExec(args []string) {
28 if len(args) < 1 {
29 log.Fatal(hostfloor.ErrFloor)
30 }
31 d, err := floorDisk(release.OptDir())
32 if err != nil {
33 log.Fatal(err)
34 }
35 policy, local, err := d.Load()
36 if err != nil {
37 log.Fatal(err)
38 }
39 if args[0] == "recover" {
40 held, err := recoverLockHeld(release.OptDir())
41 if err != nil {
42 log.Fatal(err)
43 }
44 if held {
45 log.Print("system guard-exec recover: an apply holds the lock; it recovers at its own start")
46 return
47 }
48 }
49 networkFree := false
50 if args[0] == "recover" {
51 _, networkFree, err = rootMovesFloorIdentity()
52 if err != nil {
53 log.Fatal(err)
54 }
55 }
56 if policy.Mode != "unpublished" && !networkFree {
57 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
58 store, pub, e := openSystemStore(ctx)
59 if e == nil {
60 e = hostfloor.CheckRemotePolicy(ctx, store, pub, d.Bucket, policy, local)
61 }
62 cancel()
63 if e != nil && !hostfloor.Offline(e) {
64 log.Fatal(e)
65 }
66 }
67 current := systemBinaryPath(release.OptDir())
68 candidate := current
69 component := "router"
70 switch args[0] {
71 case "apply", "recover":
72 _, err := os.Stat(filepath.Join(systemRootDir(release.OptDir()), "journal.json"))
73 if err == nil {
74 candidate = systemPrevPath(release.OptDir())
75 } else if !errors.Is(err, os.ErrNotExist) {
76 log.Fatal(err)
77 }
78 mode := args[0]
79 args = []string{"system", "apply"}
80 if mode == "recover" {
81 args = append(args, "--recover")
82 }
83 case "console":
84 if len(args) < 2 {
85 log.Fatal(hostfloor.ErrFloor)
86 }
87 component = "console"
88 candidate = args[1]
89 args = args[2:]
90 case "edge":
91 args = []string{"agent"}
92 default:
93 log.Fatal(hostfloor.ErrFloor)
94 }
95 if component == "router" && len(args) > 1 && args[0] == "system" {
96 id, required, e := rootMovesFloorIdentity()
97 if e != nil {
98 log.Fatal(e)
99 }
100 if required {
101 journal, e := rootFloorJournalPresent(release.OptDir())
102 if e != nil {
103 log.Fatal(e)
104 }
105 e = withRootFloorGuardCandidate(context.Background(), release.OptDir(), id, journal && len(args) > 1 && args[0] == "system", func(selected string) error {
106 return d.Exec(selected, runtime.GOOS+"-"+runtime.GOARCH, args, os.Environ(), component)
107 })
108 if e != nil {
109 log.Fatal(e)
110 }
111 return
112 }
113 }
114 if err = d.Exec(candidate, runtime.GOOS+"-"+runtime.GOARCH, args, os.Environ(), component); err != nil {
115 log.Fatal(err)
116 }
117}
118
119// recoverLockHeld reports whether another process holds the apply lock.
120// Only root's apply, recover and guard-arm take it, and an apply recovers
121// any journal itself at its start, so guard-exec recover leaves the box to
122// the holder at once. The edge drop-in Requires= the recover unit, so every
123// edge restart inside an apply starts a recover; waiting out
124// systemRecoverLockWait there kept the restarted edge down for 30 s (Opus 8b
125// review I-2). A missing lock file is a free lock and is not created.
126func recoverLockHeld(optDir string) (bool, error) {
127 lock, err := os.OpenFile(filepath.Join(systemRootDir(optDir), systemLockFile), os.O_RDWR, 0)
128 if errors.Is(err, os.ErrNotExist) {
129 return false, nil
130 }
131 if err != nil {
132 return false, err
133 }
134 defer lock.Close()
135 err = flockExclusiveNonblock(lock)
136 if errors.Is(err, syscall.EWOULDBLOCK) {
137 return true, nil
138 }
139 return false, err
140}
141