Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dashboard panels to all available dashboards #34

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ server/vendor/
**/.test/
**/.release/
**/.tarballs/
/.idea/
62 changes: 49 additions & 13 deletions server/main.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,36 @@
package main

import (
"net/http"
"os"
"time"

"github.com/MacroPower/macropower-analytics-panel/server/cacher"
"github.com/MacroPower/macropower-analytics-panel/server/collector"
"github.com/MacroPower/macropower-analytics-panel/server/payload"
"github.com/MacroPower/macropower-analytics-panel/server/worker"
"github.com/alecthomas/kong"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/prometheus/common/version"
"net/http"
"os"
"strconv"
"time"
)

var (
cli struct {
HTTPAddress string `help:"Address to listen on for payloads and metrics." env:"HTTP_ADDRESS" default:":8080"`
SessionTimeout time.Duration `help:"The maximum duration that may be added between heartbeats. 0 = auto." type:"time.Duration" env:"SESSION_TIMEOUT" default:"0"`
MaxCacheSize int `help:"The maximum number of sessions to store in the cache before resetting. 0 = unlimited." env:"MAX_CACHE_SIZE" default:"100000"`
LogFormat string `help:"One of: [logfmt, json]." env:"LOG_FORMAT" enum:"logfmt,json" default:"logfmt"`
LogRaw bool `help:"Outputs raw payloads as they are received." env:"LOG_RAW"`
DisableUserMetrics bool `help:"Disables user labels in metrics." env:"DISABLE_USER_METRICS"`
DisableSessionLog bool `help:"Disables logging sessions to the console." env:"DISABLE_SESSION_LOG"`
DisableVariableLog bool `help:"Disables logging variables to the console." env:"DISABLE_VARIABLE_LOG"`
HTTPAddress string `help:"Address to listen on for payloads and metrics." env:"HTTP_ADDRESS" default:":8080"`
SessionTimeout time.Duration `help:"The maximum duration that may be added between heartbeats. 0 = auto." type:"time.Duration" env:"SESSION_TIMEOUT" default:"0"`
MaxCacheSize int `help:"The maximum number of sessions to store in the cache before resetting. 0 = unlimited." env:"MAX_CACHE_SIZE" default:"100000"`
LogFormat string `help:"One of: [logfmt, json]." env:"LOG_FORMAT" enum:"logfmt,json" default:"logfmt"`
LogRaw bool `help:"Outputs raw payloads as they are received." env:"LOG_RAW"`
DisableUserMetrics bool `help:"Disables user labels in metrics." env:"DISABLE_USER_METRICS"`
DisableSessionLog bool `help:"Disables logging sessions to the console." env:"DISABLE_SESSION_LOG"`
DisableVariableLog bool `help:"Disables logging variables to the console." env:"DISABLE_VARIABLE_LOG"`
DashboardUpdateToken string `help:"Grafana token for updating dashboards." env:"DASHBOARD_UPDATE_TOKEN"`
GrafanaUrl string `help:"Grafana base URL, which is separate from analytics." env:"GRAFANA_URL"`
Timeout string `help:"Timeout for auto analytic adder interval" env:"TIMEOUT" default:"24" `
DashboardFilter string `help:"Update only single dashboard matching this name, useful to test analytics adder" env:"DASHBOARD_FILTER"`
}
)

Expand Down Expand Up @@ -73,6 +78,37 @@ func main() {
prometheus.MustRegister(exporter, metricExporter)
mux.Handle("/metrics", promhttp.Handler())

err := http.ListenAndServe(cli.HTTPAddress, mux)
workerClient := worker.Client{
GrafanaUrl: cli.GrafanaUrl,
Token: cli.DashboardUpdateToken,
AnalyticsUrl: cli.HTTPAddress,
Logger: logger,
Filter: cli.DashboardFilter,
}

mux.HandleFunc("/patch-dashboards", func(w http.ResponseWriter, r *http.Request) {
workerClient.AddAnalyticsToDashboards()
})

timeout, err := strconv.Atoi(cli.Timeout)
if err != nil {
level.Error(logger).Log(
"msg", "Failed to parse timeout",
"version", version.Version,
"branch", version.Branch,
"revision", version.Revision,
)
}

ticker := time.NewTicker(time.Duration(timeout) * time.Hour)
defer ticker.Stop()

go func() {
for range ticker.C {
workerClient.AddAnalyticsToDashboards()
}
}()

err = http.ListenAndServe(cli.HTTPAddress, mux)
ctx.FatalIfErrorf(err)
}
99 changes: 99 additions & 0 deletions server/worker/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package worker

import (
"bytes"
"errors"
"fmt"
"github.com/go-kit/kit/log"
"io"
"net/http"
"strconv"
)

type Client struct {
AnalyticsUrl string
GrafanaUrl string
Token string
Logger log.Logger
Filter string
}

const contentTypeJson = "application/json"

func (api *Client) Get(endpoint string) ([]byte, error) {
req, err := api.prepareRequest("GET", endpoint)
if err != nil {
return nil, err
}

client := &http.Client{}
res, err := client.Do(req)
if res.StatusCode != http.StatusOK {
errorMessage := fmt.Sprintf("Request failed\nEndpoint: %s\nStatus: %s", endpoint, strconv.Itoa(res.StatusCode))

return nil, errors.New(errorMessage)
}

defer res.Body.Close()
if err != nil {
return nil, err
}

body, err := io.ReadAll(res.Body)
if err != nil {
return nil, err
}

return body, nil
}

func (api *Client) Post(endpoint string, payload []byte) ([]byte, error) {
req, err := api.prepareRequest("POST", endpoint)
if err != nil {
return nil, err
}

setContentType(req, contentTypeJson)
addBodyToRequest(req, payload)

client := &http.Client{}
res, err := client.Do(req)
if res.StatusCode != http.StatusOK {
errorMessage := fmt.Sprintf("Request failed\nEndpoint: %s\nStatus: %s", endpoint, strconv.Itoa(res.StatusCode))

return nil, errors.New(errorMessage)
}
defer res.Body.Close()

if err != nil {
return nil, err
}

body, err := io.ReadAll(res.Body)
if err != nil {
return nil, err
}

return body, nil
}

func (api *Client) prepareRequest(method string, endpoint string) (*http.Request, error) {
req, err := http.NewRequest(method, api.GrafanaUrl+endpoint, nil)
if err != nil {
return nil, errors.New(fmt.Sprintf("Failed to create new HTTP Request\nMethod: %s\nEndpoint: %s", method, endpoint))
}

req.Header.Add("Authorization", "Bearer "+api.Token)
req.Header.Add("Accept", contentTypeJson)

return req, nil
}

func setContentType(req *http.Request, contentType string) {
req.Header.Add("Content-Type", contentType)
}

func addBodyToRequest(req *http.Request, payload []byte) {
req.Body = io.NopCloser(bytes.NewBuffer(payload))
req.ContentLength = int64(len(payload))
}
Loading