Skip to content

Commit

Permalink
Add M3QL support to native prom read handler (#1244)
Browse files Browse the repository at this point in the history
  • Loading branch information
benraskin92 authored Dec 18, 2018
1 parent 772701a commit 7dd6c34
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 6 deletions.
50 changes: 49 additions & 1 deletion src/query/api/v1/handler/prometheus/native/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"io"
"net/http"
"strconv"
"strings"
"time"

"github.com/m3db/m3/src/query/api/v1/handler/prometheus"
Expand Down Expand Up @@ -138,6 +139,10 @@ func parseParams(r *http.Request) (models.RequestParams, *xhttp.ParseError) {
params.IncludeEnd = !excludeEnd
}

if strings.ToLower(r.Header.Get("X-M3-Render-Format")) == "m3ql" {
params.FormatType = models.FormatM3QL
}

return params, nil
}

Expand Down Expand Up @@ -269,7 +274,6 @@ func renderResultsJSON(
func renderResultsInstantaneousJSON(
w io.Writer,
series []*ts.Series,
params models.RequestParams,
) {
jw := json.NewWriter(w)
jw.BeginObject()
Expand Down Expand Up @@ -312,3 +316,47 @@ func renderResultsInstantaneousJSON(
jw.EndObject()
jw.Close()
}

func renderM3QLResultsJSON(
w io.Writer,
series []*ts.Series,
params models.RequestParams,
) {
jw := json.NewWriter(w)
jw.BeginArray()

for _, s := range series {
jw.BeginObject()
jw.BeginObjectField("target")
jw.WriteString(s.Name())

jw.BeginObjectField("tags")
jw.BeginObject()

for _, tag := range s.Tags.Tags {
jw.BeginObjectField(string(tag.Name))
jw.WriteString(string(tag.Value))
}

jw.EndObject()

jw.BeginObjectField("datapoints")
jw.BeginArray()
for i := 0; i < s.Len(); i++ {
dp := s.Values().DatapointAt(i)
jw.BeginArray()
jw.WriteFloat64(dp.Value)
jw.WriteInt(int(dp.Timestamp.Unix()))
jw.EndArray()
}
jw.EndArray()

jw.BeginObjectField("step_size_ms")
jw.WriteInt(int(params.Step.Seconds() * 1000))

jw.EndObject()
}

jw.EndArray()
jw.Close()
}
3 changes: 1 addition & 2 deletions src/query/api/v1/handler/prometheus/native/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,6 @@ func TestRenderResultsJSON(t *testing.T) {
func TestRenderInstantaneousResultsJSON(t *testing.T) {
start := time.Unix(1535948880, 0)
buffer := bytes.NewBuffer(nil)
params := models.RequestParams{}
series := []*ts.Series{
ts.NewSeries("foo", ts.NewFixedStepValues(10*time.Second, 1, 1, start), test.TagSliceToTags([]models.Tag{
models.Tag{Name: []byte("bar"), Value: []byte("baz")},
Expand All @@ -198,7 +197,7 @@ func TestRenderInstantaneousResultsJSON(t *testing.T) {
})),
}

renderResultsInstantaneousJSON(buffer, series, params)
renderResultsInstantaneousJSON(buffer, series)

expected := mustPrettyJSON(t, `
{
Expand Down
8 changes: 6 additions & 2 deletions src/query/api/v1/handler/prometheus/native/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,13 @@ func (h *PromReadHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}

// TODO: Support multiple result types
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*")
if params.FormatType == models.FormatM3QL {
renderM3QLResultsJSON(w, result, params)
return
}

// TODO: Support multiple result types
renderResultsJSON(w, result, params)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,5 @@ func (h *PromReadInstantHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques

// TODO: Support multiple result types
w.Header().Set("Content-Type", "application/json")
renderResultsInstantaneousJSON(w, result, params)
renderResultsInstantaneousJSON(w, result)
}
38 changes: 38 additions & 0 deletions src/query/api/v1/handler/prometheus/native/read_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func TestPromReadHandler_Read(t *testing.T) {

r, parseErr := parseParams(req)
require.Nil(t, parseErr)
assert.Equal(t, models.FormatPromQL, r.FormatType)
seriesList, err := read(context.TODO(), promRead.engine, promRead.tagOpts, httptest.NewRecorder(), r)
require.NoError(t, err)
require.Len(t, seriesList, 2)
Expand All @@ -70,6 +71,43 @@ func TestPromReadHandler_Read(t *testing.T) {
}
}

type M3QLResp []struct {
Target string `json:"target"`
Tags map[string]string `json:"tags"`
Datapoints [][]float64 `json:"datapoints"`
StepSizeMs int `json:"step_size_ms"`
}

func TestPromReadHandler_ReadM3QL(t *testing.T) {
logging.InitWithCores(nil)

values, bounds := test.GenerateValuesAndBounds(nil, nil)

setup := newTestSetup()
promRead := setup.Handler

b := test.NewBlockFromValues(bounds, values)
setup.Storage.SetFetchBlocksResult(block.Result{Blocks: []block.Block{b}}, nil)

req, _ := http.NewRequest("GET", PromReadURL, nil)
req.Header.Add("X-M3-Render-Format", "m3ql")
req.URL.RawQuery = defaultParams().Encode()

recorder := httptest.NewRecorder()
promRead.ServeHTTP(recorder, req)

var m3qlResp M3QLResp
require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &m3qlResp))

assert.Len(t, m3qlResp, 2)
assert.Equal(t, "dummy0", m3qlResp[0].Target)
assert.Equal(t, map[string]string{"__name__": "dummy0", "dummy0": "dummy0"}, m3qlResp[0].Tags)
assert.Equal(t, 10000, m3qlResp[0].StepSizeMs)
assert.Equal(t, "dummy1", m3qlResp[1].Target)
assert.Equal(t, map[string]string{"__name__": "dummy1", "dummy1": "dummy1"}, m3qlResp[1].Tags)
assert.Equal(t, 10000, m3qlResp[1].StepSizeMs)
}

func newReadRequest(t *testing.T, params url.Values) *http.Request {
req, err := http.NewRequest("GET", PromReadURL, nil)
require.NoError(t, err)
Expand Down
11 changes: 11 additions & 0 deletions src/query/models/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ import (
"time"
)

// FormatType describes what format to return the data in
type FormatType int

const (
// FormatPromQL returns results in Prom format
FormatPromQL FormatType = iota
// FormatM3QL returns results in M3QL format
FormatM3QL
)

// LookbackDelta determines the time since the last sample after which a time
// series is considered stale (inclusive).
// TODO: Make this configurable
Expand All @@ -40,6 +50,7 @@ type RequestParams struct {
Query string
Debug bool
IncludeEnd bool
FormatType FormatType
}

// ExclusiveEnd returns the end exclusive
Expand Down

0 comments on commit 7dd6c34

Please sign in to comment.