~pixelherodev/ikiru

32694d918d951b19713f64db2ef8502b9d1b86f8 — Noam Preil 3 months ago c92d8f8
Use a proper template
3 files changed, 83 insertions(+), 49 deletions(-)

M example/example.yaml
M main.go
A templates/dashboard.html
M example/example.yaml => example/example.yaml +3 -14
@@ 38,7 38,7 @@ rows:
        - {
            open: true,
            name: "meta.sr.ht",
            width: 8,
            width: 6,
            rows: [
                {
                    metrics: [


@@ 52,18 52,7 @@ rows:
                            ],
                            title: "network status",
                            duration: 120,
                            width: 6,
                        },
                        {
                            format: "svg",
                            queries: [
                                "rate(node_network_receive_bytes_total{instance=\"node.meta.sr.ht:80\",device=\"eth0\"}[5m]) / 1024 / 1024"
                            ],
                            colors: [
                                "#03e0bb"
                            ],
                            title: "network status",
                            duration: 120,
                            format: "png",
                            width: 6,
                        },



@@ 74,7 63,7 @@ rows:
        - {
            open: true,
            name: "git.sr.ht",
            width: 4,
            width: 6,
            rows: [
                {
                    metrics: [

M main.go => main.go +38 -35
@@ 6,6 6,7 @@ import (
	"flag"
	"fmt"
	yaml "gopkg.in/yaml.v2"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"


@@ 49,6 50,13 @@ type Metric struct {
	Renderer string        `yaml:"renderer"`
	Type     string        `yaml:"type"`
	Duration time.Duration `yaml:"duration"`
	// Not in the YAML; this caches the resultant image for the template
	Result struct {
		Error  error
		Width  int
		Height int
		Image  template.HTML
	}
}

func (m *Metric) UnmarshalYAML(unmarshal func(interface{}) error) error {


@@ 61,64 69,59 @@ func (m *Metric) UnmarshalYAML(unmarshal func(interface{}) error) error {
	m.Duration = 60
	m.Width = 6
	m.Title = ""
	m.Result.Error = errors.New("Not yet generated!")
	m.Result.Image = ""
	type plain Metric
	return unmarshal((*plain)(m))
}

func generateHook(dash Dashboard, gnuplotPath string, static string) {
	header := fmt.Sprintf("<html>\n\t<head>\n\t\t<title>%s - Ikiru</title>\n\t\t<link href=\"style.css\" rel=\"stylesheet\">\n\t</head>\n\t<body>\n\t\t<p class=\"navbar-brand\">sourcehut <span class=\"text-danger\">ikiru</span></p>\n\t\t<br>\n\t\t", dash.Name)
	http.HandleFunc(dash.Path, func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(header))
		for _, row := range dash.Rows {
			w.Write([]byte("<div class=\"row container-fluid\">"))
			for _, cat := range row.Categories {
				w.Write([]byte("\n\t<details "))
				if cat.Open {
					w.Write([]byte("open "))
				}
				fmt.Fprintf(w, "class=\"col-lg-%d\">\n\t\t<summary>%s</summary>", cat.Width, cat.Name)
				for _, metrow := range cat.Rows {
					w.Write([]byte("<div class=\"row\">\n"))
					for _, metric := range metrow.Metrics {
	http.HandleFunc(fmt.Sprintf("%s", dash.Path), func(w http.ResponseWriter, r *http.Request) {
		tmpl, err := template.ParseFiles("templates/dashboard.html")
		if err != nil {
			fmt.Fprintf(w, "Error: %s\n", err)
		}
		for rowdex := range dash.Rows {
			for catdex := range dash.Rows[rowdex].Categories {
				for metrow := range dash.Rows[rowdex].Categories[catdex].Rows {
					for metricdex := range dash.Rows[rowdex].Categories[catdex].Rows[metrow].Metrics {
						metric := &dash.Rows[rowdex].Categories[catdex].Rows[metrow].Metrics[metricdex]
						image, err := []byte(""), (error)(nil)
						if static != "" {
							image, err = ioutil.ReadFile(static)
						} else if metric.Type == "raw" {
							image, err = getImage(gnuplotPath, metric, 844, 478)
							image, err = getImage(gnuplotPath, *metric, 844, 478)
							metric.Result.Width = 844
							metric.Result.Height = 478
						} else if metric.Type == "bucket" {
							image, err = getBox(gnuplotPath, metric, 844, 478)
							if err == nil {
								fmt.Fprintf(w, "<div class=container>%s</div>\n", image)
								continue
							}
							metric.Result.Width = 844
							metric.Result.Height = 478
							image, err = getBox(gnuplotPath, *metric, 844, 478)
						} else {
							err = errors.New("invalid metric type")
						}
						if err != nil {
							fmt.Fprintf(w, "<b style='color:red display:table-cell' class='container-fluid col-sm-%d'>%s</b>", metric.Width, err)
							continue
						}
						if metric.Format == "svg" {
							// Drop XML header
							nl := strings.Index(string(image), "\n")
							image = image[nl+1:]
							// embed class in image tag
							nl = strings.Index(string(image), "\n")
							w.Write(image[0:nl])
							fmt.Fprintf(w, "\tclass=\"col-lg-%d container-fluid\"\n\t\t", metric.Width)
							w.Write(image[nl+1:])
							nl = strings.Index(string(image), ">")
							image = image[nl+1:]
						} else {
							b64 := base64.StdEncoding.EncodeToString(image)
							fmt.Fprintf(w, "<img class=\"col-lg-%d\" src=\"data:image/%s;base64, %s\"/>", metric.Width, metric.Format, b64)
							image = []byte(base64.StdEncoding.EncodeToString(image))
						}
						if err == nil {
							metric.Result.Image = template.HTML(image)
							metric.Result.Error = nil
						}
						metric.Result.Error = err
					}
					w.Write([]byte("</div>"))
				}
				w.Write([]byte("</details>"))
			}
			w.Write([]byte("\n</div>"))
		}
		w.Write([]byte("</body></html>"))
		err = tmpl.Execute(w, dash)
		if err != nil {
			fmt.Fprintf(w, "Error: %s\n", err)
		}
	})
}


A templates/dashboard.html => templates/dashboard.html +42 -0
@@ 0,0 1,42 @@
<html>
	<head>
		<title>{{.Name}} - Ikiru</title>
		<link href="style.css" rel="stylesheet">
	</head>
	<body>
		<p class="navbar-brand">sourcehut <span class="text-danger">ikiru</span></p>
		<br>
{{range .Rows}}
		<div class="row container-fluid">
	{{range .Categories}}
			<details {{if .Open}}open {{end}} class="col-lg-{{.Width}}">
				<summary>{{.Name}}</summary>
		{{range .Rows}}
				<div class="row">
				{{range .Metrics}}
					{{if .Result.Error}}
						<b>Error: {{.Result.Error}}</b>
					{{else}}
						{{if eq .Format "svg"}}
							<!-- Image is a raw SVG with the opening tag stripped out -->
							<svg class="col-lg-{{.Width}} container-fluid"
								width="{{.Result.Width}}" height="{{.Result.Height}}"
								viewBox="0 0 {{.Result.Width}} {{.Result.Height}}"
								xmlns="http://www.w3.org/2000/svg"
								xmlns:xlink="http://www.w3.org/1999/xlink"
							>
							{{.Result.Image}}
						{{else}}
							<!-- Image is encoded in base64 -->
							<img class="col-lg-{{.Width}} container" src="data:image/{{.Format}};base64, {{.Result.Image}}"/>
						{{end}}
					{{end}}
				{{end}}
				</div>
		{{end}}
			</details>
	{{end}}
		</div>
{{end}}
	</body>
</html>