From 8e0926fe5e548632d7d67594a4dac3cff3ddaffd Mon Sep 17 00:00:00 2001 From: Kevin Jing Qiu Date: Fri, 3 Aug 2018 00:37:19 -0400 Subject: [PATCH] Allow evaluating of individual expressions --- _proxy/proxy.go | 104 ++++++++++++++++++++++++++++++++++++++++-------- main.go | 6 +-- pkg/executor.go | 57 +++++++++++++------------- 3 files changed, 118 insertions(+), 49 deletions(-) diff --git a/_proxy/proxy.go b/_proxy/proxy.go index 048a1b5..800af3e 100644 --- a/_proxy/proxy.go +++ b/_proxy/proxy.go @@ -2,10 +2,8 @@ package promql import ( "log" -"github.com/prometheus/prometheus/storage" -"github.com/prometheus/prometheus/util/testutil" + "fmt" "time" - "golang.org/x/net/context" ) type TestCommand = testCommand @@ -32,21 +30,95 @@ func ParseTestCommand(input string) ([]TestCommand, error) { return t.cmds, nil } -func ExecuteTestCommand(tc TestCommand, storage *storage.Storage) (*storage.Storage, error) { - t := Test{ - T: t{}, - storage: *storage, - cmds: []TestCommand{tc}, +type TestShell struct { + Test Test +} + +func (ts *TestShell) SetCmds(cmds []TestCommand) { + ts.Test.cmds = cmds +} + +func (ts *TestShell) Run() error { + for _, cmd := range ts.Test.cmds { + err := ts.exec(cmd) + if err != nil { + return err // TODO: use multi-error + } } - t.queryEngine = NewEngine(nil, nil, 20, 10*time.Second) - t.context, t.cancelCtx = context.WithCancel(context.Background()) - return &t.storage, t.exec(tc) + return nil } -func NewTestStorage() *storage.Storage { - t := Test{ - T: t{}, +func (ts *TestShell) exec(tc testCommand) error { + switch cmd := tc.(type) { + case *evalCmd: + t := ts.Test + q, _ := t.queryEngine.NewInstantQuery(t.storage, cmd.expr, cmd.start) + res := q.Exec(t.context) + if res.Err != nil { + if cmd.fail { + return nil + } + return fmt.Errorf("error evaluating query %q: %s", cmd.expr, res.Err) + } + defer q.Close() + if res.Err == nil && cmd.fail { + return fmt.Errorf("expected error evaluating query but got none") + } + + if len(cmd.expected) == 0 { + fmt.Println(res.Value) + return nil + } + err := cmd.compareResult(res.Value) + if err != nil { + return fmt.Errorf("error in %s %s: %s", cmd, cmd.expr, err) + } + + // Check query returns same result in range mode, + /// by checking against the middle step. + q, _ = t.queryEngine.NewRangeQuery(t.storage, cmd.expr, cmd.start.Add(-time.Minute), cmd.start.Add(time.Minute), time.Minute) + rangeRes := q.Exec(t.context) + if rangeRes.Err != nil { + return fmt.Errorf("error evaluating query %q in range mode: %s", cmd.expr, rangeRes.Err) + } + defer q.Close() + if cmd.ordered { + // Ordering isn't defined for range queries. + return nil + } + mat := rangeRes.Value.(Matrix) + vec := make(Vector, 0, len(mat)) + for _, series := range mat { + for _, point := range series.Points { + if point.T == timeMilliseconds(cmd.start) { + vec = append(vec, Sample{Metric: series.Metric, Point: point}) + break + } + } + } + if len(cmd.expected) == 0 { + fmt.Println(res.Value) + return nil + } + if _, ok := res.Value.(Scalar); ok { + err = cmd.compareResult(Scalar{V: vec[0].Point.V}) + } else { + err = cmd.compareResult(vec) + } + if err != nil { + return fmt.Errorf("error in %s %s rande mode: %s", cmd, cmd.expr, err) + } + default: return ts.Test.exec(tc) } - storage := testutil.NewStorage(t) - return &storage + return nil +} + +func NewTestShell() *TestShell { + ts := &TestShell{ + Test: Test{ + T: t{}, + cmds: []TestCommand{}, + }} + ts.Test.exec(&clearCmd{}) + return ts } diff --git a/main.go b/main.go index 58ada93..1a23dd5 100644 --- a/main.go +++ b/main.go @@ -4,8 +4,7 @@ import ( "github.com/c-bata/go-prompt" "github.com/kevinjqiu/promcli/pkg" "fmt" - "github.com/prometheus/prometheus/promql" -) + ) const Banner = ` ____ ____ _ ___ @@ -16,8 +15,7 @@ const Banner = ` func executor(input string) { - storage := promql.NewTestStorage() - pkg.Executor(input, storage) + pkg.Executor(input) } func main() { diff --git a/pkg/executor.go b/pkg/executor.go index e1e4b0b..64ecb6c 100644 --- a/pkg/executor.go +++ b/pkg/executor.go @@ -1,8 +1,7 @@ package pkg import ( - "github.com/prometheus/prometheus/storage" - "strings" + "strings" "fmt" "github.com/prometheus/prometheus/promql" ) @@ -18,73 +17,73 @@ var ApplicationState struct { buffer []string } -func Init() { +var testShell *promql.TestShell + +func init() { ApplicationState.state = StateCommand ApplicationState.buffer = make([]string, 0) + testShell = promql.NewTestShell() } -func execute(input string, storage *storage.Storage) *storage.Storage { +func execute(input string) { cmds, err := promql.ParseTestCommand(input) if err != nil { fmt.Println(err.Error()) } - for _, cmd := range cmds { - storage, err = promql.ExecuteTestCommand(cmd, storage) - if err != nil { - fmt.Println(err.Error()) - } + + testShell.SetCmds(cmds) + err = testShell.Run() + if err != nil { + fmt.Println(err.Error()) } - return storage } -func handleClear(input string, storage *storage.Storage) *storage.Storage { - return execute(input, storage) +func handleClear(input string) { + execute(input) } -func handleLoad(input string, storage *storage.Storage) *storage.Storage { +func handleLoad(input string) { if ApplicationState.state == StateLoad { fmt.Println("Already in Loading state") - return storage + return } ApplicationState.state = StateLoad ApplicationState.buffer = append(ApplicationState.buffer, input) - return storage + return } -func handleEval(input string, storage *storage.Storage) *storage.Storage { +func handleEval(input string) { if ApplicationState.state == StateEval { fmt.Println("Already in Eval state") - return storage + return } ApplicationState.state = StateEval ApplicationState.buffer = append(ApplicationState.buffer, input) - return storage + return } -func handleEmptyLine(input string, storage *storage.Storage) *storage.Storage { +func handleEmptyLine(input string) { if ApplicationState.state == StateLoad || ApplicationState.state == StateEval { lines := strings.Join(ApplicationState.buffer, "\n") - storage = execute(lines, storage) + execute(lines) ApplicationState.buffer = make([]string, 0) ApplicationState.state = StateCommand } - return storage } -func handleOther(input string, storage *storage.Storage) *storage.Storage { +func handleOther(input string) { if ApplicationState.state == StateLoad || ApplicationState.state == StateEval { ApplicationState.buffer = append(ApplicationState.buffer, input) } - return storage } -func Executor(input string, storage *storage.Storage) { +func Executor(input string) { switch { - case input == "clear": storage = handleClear(input, storage) - case strings.HasPrefix(input, "load"): storage = handleLoad(input, storage) - case strings.HasPrefix(input, "eval"): storage = handleEval(input, storage) // TODO: promql.test's eval evaluates against expectations. We need it to display the result of an evaluation. - case input == "": storage = handleEmptyLine(input, storage) - default: storage = handleOther(input, storage) + case input == "clear": handleClear(input) + case strings.HasPrefix(input, "load"): handleLoad(input) + case strings.HasPrefix(input, "eval"): handleEval(input) // TODO: promql.test's eval evaluates against expectations. We need it to display the result of an evaluation. + case input == "": handleEmptyLine(input) + default: handleOther(input) } }