Skip to content

Commit

Permalink
v 0.3.9 pre release 20240914
Browse files Browse the repository at this point in the history
  • Loading branch information
peekjef72 committed Sep 14, 2024
1 parent b44ee0c commit 4f2623d
Show file tree
Hide file tree
Showing 12 changed files with 134 additions and 42 deletions.
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ This project adheres to [Semantic Versioning](http://semver.org/) and [Keep a ch

<!--next-version-placeholder-->
## 0.3.9 / not release

- renamed entrypoint /healthz to /health : response format depends on "accept" header (application/json, text/plain, text/html default)
- updated entrypoint /status : response format depends on "accept" header (application/json, text/html default)
- added cmd line --model_name to perform test with model and uri in dry-run mode
- added out format for passwd_encrypt that can be cut/pasted into config file.
- added InvalidLogin error cases: no cipher (auth_key not provided) or (invalid auth_key). For those cases if target is up, metrics for collectors status will return code 2; invalid_login
- added GET /loglevel to retrieve current level, add POST /loglevel[/level] to set loglevel to level directly
- added debug message for basic auth (auth_config.mode=basic) and bearer (auth_config.mode=token)
- added debug message for ba:qsic auth (auth_config.mode=basic) and bearer (auth_config.mode=token)
- loglevel link in landing page
- fixed typos

Expand Down
2 changes: 1 addition & 1 deletion client.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ func (c *Client) getJSONResponse(resp *resty.Response) any {

body := resp.Body()
if len(body) > 0 {
content_type := resp.Header().Get("content-type")
content_type := resp.Header().Get(contentTypeHeader)
if strings.Contains(content_type, "application/json") {
// tmp := make([]byte, len(body))
// copy(tmp, body)
Expand Down
3 changes: 2 additions & 1 deletion collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"context"
"errors"
"fmt"

"sync"
Expand Down Expand Up @@ -68,7 +69,7 @@ func NewCollector(
if act.Type() == metric_action {
mc := act.GetMetric()
if mc == nil {
return nil, fmt.Errorf("MetricAction nil received")
return nil, errors.New("MetricAction nil received")
}
mf, err := NewMetricFamily(logContext, mc, constLabels, cc.customTemplate)
if err != nil {
Expand Down
73 changes: 58 additions & 15 deletions content.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package main

import (
"encoding/json"
"fmt"
"html/template"
"net/http"
"runtime"
"strings"

"github.com/prometheus/common/version"
"gopkg.in/yaml.v3"
Expand Down Expand Up @@ -60,6 +62,10 @@ const (
You are probably looking for its <a href="{{ .MetricsPath }}">metrics</a> handler.</p>
{{- end }}
{{ define "content.health" -}}
<H2>OK</H2>
{{- end }}
{{ define "content.config" -}}
<h2>Configuration</h2>
<pre>{{ .Config }}</pre>
Expand Down Expand Up @@ -118,14 +124,14 @@ const (
)

type versionInfo struct {
Version string
Revision string
Branch string
BuildUser string
BuildDate string
GoVersion string
StartTime string
ReloadTime string
Version string `json:"version"`
Revision string `json:"revision"`
Branch string `json:"branch"`
BuildUser string `json:"build_user"`
BuildDate string `json:"build_date"`
GoVersion string `json:"go_version"`
StartTime string `json:"start_time"`
ReloadTime string `json:"reload_time"`
}
type tdata struct {
ExporterName string
Expand All @@ -146,6 +152,7 @@ type tdata struct {

var (
allTemplates = template.Must(template.New("").Parse(templates))
healthTemplate = pageTemplate("health")
homeTemplate = pageTemplate("home")
configTemplate = pageTemplate("config")
targetsTemplate = pageTemplate("targets")
Expand All @@ -168,6 +175,29 @@ func HomeHandlerFunc(metricsPath string, exporter Exporter) func(http.ResponseWr
})
}
}
func HealthHandlerfunc(metricsPath string, exporter Exporter) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
var status []byte
content_type := r.Header.Get(acceptHeader)
if strings.Contains(content_type, applicationJSON) {
w.Header().Set(contentTypeHeader, applicationJSON)
status = []byte("{\"status\"=\"ok\"}")
w.Header().Set(contentLengthHeader, fmt.Sprint(len(status)))
} else if strings.Contains(content_type, textPLAIN) {
w.Header().Set(contentTypeHeader, textPLAIN)
status = []byte("OK")
} else {
w.Header().Set(contentTypeHeader, textHTML)
healthTemplate.Execute(w, &tdata{
ExporterName: exporter.Config().Globals.ExporterName,
MetricsPath: metricsPath,
DocsUrl: docsUrl,
})
return
}
w.Write(status)
}
}

// ConfigHandlerFunc is the HTTP handler for the `/config` page. It outputs the configuration marshaled in YAML format.
func ConfigHandlerFunc(metricsPath string, exporter Exporter) func(http.ResponseWriter, *http.Request) {
Expand Down Expand Up @@ -200,13 +230,26 @@ func StatusHandlerFunc(metricsPath string, exporter Exporter) func(http.Response
StartTime: exporter.GetStartTime(),
ReloadTime: exporter.GetReloadTime(),
}

statusTemplate.Execute(w, &tdata{
ExporterName: exporter.Config().Globals.ExporterName,
MetricsPath: metricsPath,
DocsUrl: docsUrl,
Version: vinfos,
})
content_type := r.Header.Get(acceptHeader)
if strings.Contains(content_type, applicationJSON) {
res, err := json.Marshal(vinfos)
if err != nil {
HandleError(http.StatusBadRequest, err, metricsPath, exporter, w, r)
return
}
w.Header().Set(contentTypeHeader, applicationJSON)
w.Header().Set(contentLengthHeader, fmt.Sprint(len(res)))
w.WriteHeader(http.StatusOK)
w.Write(res)
} else {
w.Header().Set(contentTypeHeader, textHTML)
statusTemplate.Execute(w, &tdata{
ExporterName: exporter.Config().Globals.ExporterName,
MetricsPath: metricsPath,
DocsUrl: docsUrl,
Version: vinfos,
})
}
}
}

Expand Down
7 changes: 4 additions & 3 deletions exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"context"
"errors"
"fmt"
"sync"
"time"
Expand All @@ -16,7 +17,7 @@ import (
)

var (
ErrTargetNotFound = fmt.Errorf("target not found")
ErrTargetNotFound = errors.New("target not found")
)

// Exporter is a prometheus.Gatherer that gathers SQL metrics from targets and merges them with the default registry.
Expand Down Expand Up @@ -120,7 +121,7 @@ func (e *exporter) Gather() ([]*dto.MetricFamily, error) {
if r := recover(); r != nil {
err, ok := r.(error)
if !ok {
err = fmt.Errorf("undefined error")
err = errors.New("undefined error")
}
level.Debug(e.logger).Log(
"collid", target.Name(),
Expand Down Expand Up @@ -231,7 +232,7 @@ func (e *exporter) AddTarget(tg_config *TargetConfig) (Target, error) {
func (e *exporter) GetFirstTarget() (Target, error) {
var t_found Target
if len(e.targets) == 0 {
return t_found, fmt.Errorf("no target found")
return t_found, errors.New("no target found")
} else {
t_found = e.targets[0]
}
Expand Down
7 changes: 4 additions & 3 deletions field.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"encoding/json"
"errors"
"fmt"
"html"

Expand Down Expand Up @@ -121,7 +122,7 @@ func (f *Field) GetValueString(
ok := false
if r := recover(); r != nil {
if err, ok = r.(error); !ok {
err = fmt.Errorf("panic in GetValueString template with undefined error")
err = errors.New("panic in GetValueString template with undefined error")
}
res = ""
}
Expand Down Expand Up @@ -181,7 +182,7 @@ func (f *Field) GetValueFloat(
ok := false
if r := recover(); r != nil {
if err, ok = r.(error); !ok {
err = fmt.Errorf("panic in GetValueFloat template with undefined error")
err = errors.New("panic in GetValueFloat template with undefined error")
}
res = 0
}
Expand Down Expand Up @@ -262,7 +263,7 @@ func (f *Field) GetValueObject(
ok := false
if r := recover(); r != nil {
if err, ok = r.(error); !ok {
err = fmt.Errorf("panic in GetValueObject template with undefined error")
err = errors.New("panic in GetValueObject template with undefined error")
}
res = res_slice
}
Expand Down
51 changes: 42 additions & 9 deletions httpapi_exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"context"
"errors"
"fmt"
"net/http"
"os"
Expand Down Expand Up @@ -39,6 +40,7 @@ var (
dry_run = kingpin.Flag("dry-run", "Only check exporter configuration file and exit.").Short('n').Default("false").Bool()
// alsologtostderr = kingpin.Flag("alsologtostderr", "log to standard error as well as files.").Default("true").Bool()
target_name = kingpin.Flag("target", "In dry-run mode specify the target name, else ignored.").Short('t').String()
model_name = kingpin.Flag("model", "In dry-run mode specify the model name to build the dynamic target, else ignored.").Default("default").Short('m').String()
auth_key = kingpin.Flag("auth.key", "In dry-run mode specify the auth_key to use, else ignored.").Short('a').String()
collector_name = kingpin.Flag("collector", "Specify the collector name restriction to collect, replace the collector_names set for each target.").Short('o').String()
toolkitFlags = kingpinflag.AddFlags(kingpin.CommandLine, metricsPublishingPort)
Expand Down Expand Up @@ -77,7 +79,7 @@ func BuildHandler(exporter Exporter, actionCh chan<- actionMsg) http.Handler {
var routes = []*route{
newRoute(OpEgals, "/", HomeHandlerFunc(*metricsPath, exporter)),
newRoute(OpEgals, "/config", ConfigHandlerFunc(*metricsPath, exporter)),
newRoute(OpEgals, "/healthz", func(w http.ResponseWriter, r *http.Request) { http.Error(w, "OK", http.StatusOK) }),
newRoute(OpEgals, "/health", HealthHandlerfunc(*metricsPath, exporter)),
newRoute(OpEgals, "/httpapi_exporter_metrics", func(w http.ResponseWriter, r *http.Request) { promhttp.Handler().ServeHTTP(w, r) }),
newRoute(OpEgals, "/reload", ReloadHandlerFunc(*metricsPath, exporter, actionCh)),
newRoute(OpMatch, "/loglevel(?:/(.*))?", LogLevelHandlerFunc(*metricsPath, exporter, actionCh, "")),
Expand Down Expand Up @@ -115,7 +117,7 @@ func BuildHandler(exporter Exporter, actionCh chan<- actionMsg) http.Handler {
return
}
}
err := fmt.Errorf("not found")
err := errors.New("not found")
HandleError(http.StatusNotFound, err, *metricsPath, exporter, w, req)
})
}
Expand All @@ -136,7 +138,7 @@ func ReloadHandlerFunc(metricsPath string, exporter Exporter, reloadCh chan<- ac
return func(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
level.Info(exporter.Logger()).Log("msg", "received invalid method on /reload", "client", r.RemoteAddr)
HandleError(http.StatusMethodNotAllowed, fmt.Errorf("this endpoint requires a POST request"), metricsPath, exporter, w, r)
HandleError(http.StatusMethodNotAllowed, errors.New("this endpoint requires a POST request"), metricsPath, exporter, w, r)
return
}
level.Info(exporter.Logger()).Log("msg", "received /reload from %s", "client", r.RemoteAddr)
Expand Down Expand Up @@ -165,7 +167,7 @@ func LogLevelHandlerFunc(metricsPath string, exporter Exporter, reloadCh chan<-
case "POST":
ctxval, ok := r.Context().Value(ctxKey{}).(*ctxValue)
if !ok {
err := fmt.Errorf("invalid context received")
err := errors.New("invalid context received")
HandleError(http.StatusInternalServerError, err, metricsPath, exporter, w, r)
return

Expand All @@ -179,11 +181,11 @@ func LogLevelHandlerFunc(metricsPath string, exporter Exporter, reloadCh chan<-
if err := <-msg.retCh; err != nil {
http.Error(w, fmt.Sprintf("OK loglevel set to %s", err), http.StatusOK)
} else {
HandleError(http.StatusInternalServerError, fmt.Errorf("KO something wrong with increase loglevel"), metricsPath, exporter, w, r)
HandleError(http.StatusInternalServerError, errors.New("KO something wrong with increase loglevel"), metricsPath, exporter, w, r)
}
default:
level.Info(exporter.Logger()).Log("msg", "received invalid method on /loglevel", "client", r.RemoteAddr)
HandleError(http.StatusMethodNotAllowed, fmt.Errorf("this endpoint requires a GET or POST request"), metricsPath, exporter, w, r)
HandleError(http.StatusMethodNotAllowed, errors.New("this endpoint requires a GET or POST request"), metricsPath, exporter, w, r)
return
}
}
Expand All @@ -210,10 +212,41 @@ func main() {
if *dry_run {
level.Info(logger).Log("msg", "configuration OK.")
// get the target if defined
var t Target
var err error
var (
err error
t Target
tmp_t *TargetConfig
)
if *target_name != "" {
*target_name = strings.TrimSpace(*target_name)
t, err = exporter.FindTarget(*target_name)
if err == ErrTargetNotFound {
err = nil
if *model_name != "" {
*model_name = strings.TrimSpace(*model_name)
t_def, err := exporter.FindTarget(*model_name)
if err != nil {
err := fmt.Errorf("Target model '%s' not found: %s", *model_name, err)
level.Error(logger).Log("errmsg", err)
os.Exit(1)
}
if tmp_t, err = t_def.Config().Clone(*target_name, ""); err != nil {
err := fmt.Errorf("invalid url set for remote_target '%s' %s", *target_name, err)
level.Error(logger).Log("errmsg", err)
os.Exit(1)
}
t, err = exporter.AddTarget(tmp_t)
if err != nil {
err := fmt.Errorf("unable to create temporary target %s", err)
level.Error(logger).Log("errmsg", err)
os.Exit(1)
}
exporter.Config().Targets = append(exporter.Config().Targets, tmp_t)
}
}
if err == ErrTargetNotFound {
t, err = exporter.GetFirstTarget()
}
} else {
t, err = exporter.GetFirstTarget()
}
Expand Down Expand Up @@ -310,7 +343,7 @@ func main() {
level.Info(logger).Log("msg", "set loglevel received.")
}
exporter.IncreaseLogLevel(action.logLevel)
action.retCh <- fmt.Errorf(exporter.GetLogLevel())
action.retCh <- errors.New(exporter.GetLogLevel())
}
}
}
Expand Down
9 changes: 8 additions & 1 deletion promhttp.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"compress/gzip"
"context"
"errors"
"fmt"
"io"
"net/http"
Expand All @@ -22,6 +23,10 @@ const (
contentLengthHeader = "Content-Length"
contentEncodingHeader = "Content-Encoding"
acceptEncodingHeader = "Accept-Encoding"
acceptHeader = "Accept"
applicationJSON = "application/json"
textHTML = "text/html"
textPLAIN = "text/plain"
)

// ExporterHandlerFor returns an http.Handler for the provided Exporter.
Expand All @@ -36,13 +41,15 @@ func ExporterHandlerFor(exporter Exporter) http.Handler {

tname := params.Get("target")
if tname == "" {
err := fmt.Errorf("Target parameter is missing")
err := errors.New("Target parameter is missing")
HandleError(http.StatusBadRequest, err, *metricsPath, exporter, w, req)
return
}
tname = strings.TrimSpace(tname)
target, err = exporter.FindTarget(tname)
if err == ErrTargetNotFound {
model := params.Get("model")
model = strings.TrimSpace(model)
if model == "" {
model = "default"
}
Expand Down
Loading

0 comments on commit 4f2623d

Please sign in to comment.