| 1 | {{/* /fleet/architecture (archpage.go): the deployment's map — how a request |
| 2 | reaches a process, drawn over the estate the account is carrying. |
| 3 | |
| 4 | Everything here is server-rendered: the diagram is inline SVG built in |
| 5 | Go (archlayout.go's RenderArchSVG) so the picture is deterministic and |
| 6 | testable as a whole, and the page works with JS off because there is no |
| 7 | JS. Every block below is independently guarded — the inventory, the |
| 8 | boxes, the resource tables and the failed-reads footer each appear only |
| 9 | when their own read produced something, so one bad read costs its own |
| 10 | section and never the page. */}} |
| 11 | {{define "architecture"}}{{template "top" .}} |
| 12 | <h1>Architecture</h1> |
| 13 | {{if .Flash}}<p class="banner">{{.Flash}}</p>{{end}} |
| 14 | {{if .FlashErr}}<p class="banner error">{{.FlashErr}}</p>{{end}} |
| 15 | {{/* The page's only control, at the top with the inventory timestamp it acts |
| 16 | on. It used to sit below every resource table, several thousand pixels |
| 17 | from the line telling you the inventory was stale (discussion #9). |
| 18 | Outside the {{with .Arch}} below on purpose: a page that could not read |
| 19 | an inventory is exactly the page whose refresh button has to still be |
| 20 | there. */}} |
| 21 | <form method="post" action="/fleet/architecture/refresh"> |
| 22 | <input type="hidden" name="csrf" value="{{.CSRF}}"> |
| 23 | <button type="submit">Refresh inventory</button> |
| 24 | </form> |
| 25 | {{/* A firing alarm, said where the reader already is. The marks ride the |
| 26 | nodes they watch, which is right, but that put them off to the right of |
| 27 | a drawing wider than the window and left the Alarms table thousands of |
| 28 | pixels below — so the page could know something was on fire and show |
| 29 | nothing above the fold. Spans both estates: the reader wants to know |
| 30 | something is firing, not which half of the page to look in. */}} |
| 31 | {{with .Arch}}{{if .Firing}} |
| 32 | <p class="banner error">{{icon "triangle-alert"}} |
| 33 | {{len .Firing}} alarm{{if ne (len .Firing) 1}}s{{end}} firing: |
| 34 | {{range $i, $a := .Firing}}{{if $i}}, {{end}}{{$a}}{{end}} |
| 35 | </p> |
| 36 | {{end}}{{end}} |
| 37 | {{with .Arch}} |
| 38 | {{/* The three missing states are named apart, never collapsed into one |
| 39 | "nothing here": never-taken, ungranted and unreadable are three |
| 40 | different facts about the deployment and only one of them is normal. */}} |
| 41 | {{if .Missing}} |
| 42 | <p class="banner">{{icon "triangle-alert"}} |
| 43 | {{if eq .Missing "never"}}No inventory has been taken yet — the first pass runs within the hour. The boxes below are what CARLOS itself reports. |
| 44 | {{else if eq .Missing "ungranted"}}This deployment has no infrastructure read grant, so the cloud half of this page is unavailable. The boxes below are what CARLOS itself reports. |
| 45 | {{else}}The inventory snapshot could not be read. The boxes below are what CARLOS itself reports.{{end}} |
| 46 | </p> |
| 47 | {{else}} |
| 48 | <p class="small">Estate {{.Account}} · inventoried {{fmtTime .TakenAt}}</p> |
| 49 | {{end}} |
| 50 | |
| 51 | {{/* Two estates, one above the other, never one picture with a line |
| 52 | through it. The account holds a CARLOS deployment and, very often, |
| 53 | whatever was already there — Tito's own account carries a Kubernetes |
| 54 | estate CARLOS deliberately sits beside so it can reach the legacy |
| 55 | database. Drawn together that is one unreadable graph; drawn apart it |
| 56 | is two readable ones. |
| 57 | |
| 58 | NOTHING IS FILTERED. Every node and every resource on the old single |
| 59 | page is still here, under one of the two headings — the split groups, |
| 60 | it does not hide (archestate.go). */}} |
| 61 | <h2>CARLOS</h2> |
| 62 | {{/* One picture per region, stacked down the page. Region is the |
| 63 | STRUCTURE here, not a word on a card: a box is the only thing on this |
| 64 | diagram that is really in a region, and everything else is drawn in the |
| 65 | band of the box it reaches (archregion.go). That is also what keeps |
| 66 | each picture inside the 64rem column — a band is at most four nodes |
| 67 | wide — so this page scrolls down and never across. */}} |
| 68 | {{range .CARLOS.Bands}} |
| 69 | <h4 class="archband">{{.Label}}</h4> |
| 70 | <div class="archwrap">{{.SVG}}</div> |
| 71 | {{end}} |
| 72 | |
| 73 | <h3>Boxes</h3> |
| 74 | {{range .Boxes}} |
| 75 | <section class="archbox"> |
| 76 | <h4>{{.Label}}{{if .Region}} <span class="small">{{.Region}}</span>{{end}}</h4> |
| 77 | {{/* Each fact is its own span and the separators are drawn by CSS |
| 78 | (.fact + .fact::before), because every one of these is independently |
| 79 | absent — a deployment with no rate card entry has no size and no |
| 80 | price, and a hand-written separator would leave a dangling middot on |
| 81 | the commonest page of all. */}} |
| 82 | <p class="small"> |
| 83 | {{if .InstanceType}}<span class="fact">{{.InstanceType}}</span>{{end}} |
| 84 | {{if .State}}<span class="fact">{{.State}}</span>{{end}} |
| 85 | {{if .Cores}}<span class="fact">{{printf "%.0f" .Cores}} cores</span>{{end}} |
| 86 | {{if .MemGB}}<span class="fact">{{printf "%.0f" .MemGB}} GB</span>{{end}} |
| 87 | {{if .DiscoveredDiskGB}}<span class="fact">{{printf "%.0f" .DiscoveredDiskGB}} GB disk</span>{{end}} |
| 88 | {{if .USDMonth}}<span class="fact">${{printf "%.0f" .USDMonth}}/mo</span>{{end}} |
| 89 | {{if not .CloudID}}<span class="fact">no cloud machine (bring-your-own box)</span>{{end}} |
| 90 | </p> |
| 91 | <p class="small">Apps: {{if .Apps}}{{range .Apps}}<code>{{.}}</code> {{end}}{{else}}none reported{{end}}</p> |
| 92 | {{/* The counts come from the fleet aggregate and nowhere else, so a box |
| 93 | that read never described has NO counts — printing three zeroes here |
| 94 | would state a read that never happened, beside an awake/asleep that |
| 95 | correctly says it does not know. */}} |
| 96 | <p class="small"> |
| 97 | {{if .CountsKnown}}static {{.Static}} · resident {{.Resident}} · hibernating {{.Hibernating}} · |
| 98 | awake {{intOrDash .Awake}} · asleep {{intOrDash .Asleep}} |
| 99 | {{else}}counts unknown{{end}} |
| 100 | {{with .Release}}· <code>{{.}}</code>{{end}} |
| 101 | </p> |
| 102 | {{if .Stale}}<p><span class="chip chip-stale">dark{{if .Dark}} {{.Dark}}{{end}}</span></p>{{end}} |
| 103 | {{/* Drift is where the rate card and the estate disagree — a fact to |
| 104 | watch, not an outage, so it wears chip-stale's bordered warn tone |
| 105 | rather than a filled marker. */}} |
| 106 | {{range .Drift}}<p><span class="chip chip-stale">{{.}}</span></p>{{end}} |
| 107 | </section> |
| 108 | {{else}} |
| 109 | {{/* An empty box list has two causes and they are not the same fact: no box |
| 110 | has reported, or the read that would have listed them failed. Saying the |
| 111 | first when the second happened is the confident negative claim this page |
| 112 | exists to avoid — so the handler carries which one it is. */}} |
| 113 | {{if $.ArchBoxesUnread}}<p>The box list could not be read — this is not a claim that the deployment has no boxes.</p> |
| 114 | {{else}}<p>No box has reported to this deployment yet.</p>{{end}} |
| 115 | {{end}} |
| 116 | |
| 117 | {{range .CARLOS.Groups}} |
| 118 | <h3>{{.Title}}</h3> |
| 119 | {{template "archtable" .}} |
| 120 | {{end}} |
| 121 | |
| 122 | {{/* The second bank is named for what is TRUE of it in every deployment, |
| 123 | not for what it happens to be here. This console serves every CARLOS |
| 124 | estate; "legacy" describes Tito's Kubernetes cluster and would be |
| 125 | wrong, and unexplained, on the next account. */}} |
| 126 | <h2>Also in this account</h2> |
| 127 | <p class="small">Everything else the account is carrying. The console has no |
| 128 | first-hand knowledge of these — it can see them, and it cannot trace a path |
| 129 | to any of them from a box it runs or the bucket it deploys from.</p> |
| 130 | {{/* One picture per region, stacked down the page. Region is the |
| 131 | STRUCTURE here, not a word on a card: a box is the only thing on this |
| 132 | diagram that is really in a region, and everything else is drawn in the |
| 133 | band of the box it reaches (archregion.go). That is also what keeps |
| 134 | each picture inside the 64rem column — a band is at most four nodes |
| 135 | wide — so this page scrolls down and never across. */}} |
| 136 | {{range .Rest.Bands}} |
| 137 | <h4 class="archband">{{.Label}}</h4> |
| 138 | <div class="archwrap">{{.SVG}}</div> |
| 139 | {{end}} |
| 140 | {{range .Rest.Groups}} |
| 141 | <h3>{{.Title}}</h3> |
| 142 | {{template "archtable" .}} |
| 143 | {{end}} |
| 144 | |
| 145 | {{/* The inventory's own partial-read list: which service or region the |
| 146 | estate read could not see. A page that quietly dropped these would be |
| 147 | claiming the estate is smaller than it is. */}} |
| 148 | {{if .Partial}} |
| 149 | <h2>Reads that failed</h2> |
| 150 | <ul class="small">{{range .Partial}}<li>{{.}}</li>{{end}}</ul> |
| 151 | {{end}} |
| 152 | {{end}} |
| 153 | |
| 154 | {{template "bottom" .}}{{end}} |
| 155 | |
| 156 | {{/* One resource table, rendered identically in both banks — the two halves |
| 157 | of this page differ in what they contain and in nothing else. */}} |
| 158 | {{define "archtable"}}<div class="tablewrap"><table> |
| 159 | <tr><th>Name</th><th>Type</th><th>Region</th><th>Detail</th></tr> |
| 160 | {{range .Rows}}<tr><td>{{.Name}}</td><td><code>{{.NativeType}}</code></td><td>{{.Region}}</td><td class="small">{{.Detail}}</td></tr>{{end}} |
| 161 | </table></div>{{end}} |
| 162 | |