add HTML output to arthur

This commit is contained in:
Max Erenberg 2023-04-04 20:17:04 -04:00 committed by Mirror
parent 8078330a2e
commit 900ac27d63
2 changed files with 148 additions and 19 deletions

View File

@ -1,21 +1,24 @@
package main
import (
"bytes"
_ "embed"
"encoding/json"
"flag"
"fmt"
"html/template"
"io/ioutil"
"log"
"net"
"os"
"strings"
"text/tabwriter"
"time"
serverArthurPkg "git.csclub.uwaterloo.ca/public/merlin/arthur"
)
// var DEFAULT_SOCKET_PATH = "/home/mirror/merlin/merlin.sock"
var DEFAULT_SOCKET_PATH = "/mirror/merlin/run/merlin-go.sock"
var DEFAULT_SOCKET_PATH = "/mirror/merlin/run/merlin.sock"
var HELP_MESSAGE = `USAGE:
arthur [-h|--help] [-json] [-sock <socket_path>] COMMAND
@ -27,6 +30,9 @@ COMMANDS:
FLAGS:
`
//go:embed layout.html
var layoutHtmlTemplate string
func main() {
log.SetPrefix("[ERROR]: ")
log.SetFlags(0)
@ -43,13 +49,15 @@ func main() {
var shouldOutputJson bool
flag.BoolVar(&shouldOutputJson, "json", false, "JSON output")
var shouldOutputHtml bool
flag.BoolVar(&shouldOutputHtml, "html", false, "HTML output")
flag.Parse()
if flag.NArg() < 1 {
os.Exit(1)
}
command := os.Args[1]
command := flag.Args()[0]
conn, err := net.Dial("unix", *sockPath)
if err != nil {
@ -67,35 +75,57 @@ func main() {
if err != nil {
log.Fatal(err)
}
// TODO: merlin response should always be JSON
if strings.HasPrefix(command, "sync") {
fmt.Println(string(response))
return
}
statusInfo := serverArthurPkg.StatusInfo{}
err = json.Unmarshal(response, &statusInfo)
if err != nil {
log.Fatal(err)
}
if command == "status" && shouldOutputJson {
if shouldOutputJson {
jsonOutput, jsonErr := json.MarshalIndent(&statusInfo, "", " ")
if jsonErr == nil {
fmt.Println(string(jsonOutput))
} else {
log.Fatal(jsonErr)
}
return
} else if shouldOutputHtml {
funcs := template.FuncMap{
"unixTimeToStr": unixTimeToStr,
"minus": func(a, b int64) int64 { return a - b },
}
tmpl := template.Must(template.New("layout").
Funcs(funcs).
Parse(layoutHtmlTemplate))
var buf bytes.Buffer
data := map[string]interface{}{
"Repos": statusInfo.Repos,
"Now": time.Now().Unix(),
}
err = tmpl.Execute(&buf, data)
if err != nil {
log.Fatal(err)
}
fmt.Print(buf.String())
} else {
writer := tabwriter.NewWriter(os.Stdout, 5, 5, 5, ' ', 0)
// print out the state of each repo in the config (last and next sync time + if it is currently running)
fmt.Fprintf(writer, "Repository\tLast Synced\tNext Expected Sync\tLast Exit\tRunning\n")
for _, repo := range statusInfo.Repos {
fmt.Fprintf(writer, "%s\t%s\t%s\t%s\t%t\n",
repo.Name,
unixTimeToStr(repo.LastAttemptStartTime),
unixTimeToStr(repo.NextSyncTime),
repo.LastAttemptExit,
repo.IsRunning,
)
}
writer.Flush()
}
writer := tabwriter.NewWriter(os.Stdout, 5, 5, 5, ' ', 0)
// print out the state of each repo in the config (last and next sync time + if it is currently running)
fmt.Fprintf(writer, "Repository\tLast Synced\tNext Expected Sync\tLast Exit\tRunning\n")
for _, repo := range statusInfo.Repos {
fmt.Fprintf(writer, "%s\t%s\t%s\t%s\t%t\n",
repo.Name,
unixTimeToStr(repo.LastAttemptStartTime),
unixTimeToStr(repo.NextSyncTime),
repo.LastAttemptExit,
repo.IsRunning,
)
}
writer.Flush()
}
var location *time.Location

View File

@ -0,0 +1,99 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CSC Mirror Status</title>
<style>
:root {
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
"Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}
* {
box-sizing: border-box;
}
.main-title {
text-align: center;
}
.generated-at {
margin: 2rem 0;
display: flex;
flex-wrap: wrap;
justify-content: center;
}
ul {
list-style: none;
padding: 0;
margin: 0;
}
.repos {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(min(400px, 95%), 1fr));
grid-auto-rows: auto;
gap: 1rem;
}
.repo {
border: 1px solid black;
border-radius: 0.25rem;
padding: 1rem;
display: flex;
flex-direction: column;
align-items: flex-start;
}
.repo-name {
width: max-content;
margin: 0 0 0.5rem 0;
font-size: 1.125rem;
line-height: 1.75rem;
}
.repo-data-item {
padding-left: 0.5rem;
text-indent: -0.5rem;
}
.succeeded {
background-color: rgb(187, 247, 208);
}
.out-of-date {
background-color: rgb(252, 165, 165);
}
.failed {
background-color: rgb(254, 202, 202);
}
.unknown {
background-color: rgb(229, 229, 229);
}
</style>
</head>
<body>
<h1 class="main-title">CSC Mirror Status</h1>
<div class="generated-at">
<div>Generated at:&nbsp;</div>
<div>{{unixTimeToStr .Now}}</div>
</div>
<ul class="repos">
{{range .Repos}}
{{$outOfDate := (gt (minus $.Now .LastSuccessfulAttemptStartTime) 86400)}}
{{$succeeded := (or (eq .LastAttemptStartTime .LastSuccessfulAttemptStartTime) (and .IsRunning (eq .LastAttemptExit "completed")))}}
{{$failed := (eq .LastAttemptExit "failed")}}
<li class="repo {{if $outOfDate}} out-of-date {{else if $succeeded}} succeeded {{else if $failed}} failed {{else}} unknown {{end}}">
<a href="./{{.Name}}">
<h4 class="repo-name">{{.Name}}</h4>
</a>
<div class="repo-data-item">
{{if .IsRunning}} Is currently syncing {{else}} Is not syncing {{end}}
</div>
<div class="repo-data-item">
Last sync time: {{unixTimeToStr .LastAttemptStartTime}}
</div>
<div class="repo-data-item">
Last sync result: {{.LastAttemptExit}}
</div>
<div class="repo-data-item">
Last successful sync time: {{unixTimeToStr .LastSuccessfulAttemptStartTime}}
</div>
</li>
{{end}}
</ul>
</body>
</html>