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 "fmt"
5 "time"
6
7 "github.com/carlosframework/platform/internal/applog"
8 "github.com/carlosframework/platform/internal/provision"
9 "github.com/carlosframework/platform/internal/runs"
10)
11
12// A run's two lines in `carlos logs` (amadan #325): when it started and who
13// asked for it, then how it ended. The output stays in the run's chunks and
14// the argv in the audit log; neither is ever written here, since a log
15// reader is not necessarily someone the command's arguments were meant for.
16//
17// Both lines are info. The Errors and Availability views read the platform
18// stream at warn and above, and a command that failed says nothing about
19// whether the instance could serve.
20
21const runEventLevel = applog.LevelInfo
22
23// runStartedLine is the line for a run whose unit has started. A service's
24// run names the service, and the person it named only as its statement
25// (service runs spec §2).
26func runStartedLine(req runs.Request) string {
27 if by := req.ServiceBy(); by != "" {
28 return fmt.Sprintf("run %s started %s", req.ID, by)
29 }
30 return fmt.Sprintf("run %s started by %s", req.ID, req.By)
31}
32
33// runEndedLine is the line for a run's result, whatever ended it. started
34// says whether RunCmd handed back a running unit: a result written before
35// that (a refusal, a cancel or a timeout during the wake or the staging, a
36// stop that overlapped the launch) is a run that did not start, whatever
37// its fields say. A failed start is the exception: StartCmd can answer an
38// error for a unit the control service did start (a lost response), and
39// RunCmd then stops it and hands back nothing, so the line hedges as the
40// CLI's does rather than say it never ran. A cancel or a deadline that
41// lands during that same start is written as cancelled or timed out with
42// no handle, and its line says "before it started", as the CLI says of the
43// same result: the result does not record whether the start was reached,
44// and the line restates the result, never more.
45func runEndedLine(id string, res runs.Result, started bool) string {
46 took := res.Ended.Sub(res.Started).Round(time.Second)
47 switch {
48 case res.Refused != "":
49 return fmt.Sprintf("run %s did not start: %s", id, runRefusedWords(res))
50 case res.Interrupted != "" && !started && res.Interrupted != runs.InterruptedStartFailed:
51 return fmt.Sprintf("run %s did not start: %s", id, res.Interrupted)
52 case res.Cancelled && !started:
53 return fmt.Sprintf("run %s was cancelled before it started", id)
54 case res.Cancelled:
55 return fmt.Sprintf("run %s was cancelled after %s", id, took)
56 case res.TimedOut && !started:
57 return fmt.Sprintf("run %s timed out before it started", id)
58 case res.TimedOut:
59 return fmt.Sprintf("run %s timed out after %s", id, took)
60 case res.Interrupted != "":
61 return fmt.Sprintf("run %s was interrupted: %s. it may have done part of its work", id, res.Interrupted)
62 case res.Signal != "":
63 return fmt.Sprintf("run %s was ended by signal %s after %s", id, res.Signal, took)
64 }
65 return fmt.Sprintf("run %s ended with exit %d after %s", id, res.Exit, took)
66}
67
68// runRefusedWords is why the box did not start the run. An unknown code
69// renders as itself, as the CLI's does.
70func runRefusedWords(res runs.Result) string {
71 switch res.Refused {
72 case runs.RefusedFlat:
73 return "the box is not on the nested layout yet"
74 case runs.RefusedClaim:
75 return "the box has host claims armed"
76 case runs.RefusedWake:
77 return fmt.Sprintf("the instance did not wake. %s", res.Detail)
78 case runs.RefusedIncarnation:
79 return "the instance was deleted and created again after the run was requested"
80 case runs.RefusedImport:
81 return "the instance is an import that is not committed yet"
82 }
83 return res.Refused
84}
85
86// runEvent publishes one of a run's lines against the record's instance.
87// A box with no log store has no sink, and emits nothing.
88func (w *runsWorker) runEvent(rec provision.Record, line string) {
89 if w.events == nil {
90 return
91 }
92 w.events.Event(rec.Account, rec.App, rec.Host, runEventLevel, line)
93}
94