add HTML output to arthur
This commit is contained in:
parent
8078330a2e
commit
900ac27d63
|
@ -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
|
||||
|
|
|
@ -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: </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>
|
Loading…
Reference in New Issue