| 1 | {{define "app_instances"}}{{template "top" .}} |
| 2 | {{if .Flash}}<p class="banner">{{.Flash}}</p>{{end}} |
| 3 | {{if .FlashErr}}<p class="banner error">{{.FlashErr}}</p>{{end}} |
| 4 | {{template "appnav" .}} |
| 5 | |
| 6 | <h2>Instances</h2> |
| 7 | {{if .CanRestartApp}}<form method="post" action="{{.Base}}/apps/{{.AppName}}/restart" class="inline"> |
| 8 | <input type="hidden" name="csrf" value="{{.CSRF}}"> |
| 9 | <button type="submit" class="btn-sm">{{.BrowserRestartCopy.Button}}</button> |
| 10 | </form>{{end}} |
| 11 | {{if or .InstanceRows .InstanceQuery.Q .InstanceQuery.Health .InstanceQuery.Channel}} |
| 12 | <form method="get" class="listctl"> |
| 13 | <input type="search" name="q" value="{{.InstanceQuery.Q}}" placeholder="Filter addresses" aria-label="Filter addresses"> |
| 14 | {{if .InstanceQuery.Health}}<input type="hidden" name="health" value="{{healthSlug .InstanceQuery.Health}}">{{end}} |
| 15 | <label class="visually-hidden" for="instance-channel">Channel</label> |
| 16 | <select id="instance-channel" name="channel"> |
| 17 | <option value=""{{if not .InstanceQuery.Channel}} selected{{end}}>{{.InstanceCopy.AllChannels}}</option> |
| 18 | {{if .InstanceUnknownChannel}}<option value="{{.InstanceQuery.Channel}}" selected>{{.InstanceQuery.Channel}}</option>{{end}} |
| 19 | {{range .InstanceChannels}}<option value="{{.}}"{{if eq . $.InstanceQuery.Channel}} selected{{end}}>{{.}}</option>{{end}} |
| 20 | </select> |
| 21 | <label><input type="checkbox" name="group" value="channel"{{if .InstanceQuery.Group}} checked{{end}}> {{.InstanceCopy.GroupChannel}}</label> |
| 22 | <button>Filter</button> |
| 23 | </form> |
| 24 | <p class="small"> |
| 25 | <a href="{{.InstanceAllURL}}"{{if not .InstanceQuery.Health}} aria-current="true"{{end}}>All</a> |
| 26 | {{range .InstancePage.Links}} |
| 27 | · <a href="{{.URL}}"{{if .Active}} aria-current="true"{{end}}>{{.Word}} {{.Count}}</a> |
| 28 | {{end}} |
| 29 | </p> |
| 30 | {{end}} |
| 31 | {{if .InstanceRows}} |
| 32 | <div class="tablewrap"> |
| 33 | <table class="instances"> |
| 34 | <thead><tr><th scope="col">Address</th><th scope="col">Health</th><th scope="col">Version</th><th scope="col">Channel</th><th scope="col">CPU</th><th scope="col">Memory</th></tr></thead> |
| 35 | {{if .InstanceQuery.Group}} |
| 36 | {{range .InstanceGroups}} |
| 37 | <tbody> |
| 38 | <tr class="instance-group"><th colspan="6" scope="rowgroup"><code>{{.Channel}}</code> <span class="small">{{$.InstanceCopy.Target}}: {{if .TargetErr}}—{{else if .TargetVersion}}<code>{{.TargetVersion}}</code>{{else}}—{{end}} · Instances: {{len .Rows}} / {{.Total}}</span></th></tr> |
| 39 | {{range .Rows}}{{template "instance_row" (instanceRowPage $ .)}}{{end}} |
| 40 | </tbody> |
| 41 | {{end}} |
| 42 | {{else}} |
| 43 | <tbody>{{range .InstanceRows}}{{template "instance_row" (instanceRowPage $ .)}}{{end}}</tbody> |
| 44 | {{end}} |
| 45 | </table> |
| 46 | </div> |
| 47 | <p class="small">Version is what each copy reports it is running — a dash means |
| 48 | it hasn't reported yet. Charts cover the last 48 hours; the memory line is drawn |
| 49 | against that copy's own peak.</p> |
| 50 | {{if .InstancePage.NextURL}}<p class="small"><a href="{{.InstancePage.NextURL}}">Next {{len .InstanceRows}} shown of {{.InstancePage.Total}} →</a></p>{{end}} |
| 51 | {{else if or .InstanceQuery.Q .InstanceQuery.Health .InstanceQuery.Channel}} |
| 52 | <p>No addresses match.</p> |
| 53 | {{else}} |
| 54 | <p>Nothing running yet. Publish a version, then give it a web address:</p> |
| 55 | {{if .AttachEnable}}<pre><code>{{.AttachEnable}} |
| 56 | {{.AttachCmd}}</code></pre>{{else if .AttachCmd}}<pre><code>{{.AttachCmd}}</code></pre>{{end}} |
| 57 | <p class="small">The address comes live within a couple of minutes.</p> |
| 58 | {{end}} |
| 59 | |
| 60 | <h2>Placement</h2>{{if .RegionLine}} |
| 61 | <p id="app-region-line"{{if .RegionLineWarn}} class="banner error"{{end}}>{{.RegionLine}}</p>{{end}} |
| 62 | {{if .AppPlacementKnown}} |
| 63 | <p>{{if .AppPlacement}}This app runs on your fleet <a href="{{.Nav}}/fleets/{{fleetName .AppPlacement}}"><code>{{fleetName .AppPlacement}}</code></a>.{{else}}This app runs on the shared fleet.{{end}}</p> |
| 64 | {{if and .IsOwner .OwnedFleetNames}} |
| 65 | <form method="post" action="{{.Base}}/apps/{{.AppName}}/place" class="settings-row"> |
| 66 | <input type="hidden" name="csrf" value="{{.CSRF}}"> |
| 67 | <label class="visually-hidden" for="place-target">Where this app runs</label> |
| 68 | <select id="place-target" name="target"> |
| 69 | <option value=""{{if not .AppPlacement}} selected{{end}}>Shared fleet</option> |
| 70 | {{range .OwnedFleetNames}}<option value="fleet/{{.}}"{{if eq (printf "fleet/%s" .) $.AppPlacement}} selected{{end}}>fleet/{{.}}</option>{{end}} |
| 71 | </select> |
| 72 | <button type="submit" class="btn-sm">Move</button> |
| 73 | </form> |
| 74 | <p class="small">Moving changes where this app <em>serves</em>. Data does not move: |
| 75 | hibernated instance state stays in the old placement's bucket.</p> |
| 76 | {{end}} |
| 77 | {{else}} |
| 78 | <p class="small">Could not read where this app runs.</p> |
| 79 | {{end}} |
| 80 | |
| 81 | {{if and .Instances .Instances.Reporting .Instances.Instances}} |
| 82 | <details> |
| 83 | <summary>Reported by the app itself ({{len .Instances.Instances}})</summary> |
| 84 | <p class="small">This app tracks its own internal workers and reports them to the platform.</p> |
| 85 | <div class="tablewrap"> |
| 86 | <table> |
| 87 | <tr><th scope="col">Name</th><th scope="col">Status</th><th scope="col">Region</th><th scope="col">Detail</th></tr> |
| 88 | {{range .Instances.Instances}} |
| 89 | <tr> |
| 90 | <td><code>{{.Slug}}</code></td> |
| 91 | <td>{{if .Status}}{{.Status}}{{else}}—{{end}}</td> |
| 92 | <td>{{if .Region}}{{.Region}}{{else}}—{{end}}</td> |
| 93 | <td>{{if .Detail}}{{.Detail}}{{else}}—{{end}}</td> |
| 94 | </tr> |
| 95 | {{end}} |
| 96 | </table> |
| 97 | </div> |
| 98 | </details> |
| 99 | {{end}} |
| 100 | {{template "bottom" .}}{{end}} |
| 101 | |
| 102 | {{define "instance_row"}} |
| 103 | <tr> |
| 104 | <td class="instance-address"><a href="{{.Page.Nav}}/apps/{{.Page.AppName}}/instances/{{.Row.Host}}{{.Page.InstanceQuerySuffix}}">{{.Row.Host}}</a> <a class="instance-open" href="https://{{.Row.Host}}" aria-label="{{.Page.InstanceCopy.Open}} {{.Row.Host}}">{{icon "external-link"}}</a></td> |
| 105 | <td><span class="chip chip-health-{{healthTone .Row.HealthWord}}">{{.Row.HealthWord}}</span></td> |
| 106 | <td>{{if .Row.Version}}<code>{{.Row.Version}}</code>{{else}}—{{end}}</td> |
| 107 | <td><code>{{.Row.Channel}}</code></td> |
| 108 | {{if .Row.Metrics.Reporting}} |
| 109 | <td><span class="cell-value">{{pct .Row.Metrics.CPUFrac}}</span>{{if .Row.Metrics.SparkCPU}}<svg class="cellspark" aria-hidden="true" viewBox="0 0 240 40" preserveAspectRatio="none"><polyline fill="none" stroke="currentColor" points="{{.Row.Metrics.SparkCPU}}"/></svg>{{end}}</td> |
| 110 | <td><span class="cell-value">{{bytesOrDash .Row.Metrics.MemBytes}}</span>{{if .Row.Metrics.SparkMem}}<svg class="cellspark" aria-hidden="true" viewBox="0 0 240 40" preserveAspectRatio="none"><polyline fill="none" stroke="currentColor" points="{{.Row.Metrics.SparkMem}}"/></svg>{{end}}</td> |
| 111 | {{else}}<td colspan="2"><span class="small">no data</span></td>{{end}} |
| 112 | </tr> |
| 113 | {{end}} |
| 114 | |