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

WIP Timeline Support #12831

Closed
wants to merge 31 commits into from
Closed
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
dee25ff
Moar intervals
andrewvc Jul 4, 2019
a3a7cbd
Checkpoint
andrewvc Jul 8, 2019
72fab36
Checkpoint
andrewvc Jul 9, 2019
22e7c12
Checkpoint
andrewvc Jul 11, 2019
aab5d8a
Merge remote-tracking branch 'origin/master' into intervals
andrewvc Sep 13, 2019
b46f81d
[Heartbeat] Report next_run info per event
andrewvc Sep 13, 2019
e90a046
Add changelog
andrewvc Sep 13, 2019
cdeac64
Incorporate PR feedback
andrewvc Sep 18, 2019
077be58
Checkpoint
andrewvc Oct 28, 2019
b06323f
Checkpoint
andrewvc Oct 28, 2019
a371f6e
Just report the timespan
andrewvc Nov 26, 2019
9c8dbe5
Merge remote-tracking branch 'origin/master' into next-run-range
andrewvc Nov 26, 2019
3e4e8ea
fix tests
andrewvc Nov 26, 2019
c9b39cb
fix relnote
andrewvc Nov 26, 2019
a00aa0f
Fmt
andrewvc Nov 26, 2019
e1d0c65
Tweaks
andrewvc Nov 26, 2019
da1fde1
Factor timeout into timespans
andrewvc Nov 26, 2019
4e08b3d
fmt
andrewvc Nov 26, 2019
51bffaa
Merge remote-tracking branch 'origin/master' into next-run-range
andrewvc Dec 8, 2019
f74c85d
Merge remote-tracking branch 'origin/master' into next-run-range
andrewvc Dec 11, 2019
1a7c1f4
Merge remote-tracking branch 'origin/master' into next-run-range
andrewvc Dec 12, 2019
0150408
Don't require docs on date_range sub-keys
andrewvc Dec 12, 2019
95acb3c
Remove print
andrewvc Dec 12, 2019
d565042
Merge remote-tracking branch 'origin/master' into next-run-range
andrewvc Dec 13, 2019
7b2162a
FMT
andrewvc Dec 13, 2019
0b28b6f
Merge remote-tracking branch 'origin/master' into intervals
andrewvc Dec 16, 2019
8feb085
Checkpoint
andrewvc Dec 16, 2019
9232921
Merge remote-tracking branch 'origin/master' into next-run-range
andrewvc Dec 16, 2019
7bf22b5
Merge branch 'next-run-range' into intervals
andrewvc Dec 16, 2019
89563ea
Merge remote-tracking branch 'origin/master' into intervals
andrewvc Dec 16, 2019
9cadda9
Merge remote-tracking branch 'origin/master' into intervals
andrewvc Aug 11, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 188 additions & 0 deletions dev-tools/dummy_http/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package main

import (
"fmt"
"math/rand"
"net/http"
"net/url"
"os"
"regexp"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
)

// An URL like /pattern?r='200x50,404x20,200|500x30'
// The above pattern would return 50 200 responses, then 20 404s, then randomly return a mix of 200 and 500
// responses 30 times

func main() {
states := &sync.Map{}

var reqs uint64 = 0

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should drop = 0 from declaration of var reqs; it is the zero value

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should drop = 0 from declaration of var reqs; it is the zero value


http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
atomic.AddUint64(&reqs, 1)

writer.Write([]byte("Dummy HTTP Server"))
})

http.HandleFunc("/pattern", func(writer http.ResponseWriter, request *http.Request) {
atomic.AddUint64(&reqs, 1)

status, body := handlePattern(states, request.URL)
writer.WriteHeader(status)
writer.Write([]byte(body))
})

go func() {
for {
time.Sleep(time.Second * 10)
r := atomic.LoadUint64(&reqs)
fmt.Printf("Processed %d reqs\n", r)
}
}()

port := 5678
fmt.Printf("Starting server on port %d\n", port)
err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
if err != nil {
fmt.Printf("Could not start server: %s", err)
os.Exit(1)
}
}

type responsePattern struct {
httpStatuses []int
httpStatusesLen int
countLimit int
}

func (rp *responsePattern) next() (status int, body string) {
var idx int
if rp.httpStatusesLen > 1 {
fmt.Printf("INTN %d\n", rp.httpStatusesLen)
idx = rand.Intn(rp.httpStatusesLen)
} else {
idx = 0
}
status = rp.httpStatuses[idx]
return status, strconv.Itoa(status)
}

type responsePatternSequence struct {
currentPatternIdx int
currentPattern *responsePattern
currentPatternCount int
patterns []*responsePattern
shuffle bool
mtx sync.Mutex
}

func (ps *responsePatternSequence) next() (status int, body string) {
ps.mtx.Lock()
ps.mtx.Unlock()

if ps.currentPatternCount >= ps.currentPattern.countLimit {
ps.advancePattern()
}

ps.currentPatternCount = ps.currentPatternCount + 1
return ps.currentPattern.next()
}

func (ps *responsePatternSequence) advancePattern() {
if ps.shuffle {
ps.currentPatternIdx = rand.Intn(len(ps.patterns)) - 1
ps.currentPattern = ps.patterns[ps.currentPatternIdx]
} else {
var nextIdx = ps.currentPatternIdx + 1
if nextIdx == len(ps.patterns) {
nextIdx = 0
}
ps.currentPatternIdx = nextIdx
ps.currentPattern = ps.patterns[nextIdx]
}

ps.currentPatternCount = 0
}

var statusListRegexp = regexp.MustCompile("^[|\\d]+$")

func handlePattern(states *sync.Map, url *url.URL) (status int, body string) {
query := url.Query()

rpsInter, ok := states.Load(url.RawQuery)
var rps *responsePatternSequence
if !ok {
patterns, err := compilePatterns(query.Get("r"))
if err != nil {
return 400, err.Error()
}
rps = NewResponsePatternSequence(patterns, query.Get("shuffle") == "true")
states.Store(url.RawQuery, rps)
} else {
rps = rpsInter.(*responsePatternSequence)
}

return rps.next()
}

func NewResponsePatternSequence(patterns []*responsePattern, shuffle bool) *responsePatternSequence {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported func NewResponsePatternSequence returns unexported type *main.responsePatternSequence, which can be annoying to use
exported function NewResponsePatternSequence should have comment or be unexported

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported func NewResponsePatternSequence returns unexported type *main.responsePatternSequence, which can be annoying to use
exported function NewResponsePatternSequence should have comment or be unexported

ps := responsePatternSequence{
currentPatternIdx: 0,
currentPattern: patterns[0],
currentPatternCount: 0,
patterns: patterns,
shuffle: shuffle,
mtx: sync.Mutex{},
}

return &ps
}

func compilePatterns(patternsStr string) (patterns []*responsePattern, err error) {
splitPatterns := strings.Split(patternsStr, ",")

for _, patternStr := range splitPatterns {
rp, err := compilePattern(patternStr)
if err != nil {
return nil, err
}
patterns = append(patterns, rp)
}

return patterns, nil
}

func compilePattern(patternStr string) (*responsePattern, error) {
rp := responsePattern{}

splitPattern := strings.Split(patternStr, "x")
if len(splitPattern) != 2 {
return nil, fmt.Errorf("Bad pattern '%s', expected a STATUSxCOUNT as pattern. Got %s")
}

statusDefStr := splitPattern[0]
if statusListRegexp.MatchString(statusDefStr) {
statuses := strings.Split(statusDefStr, "|")
for _, statusStr := range statuses {
status, _ := strconv.Atoi(statusStr)
rp.httpStatuses = append(rp.httpStatuses, status)
}
rp.httpStatusesLen = len(rp.httpStatuses)
} else {
return nil, fmt.Errorf("Expected a | separated list of numbers for status code def, got '%s'", statusDefStr)

}

count, err := strconv.Atoi(splitPattern[1])
if err != nil {
return nil, fmt.Errorf("Repeat def should be an int, got '%s'", splitPattern[1])
}
rp.countLimit = count

return &rp, nil
}
12 changes: 11 additions & 1 deletion heartbeat/_meta/fields.common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,22 @@
type: keyword
description: >
Indicator if monitor could validate the service to be available.

- name: check_group
type: keyword
description: >
A token unique to a simultaneously invoked group of checks as in the case where multiple IPs are checked for a single DNS entry.

- name: status_block
type: date
description: >
A date representing the start time of a logically contiguous segment of statuses typically either all up
or all down. In the case of a rapidly flapping endpoint this may be a mix.

- name: timespan
type: date_range
description: >
Time range this ping reported starting at the instant the check was started, ending at the start of the next scheduled check.

- name: timespan
type: date_range
description: >
Expand Down
20 changes: 20 additions & 0 deletions heartbeat/docs/fields.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,26 @@ type: keyword

--

*`monitor.status_block`*::
+
--
A date representing the start time of a logically contiguous segment of statuses typically either all up or all down. In the case of a rapidly flapping endpoint this may be a mix.


type: date

--

*`monitor.timespan`*::
+
--
Time range this ping reported starting at the instant the check was started, ending at the start of the next scheduled check.


type: date_range

--

*`monitor.timespan`*::
+
--
Expand Down
2 changes: 1 addition & 1 deletion heartbeat/include/fields.go

Large diffs are not rendered by default.

Loading