Commit 60721435 authored by Matt T. Proud's avatar Matt T. Proud
Browse files

Initial commit of external resources.

parent 734d28b5
TEST_ARTIFACTS = prometheus
all: test
test: build
go test ./...
build:
$(MAKE) -C model
go build ./...
go build .
clean:
rm -rf $(TEST_ARTIFACTS)
$(MAKE) -C model clean
-find . -type f -iname '*~' -exec rm '{}' ';'
-find . -type f -iname '*#' -exec rm '{}' ';'
-find . -type f -iname '.*' -exec rm '{}' ';'
format:
find . -iname '*.go' | grep -v generated | xargs -n1 gofmt -w
.PHONY: build clean format test
prometheus
Prometheus
==========
Bedecke deinen Himmel, Zeus!
\ No newline at end of file
Bedecke deinen Himmel, Zeus! A new kid is in town.
Prerequisites
=============
1. Go 1.0.X.
2. LevelDB: (https://code.google.com/p/leveldb/).
3. Protocol Buffers Compiler: (http://code.google.com/p/protobuf/).
4. goprotobuf: the code generator and runtime library: (http://code.google.com/p/goprotobuf/).
5. Levigo, a Go-wrapper around LevelDB's C library: (https://github.com/jmhodges/levigo).
Initial Hurdles
===============
1. A bit of this grew organically without an easy way of binding it all together. The tests will pass but slowly. They were not optimized for speed but end-to-end coverage of the whole storage model. This is something immediate to fix.
2. Protocol Buffer generator for Go changed emitted output API. This will need to be fixed before other contributors can participate.
Milestones
==========
1. In-memory archive, basic rule language, simple computation engine, and naive exposition system.
package indexable
import (
"encoding/binary"
"time"
)
var (
EarliestTime = EncodeTime(time.Unix(0, 0))
)
func EncodeTimeInto(dst []byte, t time.Time) {
binary.BigEndian.PutUint64(dst, uint64(t.Unix()))
}
func EncodeTime(t time.Time) []byte {
buffer := make([]byte, 8)
EncodeTimeInto(buffer, t)
return buffer
}
func DecodeTime(src []byte) time.Time {
return time.Unix(int64(binary.BigEndian.Uint64(src)), 0)
}
package indexable
import (
"math/rand"
"testing"
"testing/quick"
"time"
)
func TestTimeEndToEnd(t *testing.T) {
tester := func(x int) bool {
random := rand.New(rand.NewSource(int64(x)))
buffer := make([]byte, 8)
incoming := time.Unix(random.Int63(), 0)
EncodeTimeInto(buffer, incoming)
outgoing := DecodeTime(buffer)
return incoming.Equal(outgoing) && incoming.Unix() == outgoing.Unix()
}
if err := quick.Check(tester, nil); err != nil {
t.Error(err)
}
}
mtp@Matt.local.225
\ No newline at end of file
// {
// set evaluation_interval = "30s";
// target "http://www.example.com:80/metrics"
// rule archived {name="instance:requests_total:sum"} = sum by (instance) {name="requests"};
// rule archived {name="instance:requests-by_result_code_total:sum"} =
// sum by (instance,result_code) {name="requests"};
// rule archived {name="instance:requests-by_result_code:sum"} =
// {name="instances:requests-by_result_code"}
// / by (instance)
// {name="instances:requests_total:sum"};
// }
{
set evaluation_interval = “2m”;
permanent {
rule {
labels {
set name = “process:request_rate_qps-allowed:sum”;
set job = “frontend”;
}
// Values may be a literal or an expression.
set value = 500;
// I wonder: Is it practical to express labels similar to above in a better DSL?
// set value = EXPRESSION … WITH LABELS {foo=”bar”};
}
}
rule {
// This provides a way of overriding existing labels, unsetting them, or
// appending new ones.
labels {
// “name” is obligatory.
set name = “process:requests_total:sum”;
}
// Here we are extracting a metric with the name label value of “requests” from
// job “frontend” and merely accumulating this into a named variable. It is
// similar to standing. Each sum is keyed to the UNIX process and job from which
// it came.
set value = SUM({name=”requests”, job=”frontend”}) BY (process, job);
}
rule {
// This provides a way of overriding existing labels, unsetting them, or
// appending new ones.
labels {
// “name” is obligatory.
set name = “process:request_qps:rate5m”;
}
// Here we are extracting a metric with the name label value of “requests” from
// job “frontend” and merely accumulating this into a named variable. It is
// similar to standing.
set value = RATE({name=”process:requests_total:sum”, job=”frontend”} OVER “5m”);
}
}
}
\ No newline at end of file
package main
import (
"fmt"
"strings"
"unicode"
"unicode/utf8"
)
type itemType int
const (
itemError itemType = iota
itemEof
itemRuleContextOpen
itemRuleContextClose
itemSetKeyword
itemKey
itemEqual
itemValue
itemSemicolon
itemQuote
itemRulesKeyword
itemRuleKeyword
itemRuleName
itemRuleValue
itemOpenBracket
itemCloseBracket
)
const (
literalCloseBracket = "}"
literalEof = -1
literalOpenBracket = "{"
literalRule = "rule"
literalRules = "rules"
literalSet = "set"
literalEqual = "="
literalQuote = `"`
literalSemicolon = ";"
)
type element struct {
itemType itemType
value string
}
func (e element) String() string {
switch e.itemType {
case itemError:
return e.value
case itemEof:
return "EOF"
}
return fmt.Sprintf("%s %q", e.itemType, e.value)
}
type lexer struct {
input string
elements chan element
start int
position int
runeWidth int
}
func lex(name, input string) (*lexer, chan element) {
l := &lexer{
input: input,
elements: make(chan element),
}
go l.run()
return l, l.elements
}
func (l *lexer) run() {
for state := lexBody; state != nil; {
state = state(l)
}
close(l.elements)
}
func (l *lexer) next() (rune rune) {
if l.position >= len(l.input) {
l.runeWidth = 0
return literalEof
}
rune, l.runeWidth = utf8.DecodeRuneInString(l.input[l.position:])
l.position += l.runeWidth
return rune
}
func lexBody(l *lexer) lexFunction {
if strings.HasPrefix(l.input[l.position:], literalOpenBracket) {
return lexRulesetOpen
}
switch rune := l.next(); {
case rune == literalEof:
l.emit(itemEof)
return nil
case isSpace(rune):
l.ignore()
default:
return l.errorf("illegal input")
}
return lexBody
}
func lexRulesetOpen(l *lexer) lexFunction {
l.position += len(literalOpenBracket)
l.emit(itemRuleContextOpen)
return lexRulesetInside
}
func lexRulesetInside(l *lexer) lexFunction {
if strings.HasPrefix(l.input[l.position:], literalCloseBracket) {
return lexRulesetClose
}
if strings.HasPrefix(l.input[l.position:], literalSet) {
return lexRuleSetKeyword
}
if strings.HasPrefix(l.input[l.position:], literalRules) {
return lexRulesetRules
}
switch rune := l.next(); {
case rune == literalEof:
return l.errorf("unterminated ruleset")
case isSpace(rune):
l.ignore()
case rune == ';':
l.ignore()
default:
return l.errorf("unrecognized input")
}
return lexRulesetInside
}
func lexRulesetRules(l *lexer) lexFunction {
l.position += len(literalRules)
l.emit(itemRulesKeyword)
return lexRulesetRulesBlockOpen
}
func lexRulesetRulesBlockOpen(l *lexer) lexFunction {
if strings.HasPrefix(l.input[l.position:], literalOpenBracket) {
l.position += len(literalOpenBracket)
l.emit(itemOpenBracket)
return lexRulesetRulesBlockInside
}
switch rune := l.next(); {
case isSpace(rune):
l.ignore()
default:
return l.errorf("unrecognized input")
}
return lexRulesetRulesBlockOpen
}
func lexRulesetRulesBlockInside(l *lexer) lexFunction {
if strings.HasPrefix(l.input[l.position:], literalRule) {
return lexRulesetRuleBegin
}
if strings.HasPrefix(l.input[l.position:], literalCloseBracket) {
return lexRulesetRulesBlockClose
}
switch rune := l.next(); {
case isSpace(rune):
l.ignore()
default:
return l.errorf("unrecognized input")
}
return lexRulesetRulesBlockInside
}
func lexRulesetRulesBlockClose(l *lexer) lexFunction {
l.position += len(literalCloseBracket)
l.emit(itemCloseBracket)
return lexRulesetInside
}
func lexRulesetRuleBegin(l *lexer) lexFunction {
l.position += len(literalRule)
l.emit(itemRuleKeyword)
return lexRulesetRuleName
}
func lexRulesetRuleName(l *lexer) lexFunction {
switch rune := l.next(); {
case isSpace(rune):
l.ignore()
case isIdentifierOpen(rune):
for {
switch rune := l.next(); {
case isMetricIdentifier(rune):
case rune == '=':
l.backup()
l.emit(itemRuleName)
return lexRulesetRuleEqual
default:
return l.errorf("bad rule name")
}
}
default:
return l.errorf("unrecognized input")
}
return lexRulesetRuleName
}
func lexRulesetRuleEqual(l *lexer) lexFunction {
if strings.HasPrefix(l.input[l.position:], literalEqual) {
l.position += len(literalEqual)
l.emit(itemEqual)
return lexRulesetRuleDefinitionBegin
}
switch rune := l.next(); {
case isSpace(rune):
l.ignore()
default:
return l.errorf("unrecognized input")
}
return lexRulesetRuleEqual
}
func lexRulesetRuleDefinitionBegin(l *lexer) lexFunction {
switch rune := l.next(); {
case isSpace(rune):
l.ignore()
case isIdentifierOpen(rune):
for {
switch rune := l.next(); {
case isMetricIdentifier(rune):
case rune == ';':
l.emit(itemRuleValue)
return lexRulesetRulesBlockInside
default:
return l.errorf("unrecognized input")
}
}
default:
return l.errorf("unrecognized input")
}
return lexRulesetRuleDefinitionBegin
}
func lexRuleSetKeyword(l *lexer) lexFunction {
l.position += len(literalSet)
l.emit(itemSetKeyword)
return lexRuleSetInside
}
func (l *lexer) backup() {
l.position -= l.runeWidth
}
func isIdentifierOpen(rune rune) bool {
switch rune := rune; {
case unicode.IsLetter(rune):
return true
case rune == '_':
return true
}
return false
}
func lexRuleSetInside(l *lexer) lexFunction {
switch rune := l.next(); {
case rune == literalEof:
return l.errorf("unterminated set statement")
case isSpace(rune):
l.ignore()
case rune == ';':
return l.errorf("unexpected ;")
case rune == '=':
return l.errorf("unexpected =")
case isIdentifierOpen(rune):
l.backup()
return lexRuleSetKey
default:
return l.errorf("unrecognized input")
}
return lexRuleSetInside
}
func isIdentifier(rune rune) bool {
switch rune := rune; {
case isIdentifierOpen(rune):
return true
case unicode.IsDigit(rune):
return true
}
return false
}
func isMetricIdentifier(rune rune) bool {
switch rune := rune; {
case isIdentifier(rune):
return true
case rune == ':':
return true
}
return false
}
func (l *lexer) peek() rune {
rune := l.next()
l.backup()
return rune
}
func (l *lexer) atTerminator() bool {
switch rune := l.peek(); {
case isSpace(rune):
return true
case rune == ';':
return true
}
return false
}
func lexRuleSetKey(l *lexer) lexFunction {
switch rune := l.next(); {
case rune == literalEof:
return l.errorf("incomplete set statement")
case isIdentifier(rune):
default:
l.backup()
if !l.atTerminator() {
return l.errorf("unexpected character %+U %q", rune, rune)
}
l.emit(itemKey)
return lexRuleSetEqual
}
return lexRuleSetKey
}
func lexRuleSetEqual(l *lexer) lexFunction {
if strings.HasPrefix(l.input[l.position:], literalEqual) {
l.position += len(literalEqual)
l.emit(itemEqual)
return lexRuleSetValueOpenQuote
}
switch rune := l.next(); {
case rune == literalEof:
return l.errorf("incomplete set statement")
case isSpace(rune):
l.ignore()
default:
return l.errorf("unexpected character %+U %q", rune, rune)
}
return lexRuleSetEqual
}
func lexRuleSetValueOpenQuote(l *lexer) lexFunction {
if strings.HasPrefix(l.input[l.position:], literalQuote) {
l.position += len(literalQuote)
l.emit(itemQuote)
return lexRuleSetValue
}
switch rune := l.next(); {
case rune == literalEof:
return l.errorf("incomplete set statement")
case isSpace(rune):
l.ignore()
default:
return l.errorf("unexpected character %+U %q", rune, rune)
}
return lexRuleSetValueOpenQuote
}
func lexRuleSetValue(l *lexer) lexFunction {
var lastRuneEscapes bool = false
for {
rune := l.next()
{
if rune == '"' && !lastRuneEscapes {
l.backup()
l.emit(itemValue)
return lexRuleSetValueCloseQuote
}
if !lastRuneEscapes && rune == '\\' {
lastRuneEscapes = true
} else {
lastRuneEscapes = false
}
}
}
panic("unreachable")
}
func lexRuleSetValueCloseQuote(l *lexer) lexFunction {
if strings.HasPrefix(l.input[l.position:], literalQuote) {
l.position += len(literalQuote)
l.emit(itemQuote)
return lexRuleSetSemicolon
}
switch rune := l.next(); {
case isSpace(rune):
l.ignore()
default:
return l.errorf("unexpected character %+U %q", rune, rune)
}
return lexRuleSetValueCloseQuote
}
func lexRuleSetSemicolon(l *lexer) lexFunction {
if strings.HasPrefix(l.input[l.position:], literalSemicolon) {
l.position += len(literalSemicolon)
l.emit(itemSemicolon)
return lexRulesetInside
}
switch rune := l.next(); {
case isSpace(rune):
l.ignore()
default:
return l.errorf("unexpected character %+U %q", rune, rune)
}
return lexRuleSetSemicolon
}
func (l *lexer) ignore() {
l.start = l.position
}