Skip to content

Commit

Permalink
feat: calculate uptime of query
Browse files Browse the repository at this point in the history
  • Loading branch information
henrywhitaker3 committed Apr 19, 2024
1 parent 7031a81 commit e687cf6
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 24 deletions.
13 changes: 13 additions & 0 deletions internal/collector/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ type Result struct {
Success bool
// The boolean result of the main service query
Status bool
// The percentage uptime for the specified duration
Uptime float32
}

type Collector struct {
Expand Down Expand Up @@ -43,12 +45,23 @@ func (c *Collector) Collect(ctx context.Context) []Result {
log.Printf("ERROR - Failed to scrape status metric for %s query %s: %s", svc.Name, svc.Query.Name, err)
res.Success = false
res.Status = false
res.Uptime = 0
results = append(results, res)
continue
}
uptime, err := c.q.Uptime(ctx, svc.Query)
if err != nil {
log.Printf("ERROR - Failed to scrape uptime metric for %s query %s: %s", svc.Name, svc.Query.Name, err)
res.Success = false
res.Status = false
res.Uptime = 0
results = append(results, res)
continue
}

res.Success = true
res.Status = status
res.Uptime = uptime
results = append(results, res)
}

Expand Down
7 changes: 7 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type Query struct {
Query string `yaml:"query"`
Expression string `yaml:"expression"`
Range time.Duration `yaml:"range"`
Step time.Duration `yaml:"step"`
}

type Service struct {
Expand Down Expand Up @@ -67,6 +68,12 @@ func setDefaults(conf *Config) {

for i, svc := range conf.Services {
svc.Query.Name = "main"
if svc.Query.Range == 0 {
svc.Query.Range = time.Hour * 24
}
if svc.Query.Step == 0 {
svc.Query.Step = time.Minute * 5
}
conf.Services[i] = svc
}

Expand Down
81 changes: 58 additions & 23 deletions internal/querier/querier.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ import (
"github.com/prometheus/common/model"
)

var (
ErrTypeNotImplemented = errors.New("query result type not implemented yet")
)

type Querier struct {
client v1.API
}
Expand All @@ -40,8 +44,39 @@ func NewQuerier(conf *config.Config) (*Querier, error) {
}, nil
}

func (q *Querier) Range(ctx context.Context, query config.Query) (*Result, error) {
return nil, errors.New("range queries not implemented yet")
func (q *Querier) Uptime(ctx context.Context, query config.Query) (float32, error) {
val, _, err := q.client.QueryRange(ctx, query.Query, v1.Range{
Start: time.Now().Add(-query.Range),
End: time.Now(),
Step: time.Minute * 5,
})
if err != nil {
return 0, err
}

switch r := val.(type) {
case model.Matrix:
if r.Len() < 1 {
return 0, errors.New("no results for query")
}

passing := 0
total := 0
for _, val := range r[0].Values {
res, err := q.vector(val.Value, query)
if err != nil {
panic(err)
}
total++
if res {
passing++
}
}

return float32(passing/total) * 100, nil
}

return 100, ErrTypeNotImplemented
}

func (q *Querier) Status(ctx context.Context, query config.Query) (bool, error) {
Expand All @@ -50,6 +85,18 @@ func (q *Querier) Status(ctx context.Context, query config.Query) (bool, error)
return false, err
}

switch r := val.(type) {
case model.Vector:
if r.Len() < 1 {
return false, errors.New("no results for query")
}
return q.vector(r[0].Value, query)
}

return false, ErrTypeNotImplemented
}

func (q *Querier) vector(v model.SampleValue, query config.Query) (bool, error) {
env := map[string]any{
"result": 0,
}
Expand All @@ -59,29 +106,17 @@ func (q *Querier) Status(ctx context.Context, query config.Query) (bool, error)
return false, fmt.Errorf("failed to compile expr: %v", err)
}

switch r := val.(type) {
case model.Vector:
if r.Len() < 1 {
return false, errors.New("no results for query")
}
// if r.Len() != 1 {
// return nil, errors.New("unexpected result length")
// }

val, err := strconv.ParseFloat(r[0].Value.String(), 64)
if err != nil {
return false, fmt.Errorf("failed to parse result: %v", err)
}

env["result"] = val
val, err := strconv.ParseFloat(v.String(), 64)
if err != nil {
return false, fmt.Errorf("failed to parse result: %v", err)
}

out, err := expr.Run(exp, env)
if err != nil {
return false, err
}
env["result"] = val

return out.(bool), nil
out, err := expr.Run(exp, env)
if err != nil {
return false, err
}

return false, nil
return out.(bool), nil
}
5 changes: 4 additions & 1 deletion internal/resources/views/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ <h1 class="text-center text-2xl text-avocado-600 mb-6">{{ .Config.UI.HeaderText
<div class="flex flex-col items-center justify-center rounded-md shadow-md border">
{{- range .Results }}
<div class="flex justify-between items-center w-full py-6 px-6 md:px-8 lg:px-12 border-b">
<h2 class="text-lg font-bold">{{ .Service.Name }}</h2>
<div>
<h2 class="text-lg font-bold inline-block mr-2">{{ .Service.Name }}</h2>
<span class="text-gray-500 text-sm">{{ .Uptime }}%</span>
</div>
<div>
{{- if not .Success }}
<img src="/static/unknown.svg" class="h-auto max-w-10" alt="unknown icon">
Expand Down

0 comments on commit e687cf6

Please sign in to comment.