mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-23 20:37:12 +01:00
vmalert: print example of curl
command for rule's state (#3112)
The change adds an example of `curl` command to the Rule's page. The command is generated for each recorded state. It is supposed user can just copy&execute the command to see what was returned to vmalert. Signed-off-by: hagen1778 <roman@victoriametrics.com>
This commit is contained in:
parent
455002922e
commit
9c95c81534
@ -264,13 +264,14 @@ const resolvedRetention = 15 * time.Minute
|
||||
// Based on the Querier results AlertingRule maintains notifier.Alerts
|
||||
func (ar *AlertingRule) Exec(ctx context.Context, ts time.Time, limit int) ([]prompbmarshal.TimeSeries, error) {
|
||||
start := time.Now()
|
||||
qMetrics, err := ar.q.Query(ctx, ar.Expr, ts)
|
||||
qMetrics, req, err := ar.q.Query(ctx, ar.Expr, ts)
|
||||
curState := ruleStateEntry{
|
||||
time: start,
|
||||
at: ts,
|
||||
duration: time.Since(start),
|
||||
samples: len(qMetrics),
|
||||
err: err,
|
||||
req: req,
|
||||
}
|
||||
|
||||
defer func() {
|
||||
@ -294,7 +295,10 @@ func (ar *AlertingRule) Exec(ctx context.Context, ts time.Time, limit int) ([]pr
|
||||
}
|
||||
}
|
||||
|
||||
qFn := func(query string) ([]datasource.Metric, error) { return ar.q.Query(ctx, query, ts) }
|
||||
qFn := func(query string) ([]datasource.Metric, error) {
|
||||
res, _, err := ar.q.Query(ctx, query, ts)
|
||||
return res, err
|
||||
}
|
||||
updated := make(map[uint64]struct{})
|
||||
// update list of active alerts
|
||||
for _, m := range qMetrics {
|
||||
@ -595,7 +599,10 @@ func (ar *AlertingRule) Restore(ctx context.Context, q datasource.Querier, lookb
|
||||
}
|
||||
|
||||
ts := time.Now()
|
||||
qFn := func(query string) ([]datasource.Metric, error) { return ar.q.Query(ctx, query, ts) }
|
||||
qFn := func(query string) ([]datasource.Metric, error) {
|
||||
res, _, err := ar.q.Query(ctx, query, ts)
|
||||
return res, err
|
||||
}
|
||||
|
||||
// account for external labels in filter
|
||||
var labelsFilter string
|
||||
@ -605,7 +612,7 @@ func (ar *AlertingRule) Restore(ctx context.Context, q datasource.Querier, lookb
|
||||
|
||||
expr := fmt.Sprintf("last_over_time(%s{alertname=%q%s}[%ds])",
|
||||
alertForStateMetricName, ar.Name, labelsFilter, int(lookback.Seconds()))
|
||||
qMetrics, err := q.Query(ctx, expr, ts)
|
||||
qMetrics, _, err := q.Query(ctx, expr, ts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -2,18 +2,27 @@ package datasource
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Querier interface wraps Query and QueryRange methods
|
||||
type Querier interface {
|
||||
Query(ctx context.Context, query string, ts time.Time) ([]Metric, error)
|
||||
// Query executes instant request with the given query at the given ts.
|
||||
// It returns list of Metric in response, the http.Request used for sending query
|
||||
// and error if any. Returned http.Request can't be reused and its body is already read.
|
||||
// Query should stop once ctx is cancelled.
|
||||
Query(ctx context.Context, query string, ts time.Time) ([]Metric, *http.Request, error)
|
||||
// QueryRange executes range request with the given query on the given time range.
|
||||
// It returns list of Metric in response and error if any.
|
||||
// QueryRange should stop once ctx is cancelled.
|
||||
QueryRange(ctx context.Context, query string, from, to time.Time) ([]Metric, error)
|
||||
}
|
||||
|
||||
// QuerierBuilder builds Querier with given params.
|
||||
type QuerierBuilder interface {
|
||||
// BuildWithParams creates a new Querier object with the given params
|
||||
BuildWithParams(params QuerierParams) Querier
|
||||
}
|
||||
|
||||
|
@ -98,10 +98,10 @@ func NewVMStorage(baseURL string, authCfg *promauth.Config, lookBack time.Durati
|
||||
}
|
||||
|
||||
// Query executes the given query and returns parsed response
|
||||
func (s *VMStorage) Query(ctx context.Context, query string, ts time.Time) ([]Metric, error) {
|
||||
func (s *VMStorage) Query(ctx context.Context, query string, ts time.Time) ([]Metric, *http.Request, error) {
|
||||
req, err := s.newRequestPOST()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
switch s.dataSourceType {
|
||||
@ -110,12 +110,12 @@ func (s *VMStorage) Query(ctx context.Context, query string, ts time.Time) ([]Me
|
||||
case datasourceGraphite:
|
||||
s.setGraphiteReqParams(req, query, ts)
|
||||
default:
|
||||
return nil, fmt.Errorf("engine not found: %q", s.dataSourceType)
|
||||
return nil, nil, fmt.Errorf("engine not found: %q", s.dataSourceType)
|
||||
}
|
||||
|
||||
resp, err := s.do(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, req, err
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
@ -125,7 +125,8 @@ func (s *VMStorage) Query(ctx context.Context, query string, ts time.Time) ([]Me
|
||||
if s.dataSourceType != datasourcePrometheus {
|
||||
parseFn = parseGraphiteResponse
|
||||
}
|
||||
return parseFn(req, resp)
|
||||
result, err := parseFn(req, resp)
|
||||
return result, req, err
|
||||
}
|
||||
|
||||
// QueryRange executes the given query on the given time range.
|
||||
|
@ -94,7 +94,7 @@ func TestVMInstantQuery(t *testing.T) {
|
||||
ts := time.Now()
|
||||
|
||||
expErr := func(err string) {
|
||||
if _, err := pq.Query(ctx, query, ts); err == nil {
|
||||
if _, _, err := pq.Query(ctx, query, ts); err == nil {
|
||||
t.Fatalf("expected %q got nil", err)
|
||||
}
|
||||
}
|
||||
@ -106,7 +106,7 @@ func TestVMInstantQuery(t *testing.T) {
|
||||
expErr("unknown status") // 4
|
||||
expErr("non-vector resultType error") // 5
|
||||
|
||||
m, err := pq.Query(ctx, query, ts) // 6 - vector
|
||||
m, _, err := pq.Query(ctx, query, ts) // 6 - vector
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected %s", err)
|
||||
}
|
||||
@ -129,10 +129,13 @@ func TestVMInstantQuery(t *testing.T) {
|
||||
t.Fatalf("unexpected metric %+v want %+v", m, expected)
|
||||
}
|
||||
|
||||
m, err = pq.Query(ctx, query, ts) // 7 - scalar
|
||||
m, req, err := pq.Query(ctx, query, ts) // 7 - scalar
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected %s", err)
|
||||
}
|
||||
if req == nil {
|
||||
t.Fatalf("expected request to be non-nil")
|
||||
}
|
||||
if len(m) != 1 {
|
||||
t.Fatalf("expected 1 metrics got %d in %+v", len(m), m)
|
||||
}
|
||||
@ -148,7 +151,7 @@ func TestVMInstantQuery(t *testing.T) {
|
||||
|
||||
gq := s.BuildWithParams(QuerierParams{DataSourceType: string(datasourceGraphite)})
|
||||
|
||||
m, err = gq.Query(ctx, queryRender, ts) // 8 - graphite
|
||||
m, _, err = gq.Query(ctx, queryRender, ts) // 8 - graphite
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected %s", err)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync"
|
||||
@ -44,18 +45,20 @@ func (fq *fakeQuerier) BuildWithParams(_ datasource.QuerierParams) datasource.Qu
|
||||
}
|
||||
|
||||
func (fq *fakeQuerier) QueryRange(ctx context.Context, q string, _, _ time.Time) ([]datasource.Metric, error) {
|
||||
return fq.Query(ctx, q, time.Now())
|
||||
req, _, err := fq.Query(ctx, q, time.Now())
|
||||
return req, err
|
||||
}
|
||||
|
||||
func (fq *fakeQuerier) Query(_ context.Context, _ string, _ time.Time) ([]datasource.Metric, error) {
|
||||
func (fq *fakeQuerier) Query(_ context.Context, _ string, _ time.Time) ([]datasource.Metric, *http.Request, error) {
|
||||
fq.Lock()
|
||||
defer fq.Unlock()
|
||||
if fq.err != nil {
|
||||
return nil, fq.err
|
||||
return nil, nil, fq.err
|
||||
}
|
||||
cp := make([]datasource.Metric, len(fq.metrics))
|
||||
copy(cp, fq.metrics)
|
||||
return cp, nil
|
||||
req, _ := http.NewRequest(http.MethodPost, "foo.com", nil)
|
||||
return cp, req, nil
|
||||
}
|
||||
|
||||
type fakeNotifier struct {
|
||||
|
@ -115,12 +115,13 @@ func (rr *RecordingRule) ExecRange(ctx context.Context, start, end time.Time) ([
|
||||
// Exec executes RecordingRule expression via the given Querier.
|
||||
func (rr *RecordingRule) Exec(ctx context.Context, ts time.Time, limit int) ([]prompbmarshal.TimeSeries, error) {
|
||||
start := time.Now()
|
||||
qMetrics, err := rr.q.Query(ctx, rr.Expr, ts)
|
||||
qMetrics, req, err := rr.q.Query(ctx, rr.Expr, ts)
|
||||
curState := ruleStateEntry{
|
||||
time: start,
|
||||
at: ts,
|
||||
duration: time.Since(start),
|
||||
samples: len(qMetrics),
|
||||
req: req,
|
||||
}
|
||||
|
||||
defer func() {
|
||||
|
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -53,6 +54,8 @@ type ruleStateEntry struct {
|
||||
// stores the number of samples returned during
|
||||
// the last evaluation
|
||||
samples int
|
||||
// stores the HTTP request used by datasource during rule.Exec
|
||||
req *http.Request
|
||||
}
|
||||
|
||||
const defaultStateEntriesLimit = 20
|
||||
|
@ -59,6 +59,15 @@
|
||||
background-color: rgba(0,0,0,.5);
|
||||
-webkit-box-shadow: 0 0 1px rgba(255,255,255,.5);
|
||||
}
|
||||
textarea.curl-area{
|
||||
width: 100%;
|
||||
line-height: 1;
|
||||
font-size: 12px;
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: scroll;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -101,143 +101,152 @@ func StreamHeader(qw422016 *qt422016.Writer, r *http.Request, navItems []NavItem
|
||||
background-color: rgba(0,0,0,.5);
|
||||
-webkit-box-shadow: 0 0 1px rgba(255,255,255,.5);
|
||||
}
|
||||
textarea.curl-area{
|
||||
width: 100%;
|
||||
line-height: 1;
|
||||
font-size: 12px;
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: scroll;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
`)
|
||||
//line app/vmalert/tpl/header.qtpl:65
|
||||
//line app/vmalert/tpl/header.qtpl:74
|
||||
streamprintNavItems(qw422016, r, title, navItems)
|
||||
//line app/vmalert/tpl/header.qtpl:65
|
||||
//line app/vmalert/tpl/header.qtpl:74
|
||||
qw422016.N().S(`
|
||||
<main class="px-2">
|
||||
`)
|
||||
//line app/vmalert/tpl/header.qtpl:67
|
||||
//line app/vmalert/tpl/header.qtpl:76
|
||||
}
|
||||
|
||||
//line app/vmalert/tpl/header.qtpl:67
|
||||
//line app/vmalert/tpl/header.qtpl:76
|
||||
func WriteHeader(qq422016 qtio422016.Writer, r *http.Request, navItems []NavItem, title string) {
|
||||
//line app/vmalert/tpl/header.qtpl:67
|
||||
//line app/vmalert/tpl/header.qtpl:76
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line app/vmalert/tpl/header.qtpl:67
|
||||
//line app/vmalert/tpl/header.qtpl:76
|
||||
StreamHeader(qw422016, r, navItems, title)
|
||||
//line app/vmalert/tpl/header.qtpl:67
|
||||
//line app/vmalert/tpl/header.qtpl:76
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line app/vmalert/tpl/header.qtpl:67
|
||||
//line app/vmalert/tpl/header.qtpl:76
|
||||
}
|
||||
|
||||
//line app/vmalert/tpl/header.qtpl:67
|
||||
//line app/vmalert/tpl/header.qtpl:76
|
||||
func Header(r *http.Request, navItems []NavItem, title string) string {
|
||||
//line app/vmalert/tpl/header.qtpl:67
|
||||
//line app/vmalert/tpl/header.qtpl:76
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line app/vmalert/tpl/header.qtpl:67
|
||||
//line app/vmalert/tpl/header.qtpl:76
|
||||
WriteHeader(qb422016, r, navItems, title)
|
||||
//line app/vmalert/tpl/header.qtpl:67
|
||||
//line app/vmalert/tpl/header.qtpl:76
|
||||
qs422016 := string(qb422016.B)
|
||||
//line app/vmalert/tpl/header.qtpl:67
|
||||
//line app/vmalert/tpl/header.qtpl:76
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line app/vmalert/tpl/header.qtpl:67
|
||||
//line app/vmalert/tpl/header.qtpl:76
|
||||
return qs422016
|
||||
//line app/vmalert/tpl/header.qtpl:67
|
||||
//line app/vmalert/tpl/header.qtpl:76
|
||||
}
|
||||
|
||||
//line app/vmalert/tpl/header.qtpl:71
|
||||
//line app/vmalert/tpl/header.qtpl:80
|
||||
type NavItem struct {
|
||||
Name string
|
||||
Url string
|
||||
}
|
||||
|
||||
//line app/vmalert/tpl/header.qtpl:77
|
||||
//line app/vmalert/tpl/header.qtpl:86
|
||||
func streamprintNavItems(qw422016 *qt422016.Writer, r *http.Request, current string, items []NavItem) {
|
||||
//line app/vmalert/tpl/header.qtpl:77
|
||||
//line app/vmalert/tpl/header.qtpl:86
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line app/vmalert/tpl/header.qtpl:79
|
||||
//line app/vmalert/tpl/header.qtpl:88
|
||||
prefix := "/vmalert/"
|
||||
if strings.HasPrefix(r.URL.Path, prefix) {
|
||||
prefix = ""
|
||||
}
|
||||
|
||||
//line app/vmalert/tpl/header.qtpl:83
|
||||
//line app/vmalert/tpl/header.qtpl:92
|
||||
qw422016.N().S(`
|
||||
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
|
||||
<div class="container-fluid">
|
||||
<div class="collapse navbar-collapse" id="navbarCollapse">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-md-0">
|
||||
`)
|
||||
//line app/vmalert/tpl/header.qtpl:88
|
||||
//line app/vmalert/tpl/header.qtpl:97
|
||||
for _, item := range items {
|
||||
//line app/vmalert/tpl/header.qtpl:88
|
||||
//line app/vmalert/tpl/header.qtpl:97
|
||||
qw422016.N().S(`
|
||||
<li class="nav-item">
|
||||
`)
|
||||
//line app/vmalert/tpl/header.qtpl:91
|
||||
//line app/vmalert/tpl/header.qtpl:100
|
||||
u, _ := url.Parse(item.Url)
|
||||
|
||||
//line app/vmalert/tpl/header.qtpl:92
|
||||
//line app/vmalert/tpl/header.qtpl:101
|
||||
qw422016.N().S(`
|
||||
<a class="nav-link`)
|
||||
//line app/vmalert/tpl/header.qtpl:93
|
||||
//line app/vmalert/tpl/header.qtpl:102
|
||||
if current == item.Name {
|
||||
//line app/vmalert/tpl/header.qtpl:93
|
||||
//line app/vmalert/tpl/header.qtpl:102
|
||||
qw422016.N().S(` active`)
|
||||
//line app/vmalert/tpl/header.qtpl:93
|
||||
//line app/vmalert/tpl/header.qtpl:102
|
||||
}
|
||||
//line app/vmalert/tpl/header.qtpl:93
|
||||
//line app/vmalert/tpl/header.qtpl:102
|
||||
qw422016.N().S(`"
|
||||
href="`)
|
||||
//line app/vmalert/tpl/header.qtpl:94
|
||||
//line app/vmalert/tpl/header.qtpl:103
|
||||
if u.IsAbs() {
|
||||
//line app/vmalert/tpl/header.qtpl:94
|
||||
//line app/vmalert/tpl/header.qtpl:103
|
||||
qw422016.E().S(item.Url)
|
||||
//line app/vmalert/tpl/header.qtpl:94
|
||||
//line app/vmalert/tpl/header.qtpl:103
|
||||
} else {
|
||||
//line app/vmalert/tpl/header.qtpl:94
|
||||
//line app/vmalert/tpl/header.qtpl:103
|
||||
qw422016.E().S(path.Join(prefix, item.Url))
|
||||
//line app/vmalert/tpl/header.qtpl:94
|
||||
//line app/vmalert/tpl/header.qtpl:103
|
||||
}
|
||||
//line app/vmalert/tpl/header.qtpl:94
|
||||
//line app/vmalert/tpl/header.qtpl:103
|
||||
qw422016.N().S(`">
|
||||
`)
|
||||
//line app/vmalert/tpl/header.qtpl:95
|
||||
//line app/vmalert/tpl/header.qtpl:104
|
||||
qw422016.E().S(item.Name)
|
||||
//line app/vmalert/tpl/header.qtpl:95
|
||||
//line app/vmalert/tpl/header.qtpl:104
|
||||
qw422016.N().S(`
|
||||
</a>
|
||||
</li>
|
||||
`)
|
||||
//line app/vmalert/tpl/header.qtpl:98
|
||||
//line app/vmalert/tpl/header.qtpl:107
|
||||
}
|
||||
//line app/vmalert/tpl/header.qtpl:98
|
||||
//line app/vmalert/tpl/header.qtpl:107
|
||||
qw422016.N().S(`
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
`)
|
||||
//line app/vmalert/tpl/header.qtpl:102
|
||||
//line app/vmalert/tpl/header.qtpl:111
|
||||
}
|
||||
|
||||
//line app/vmalert/tpl/header.qtpl:102
|
||||
//line app/vmalert/tpl/header.qtpl:111
|
||||
func writeprintNavItems(qq422016 qtio422016.Writer, r *http.Request, current string, items []NavItem) {
|
||||
//line app/vmalert/tpl/header.qtpl:102
|
||||
//line app/vmalert/tpl/header.qtpl:111
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line app/vmalert/tpl/header.qtpl:102
|
||||
//line app/vmalert/tpl/header.qtpl:111
|
||||
streamprintNavItems(qw422016, r, current, items)
|
||||
//line app/vmalert/tpl/header.qtpl:102
|
||||
//line app/vmalert/tpl/header.qtpl:111
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line app/vmalert/tpl/header.qtpl:102
|
||||
//line app/vmalert/tpl/header.qtpl:111
|
||||
}
|
||||
|
||||
//line app/vmalert/tpl/header.qtpl:102
|
||||
//line app/vmalert/tpl/header.qtpl:111
|
||||
func printNavItems(r *http.Request, current string, items []NavItem) string {
|
||||
//line app/vmalert/tpl/header.qtpl:102
|
||||
//line app/vmalert/tpl/header.qtpl:111
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line app/vmalert/tpl/header.qtpl:102
|
||||
//line app/vmalert/tpl/header.qtpl:111
|
||||
writeprintNavItems(qb422016, r, current, items)
|
||||
//line app/vmalert/tpl/header.qtpl:102
|
||||
//line app/vmalert/tpl/header.qtpl:111
|
||||
qs422016 := string(qb422016.B)
|
||||
//line app/vmalert/tpl/header.qtpl:102
|
||||
//line app/vmalert/tpl/header.qtpl:111
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line app/vmalert/tpl/header.qtpl:102
|
||||
//line app/vmalert/tpl/header.qtpl:111
|
||||
return qs422016
|
||||
//line app/vmalert/tpl/header.qtpl:102
|
||||
//line app/vmalert/tpl/header.qtpl:111
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
|
||||
@ -47,3 +50,62 @@ func newTimeSeriesPB(values []float64, timestamps []int64, labels []prompbmarsha
|
||||
ts.Labels = labels
|
||||
return ts
|
||||
}
|
||||
|
||||
type curlWriter struct {
|
||||
b strings.Builder
|
||||
}
|
||||
|
||||
func (cw *curlWriter) string() string {
|
||||
res := "curl " + cw.b.String()
|
||||
cw.b.Reset()
|
||||
return strings.TrimSpace(res)
|
||||
}
|
||||
|
||||
func (cw *curlWriter) addWithEsc(str string) {
|
||||
escStr := `'` + strings.Replace(str, `'`, `'\''`, -1) + `'`
|
||||
cw.add(escStr)
|
||||
}
|
||||
|
||||
func (cw *curlWriter) add(str string) {
|
||||
cw.b.WriteString(str)
|
||||
cw.b.WriteString(" ")
|
||||
}
|
||||
|
||||
func requestToCurl(req *http.Request) string {
|
||||
if req.URL == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
cw := &curlWriter{}
|
||||
|
||||
schema := req.URL.Scheme
|
||||
requestURL := req.URL.String()
|
||||
if schema == "" {
|
||||
schema = "http"
|
||||
if req.TLS != nil {
|
||||
schema = "https"
|
||||
}
|
||||
requestURL = schema + "://" + req.Host + requestURL
|
||||
}
|
||||
|
||||
if schema == "https" {
|
||||
cw.add("-k")
|
||||
}
|
||||
|
||||
cw.add("-X")
|
||||
cw.add(req.Method)
|
||||
|
||||
var keys []string
|
||||
for k := range req.Header {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, k := range keys {
|
||||
cw.add("-H")
|
||||
cw.addWithEsc(fmt.Sprintf("%s: %s", k, strings.Join(req.Header[k], " ")))
|
||||
}
|
||||
|
||||
cw.addWithEsc(requestURL)
|
||||
return cw.string()
|
||||
}
|
||||
|
47
app/vmalert/utils_test.go
Normal file
47
app/vmalert/utils_test.go
Normal file
@ -0,0 +1,47 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRequestToCurl(t *testing.T) {
|
||||
f := func(req *http.Request, exp string) {
|
||||
got := requestToCurl(req)
|
||||
if got != exp {
|
||||
t.Fatalf("expected to have %q; got %q instead", exp, got)
|
||||
}
|
||||
}
|
||||
|
||||
req, _ := http.NewRequest(http.MethodPost, "foo.com", nil)
|
||||
f(req, "curl -X POST 'http://foo.com'")
|
||||
|
||||
req, _ = http.NewRequest(http.MethodGet, "https://foo.com", nil)
|
||||
f(req, "curl -k -X GET 'https://foo.com'")
|
||||
|
||||
req, _ = http.NewRequest(http.MethodPost, "foo.com", nil)
|
||||
req.Header.Set("foo", "bar")
|
||||
req.Header.Set("baz", "qux")
|
||||
f(req, "curl -X POST -H 'Baz: qux' -H 'Foo: bar' 'http://foo.com'")
|
||||
|
||||
req, _ = http.NewRequest(http.MethodPost, "foo.com", nil)
|
||||
params := req.URL.Query()
|
||||
params.Add("query", "up")
|
||||
params.Add("step", "10")
|
||||
req.URL.RawQuery = params.Encode()
|
||||
f(req, "curl -X POST 'http://foo.com?query=up&step=10'")
|
||||
|
||||
req, _ = http.NewRequest(http.MethodPost, "http://foo.com", nil)
|
||||
params = req.URL.Query()
|
||||
params.Add("query", "up")
|
||||
params.Add("step", "10")
|
||||
req.URL.RawQuery = params.Encode()
|
||||
f(req, "curl -X POST 'http://foo.com?query=up&step=10'")
|
||||
|
||||
req, _ = http.NewRequest(http.MethodPost, "https://foo.com", nil)
|
||||
params = req.URL.Query()
|
||||
params.Add("query", "up")
|
||||
params.Add("step", "10")
|
||||
req.URL.RawQuery = params.Encode()
|
||||
f(req, "curl -k -X POST 'https://foo.com?query=up&step=10'")
|
||||
}
|
@ -435,10 +435,11 @@
|
||||
<table class="table table-striped table-hover table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" style="width: 20%" title="The time when event was created">Updated at</th>
|
||||
<th scope="col" style="width: 20%" class="text-center" title="How many samples were returned">Samples</th>
|
||||
<th scope="col" style="width: 20%" class="text-center" title="How many seconds request took">Duration</th>
|
||||
<th scope="col" style="width: 20%" class="text-center" title="Time used for rule execution">Executed at</th>
|
||||
<th scope="col" title="The time when event was created">Updated at</th>
|
||||
<th scope="col" style="width: 10%" class="text-center" title="How many samples were returned">Samples</th>
|
||||
<th scope="col" style="width: 10%" class="text-center" title="How many seconds request took">Duration</th>
|
||||
<th scope="col" class="text-center" title="Time used for rule execution">Executed at</th>
|
||||
<th scope="col" class="text-center" title="cURL command with request example">cURL</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -448,14 +449,17 @@
|
||||
<td>
|
||||
<span class="badge bg-primary rounded-pill me-3" title="Updated at">{%s u.time.Format(time.RFC3339) %}</span>
|
||||
</td>
|
||||
<td class="text-center">{%d u.samples %}</td>
|
||||
<td class="text-center" wi>{%d u.samples %}</td>
|
||||
<td class="text-center">{%f.3 u.duration.Seconds() %}s</td>
|
||||
<td class="text-center">{%s u.at.Format(time.RFC3339) %}</td>
|
||||
<td>
|
||||
<textarea class="curl-area" rows="1" onclick="this.focus();this.select()">{%s requestToCurl(u.req) %}</textarea>
|
||||
</td>
|
||||
</tr>
|
||||
</li>
|
||||
{% if u.err != nil %}
|
||||
<tr{% if u.err != nil %} class="alert-danger"{% endif %}>
|
||||
<td colspan="4">
|
||||
<td colspan="5">
|
||||
<span class="alert-danger">{%v u.err %}</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -1290,203 +1290,211 @@ func StreamRuleDetails(qw422016 *qt422016.Writer, r *http.Request, rule APIRule)
|
||||
<table class="table table-striped table-hover table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" style="width: 20%" title="The time when event was created">Updated at</th>
|
||||
<th scope="col" style="width: 20%" class="text-center" title="How many samples were returned">Samples</th>
|
||||
<th scope="col" style="width: 20%" class="text-center" title="How many seconds request took">Duration</th>
|
||||
<th scope="col" style="width: 20%" class="text-center" title="Time used for rule execution">Executed at</th>
|
||||
<th scope="col" title="The time when event was created">Updated at</th>
|
||||
<th scope="col" style="width: 10%" class="text-center" title="How many samples were returned">Samples</th>
|
||||
<th scope="col" style="width: 10%" class="text-center" title="How many seconds request took">Duration</th>
|
||||
<th scope="col" class="text-center" title="Time used for rule execution">Executed at</th>
|
||||
<th scope="col" class="text-center" title="cURL command with request example">cURL</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:446
|
||||
//line app/vmalert/web.qtpl:447
|
||||
for _, u := range rule.Updates {
|
||||
//line app/vmalert/web.qtpl:446
|
||||
//line app/vmalert/web.qtpl:447
|
||||
qw422016.N().S(`
|
||||
<tr`)
|
||||
//line app/vmalert/web.qtpl:447
|
||||
//line app/vmalert/web.qtpl:448
|
||||
if u.err != nil {
|
||||
//line app/vmalert/web.qtpl:447
|
||||
//line app/vmalert/web.qtpl:448
|
||||
qw422016.N().S(` class="alert-danger"`)
|
||||
//line app/vmalert/web.qtpl:447
|
||||
//line app/vmalert/web.qtpl:448
|
||||
}
|
||||
//line app/vmalert/web.qtpl:447
|
||||
//line app/vmalert/web.qtpl:448
|
||||
qw422016.N().S(`>
|
||||
<td>
|
||||
<span class="badge bg-primary rounded-pill me-3" title="Updated at">`)
|
||||
//line app/vmalert/web.qtpl:449
|
||||
//line app/vmalert/web.qtpl:450
|
||||
qw422016.E().S(u.time.Format(time.RFC3339))
|
||||
//line app/vmalert/web.qtpl:449
|
||||
//line app/vmalert/web.qtpl:450
|
||||
qw422016.N().S(`</span>
|
||||
</td>
|
||||
<td class="text-center">`)
|
||||
//line app/vmalert/web.qtpl:451
|
||||
<td class="text-center" wi>`)
|
||||
//line app/vmalert/web.qtpl:452
|
||||
qw422016.N().D(u.samples)
|
||||
//line app/vmalert/web.qtpl:451
|
||||
//line app/vmalert/web.qtpl:452
|
||||
qw422016.N().S(`</td>
|
||||
<td class="text-center">`)
|
||||
//line app/vmalert/web.qtpl:452
|
||||
//line app/vmalert/web.qtpl:453
|
||||
qw422016.N().FPrec(u.duration.Seconds(), 3)
|
||||
//line app/vmalert/web.qtpl:452
|
||||
//line app/vmalert/web.qtpl:453
|
||||
qw422016.N().S(`s</td>
|
||||
<td class="text-center">`)
|
||||
//line app/vmalert/web.qtpl:453
|
||||
//line app/vmalert/web.qtpl:454
|
||||
qw422016.E().S(u.at.Format(time.RFC3339))
|
||||
//line app/vmalert/web.qtpl:453
|
||||
//line app/vmalert/web.qtpl:454
|
||||
qw422016.N().S(`</td>
|
||||
<td>
|
||||
<textarea class="curl-area" rows="1" onclick="this.focus();this.select()">`)
|
||||
//line app/vmalert/web.qtpl:456
|
||||
qw422016.E().S(requestToCurl(u.req))
|
||||
//line app/vmalert/web.qtpl:456
|
||||
qw422016.N().S(`</textarea>
|
||||
</td>
|
||||
</tr>
|
||||
</li>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:456
|
||||
//line app/vmalert/web.qtpl:460
|
||||
if u.err != nil {
|
||||
//line app/vmalert/web.qtpl:456
|
||||
//line app/vmalert/web.qtpl:460
|
||||
qw422016.N().S(`
|
||||
<tr`)
|
||||
//line app/vmalert/web.qtpl:457
|
||||
//line app/vmalert/web.qtpl:461
|
||||
if u.err != nil {
|
||||
//line app/vmalert/web.qtpl:457
|
||||
//line app/vmalert/web.qtpl:461
|
||||
qw422016.N().S(` class="alert-danger"`)
|
||||
//line app/vmalert/web.qtpl:457
|
||||
//line app/vmalert/web.qtpl:461
|
||||
}
|
||||
//line app/vmalert/web.qtpl:457
|
||||
//line app/vmalert/web.qtpl:461
|
||||
qw422016.N().S(`>
|
||||
<td colspan="4">
|
||||
<span class="alert-danger">`)
|
||||
//line app/vmalert/web.qtpl:459
|
||||
//line app/vmalert/web.qtpl:463
|
||||
qw422016.E().V(u.err)
|
||||
//line app/vmalert/web.qtpl:459
|
||||
//line app/vmalert/web.qtpl:463
|
||||
qw422016.N().S(`</span>
|
||||
</td>
|
||||
</tr>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:462
|
||||
//line app/vmalert/web.qtpl:466
|
||||
}
|
||||
//line app/vmalert/web.qtpl:462
|
||||
//line app/vmalert/web.qtpl:466
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:463
|
||||
//line app/vmalert/web.qtpl:467
|
||||
}
|
||||
//line app/vmalert/web.qtpl:463
|
||||
//line app/vmalert/web.qtpl:467
|
||||
qw422016.N().S(`
|
||||
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:465
|
||||
//line app/vmalert/web.qtpl:469
|
||||
tpl.StreamFooter(qw422016, r)
|
||||
//line app/vmalert/web.qtpl:465
|
||||
//line app/vmalert/web.qtpl:469
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:466
|
||||
//line app/vmalert/web.qtpl:470
|
||||
}
|
||||
|
||||
//line app/vmalert/web.qtpl:466
|
||||
//line app/vmalert/web.qtpl:470
|
||||
func WriteRuleDetails(qq422016 qtio422016.Writer, r *http.Request, rule APIRule) {
|
||||
//line app/vmalert/web.qtpl:466
|
||||
//line app/vmalert/web.qtpl:470
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line app/vmalert/web.qtpl:466
|
||||
//line app/vmalert/web.qtpl:470
|
||||
StreamRuleDetails(qw422016, r, rule)
|
||||
//line app/vmalert/web.qtpl:466
|
||||
//line app/vmalert/web.qtpl:470
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line app/vmalert/web.qtpl:466
|
||||
//line app/vmalert/web.qtpl:470
|
||||
}
|
||||
|
||||
//line app/vmalert/web.qtpl:466
|
||||
//line app/vmalert/web.qtpl:470
|
||||
func RuleDetails(r *http.Request, rule APIRule) string {
|
||||
//line app/vmalert/web.qtpl:466
|
||||
//line app/vmalert/web.qtpl:470
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line app/vmalert/web.qtpl:466
|
||||
//line app/vmalert/web.qtpl:470
|
||||
WriteRuleDetails(qb422016, r, rule)
|
||||
//line app/vmalert/web.qtpl:466
|
||||
//line app/vmalert/web.qtpl:470
|
||||
qs422016 := string(qb422016.B)
|
||||
//line app/vmalert/web.qtpl:466
|
||||
//line app/vmalert/web.qtpl:470
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line app/vmalert/web.qtpl:466
|
||||
//line app/vmalert/web.qtpl:470
|
||||
return qs422016
|
||||
//line app/vmalert/web.qtpl:466
|
||||
//line app/vmalert/web.qtpl:470
|
||||
}
|
||||
|
||||
//line app/vmalert/web.qtpl:470
|
||||
//line app/vmalert/web.qtpl:474
|
||||
func streambadgeState(qw422016 *qt422016.Writer, state string) {
|
||||
//line app/vmalert/web.qtpl:470
|
||||
//line app/vmalert/web.qtpl:474
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:472
|
||||
//line app/vmalert/web.qtpl:476
|
||||
badgeClass := "bg-warning text-dark"
|
||||
if state == "firing" {
|
||||
badgeClass = "bg-danger"
|
||||
}
|
||||
|
||||
//line app/vmalert/web.qtpl:476
|
||||
//line app/vmalert/web.qtpl:480
|
||||
qw422016.N().S(`
|
||||
<span class="badge `)
|
||||
//line app/vmalert/web.qtpl:477
|
||||
//line app/vmalert/web.qtpl:481
|
||||
qw422016.E().S(badgeClass)
|
||||
//line app/vmalert/web.qtpl:477
|
||||
//line app/vmalert/web.qtpl:481
|
||||
qw422016.N().S(`">`)
|
||||
//line app/vmalert/web.qtpl:477
|
||||
//line app/vmalert/web.qtpl:481
|
||||
qw422016.E().S(state)
|
||||
//line app/vmalert/web.qtpl:477
|
||||
//line app/vmalert/web.qtpl:481
|
||||
qw422016.N().S(`</span>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:478
|
||||
//line app/vmalert/web.qtpl:482
|
||||
}
|
||||
|
||||
//line app/vmalert/web.qtpl:478
|
||||
//line app/vmalert/web.qtpl:482
|
||||
func writebadgeState(qq422016 qtio422016.Writer, state string) {
|
||||
//line app/vmalert/web.qtpl:478
|
||||
//line app/vmalert/web.qtpl:482
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line app/vmalert/web.qtpl:478
|
||||
//line app/vmalert/web.qtpl:482
|
||||
streambadgeState(qw422016, state)
|
||||
//line app/vmalert/web.qtpl:478
|
||||
//line app/vmalert/web.qtpl:482
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line app/vmalert/web.qtpl:478
|
||||
//line app/vmalert/web.qtpl:482
|
||||
}
|
||||
|
||||
//line app/vmalert/web.qtpl:478
|
||||
//line app/vmalert/web.qtpl:482
|
||||
func badgeState(state string) string {
|
||||
//line app/vmalert/web.qtpl:478
|
||||
//line app/vmalert/web.qtpl:482
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line app/vmalert/web.qtpl:478
|
||||
//line app/vmalert/web.qtpl:482
|
||||
writebadgeState(qb422016, state)
|
||||
//line app/vmalert/web.qtpl:478
|
||||
//line app/vmalert/web.qtpl:482
|
||||
qs422016 := string(qb422016.B)
|
||||
//line app/vmalert/web.qtpl:478
|
||||
//line app/vmalert/web.qtpl:482
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line app/vmalert/web.qtpl:478
|
||||
//line app/vmalert/web.qtpl:482
|
||||
return qs422016
|
||||
//line app/vmalert/web.qtpl:478
|
||||
//line app/vmalert/web.qtpl:482
|
||||
}
|
||||
|
||||
//line app/vmalert/web.qtpl:480
|
||||
//line app/vmalert/web.qtpl:484
|
||||
func streambadgeRestored(qw422016 *qt422016.Writer) {
|
||||
//line app/vmalert/web.qtpl:480
|
||||
//line app/vmalert/web.qtpl:484
|
||||
qw422016.N().S(`
|
||||
<span class="badge bg-warning text-dark" title="Alert state was restored after the service restart from remote storage">restored</span>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:482
|
||||
//line app/vmalert/web.qtpl:486
|
||||
}
|
||||
|
||||
//line app/vmalert/web.qtpl:482
|
||||
//line app/vmalert/web.qtpl:486
|
||||
func writebadgeRestored(qq422016 qtio422016.Writer) {
|
||||
//line app/vmalert/web.qtpl:482
|
||||
//line app/vmalert/web.qtpl:486
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line app/vmalert/web.qtpl:482
|
||||
//line app/vmalert/web.qtpl:486
|
||||
streambadgeRestored(qw422016)
|
||||
//line app/vmalert/web.qtpl:482
|
||||
//line app/vmalert/web.qtpl:486
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line app/vmalert/web.qtpl:482
|
||||
//line app/vmalert/web.qtpl:486
|
||||
}
|
||||
|
||||
//line app/vmalert/web.qtpl:482
|
||||
//line app/vmalert/web.qtpl:486
|
||||
func badgeRestored() string {
|
||||
//line app/vmalert/web.qtpl:482
|
||||
//line app/vmalert/web.qtpl:486
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line app/vmalert/web.qtpl:482
|
||||
//line app/vmalert/web.qtpl:486
|
||||
writebadgeRestored(qb422016)
|
||||
//line app/vmalert/web.qtpl:482
|
||||
//line app/vmalert/web.qtpl:486
|
||||
qs422016 := string(qb422016.B)
|
||||
//line app/vmalert/web.qtpl:482
|
||||
//line app/vmalert/web.qtpl:486
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line app/vmalert/web.qtpl:482
|
||||
//line app/vmalert/web.qtpl:486
|
||||
return qs422016
|
||||
//line app/vmalert/web.qtpl:482
|
||||
//line app/vmalert/web.qtpl:486
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user