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

exp/api: Add experimental exp module; Add remote API with write client and handler. #1658

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 11 additions & 5 deletions .bingo/Variables.mk
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.8. DO NOT EDIT.
# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.9. DO NOT EDIT.
# All tools are designed to be build inside $GOBIN.
BINGO_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
GOPATH ?= $(shell go env GOPATH)
Expand All @@ -7,16 +7,22 @@ GO ?= $(shell which go)

# Below generated variables ensure that every time a tool under each variable is invoked, the correct version
# will be used; reinstalling only if needed.
# For example for goimports variable:
# For example for buf variable:
#
# In your main Makefile (for non array binaries):
#
#include .bingo/Variables.mk # Assuming -dir was set to .bingo .
#
#command: $(GOIMPORTS)
# @echo "Running goimports"
# @$(GOIMPORTS) <flags/args..>
#command: $(BUF)
# @echo "Running buf"
# @$(BUF) <flags/args..>
#
BUF := $(GOBIN)/buf-v1.39.0
$(BUF): $(BINGO_DIR)/buf.mod
@# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies.
@echo "(re)installing $(GOBIN)/buf-v1.39.0"
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=buf.mod -o=$(GOBIN)/buf-v1.39.0 "github.com/bufbuild/buf/cmd/buf"

GOIMPORTS := $(GOBIN)/goimports-v0.9.3
$(GOIMPORTS): $(BINGO_DIR)/goimports.mod
@# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies.
Expand Down
5 changes: 5 additions & 0 deletions .bingo/buf.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT

go 1.22.6

require github.com/bufbuild/buf v1.39.0 // cmd/buf
336 changes: 336 additions & 0 deletions .bingo/buf.sum

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion .bingo/variables.env
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.8. DO NOT EDIT.
# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.9. DO NOT EDIT.
# All tools are designed to be build inside $GOBIN.
# Those variables will work only until 'bingo get' was invoked, or if tools were installed via Makefile's Variables.mk.
GOBIN=${GOBIN:=$(go env GOBIN)}
Expand All @@ -8,5 +8,7 @@ if [ -z "$GOBIN" ]; then
fi


BUF="${GOBIN}/buf-v1.39.0"

GOIMPORTS="${GOBIN}/goimports-v0.9.3"

30 changes: 27 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@
include .bingo/Variables.mk
include Makefile.common

.PHONY: deps
deps:
$(GO) work sync
$(MAKE) common-deps

.PHONY: test
test: deps common-test
test: deps common-test test-exp

.PHONY: test-short
test-short: deps common-test-short
test-short: deps common-test-short test-exp-short

.PHONY: generate-go-collector-test-files
file := supported_go_versions.txt
Expand All @@ -35,5 +40,24 @@ generate-go-collector-test-files:
go mod tidy

.PHONY: fmt
fmt: common-format
fmt: common-format $(GOIMPORTS)
$(GOIMPORTS) -local github.com/prometheus/client_golang -w .

.PHONY: proto
proto: ## Regenerate Go from remote write proto.
proto: $(BUF)
@echo ">> regenerating Prometheus Remote Write proto"
@cd exp/api/remote/genproto && $(BUF) generate
@cd exp/api/remote && find genproto/ -type f -exec sed -i '' 's/protohelpers "github.com\/planetscale\/vtprotobuf\/protohelpers"/protohelpers "github.com\/prometheus\/client_golang\/exp\/internal\/github.com\/planetscale\/vtprotobuf\/protohelpers"/g' {} \;
# For some reasons buf generates this unused import, kill it manually for now and reformat.
@cd exp/api/remote && find genproto/ -type f -exec sed -i '' 's/_ "github.com\/gogo\/protobuf\/gogoproto"//g' {} \;
@cd exp/api/remote && go fmt ./genproto/...
$(MAKE) fmt

.PHONY: test-exp
test-exp:
cd exp && $(GOTEST) $(test-flags) $(GOOPTS) $(pkgs)

.PHONY: test-exp-short
test-exp-short:
cd exp && $(GOTEST) -short $(GOOPTS) $(pkgs)
5 changes: 4 additions & 1 deletion api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"bytes"
"context"
"errors"
"io"
"net"
"net/http"
"net/url"
Expand Down Expand Up @@ -133,7 +134,8 @@ func (c *httpClient) Do(ctx context.Context, req *http.Request) (*http.Response,
resp, err := c.client.Do(req)
defer func() {
if resp != nil {
resp.Body.Close()
_, _ = io.Copy(io.Discard, resp.Body)
_ = resp.Body.Close()
}
}()

Expand All @@ -145,6 +147,7 @@ func (c *httpClient) Do(ctx context.Context, req *http.Request) (*http.Response,
done := make(chan struct{})
go func() {
var buf bytes.Buffer
// TODO(bwplotka): Add LimitReader for too long err messages (e.g. limit by 1KB)
_, err = buf.ReadFrom(resp.Body)
body = buf.Bytes()
close(done)
Expand Down
21 changes: 21 additions & 0 deletions exp/api/remote/genproto/buf.gen.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# buf.gen.yaml
version: v2

plugins:
- remote: buf.build/protocolbuffers/go:v1.31.0
out: .
opt:
- Mio/prometheus/write/v2/types.proto=./v2

# vtproto for efficiency utilities like pooling etc.
# https://buf.build/community/planetscale-vtprotobuf?version=v0.6.0
- remote: buf.build/community/planetscale-vtprotobuf:v0.6.0
out: .
opt:
- Mio/prometheus/write/v2/types.proto=./v2
- features=marshal+unmarshal+size

inputs:
- module: buf.build/prometheus/prometheus:5b212ab78fb7460e831cf7ff2d83e385
types:
- "io.prometheus.write.v2.Request"
97 changes: 97 additions & 0 deletions exp/api/remote/genproto/v2/symbols.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright (c) Bartłomiej Płotka @bwplotka
// Licensed under the Apache License 2.0.

// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Copyright 2024 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package writev2

// SymbolsTable implements table for easy symbol use.
type SymbolsTable struct {
strings []string
symbolsMap map[string]uint32
}

// NewSymbolTable returns a symbol table.
func NewSymbolTable() SymbolsTable {
return SymbolsTable{
// Empty string is required as a first element.
symbolsMap: map[string]uint32{"": 0},
strings: []string{""},
}
}

// Symbolize adds (if not added before) a string to the symbols table,
// while returning its reference number.
func (t *SymbolsTable) Symbolize(str string) uint32 {
if ref, ok := t.symbolsMap[str]; ok {
return ref
}
ref := uint32(len(t.strings))
t.strings = append(t.strings, str)
t.symbolsMap[str] = ref
return ref
}

// SymbolizeLabels symbolize Prometheus labels.
func (t *SymbolsTable) SymbolizeLabels(lbls []string, buf []uint32) []uint32 {
result := buf[:0]
for i := 0; i < len(lbls); i += 2 {
off := t.Symbolize(lbls[i])
result = append(result, off)
off = t.Symbolize(lbls[i+1])
result = append(result, off)
}
return result
}

// Symbols returns computes symbols table to put in e.g. Request.Symbols.
// As per spec, order does not matter.
func (t *SymbolsTable) Symbols() []string {
return t.strings
}

// Reset clears symbols table.
func (t *SymbolsTable) Reset() {
// NOTE: Make sure to keep empty symbol.
t.strings = t.strings[:1]
for k := range t.symbolsMap {
if k == "" {
continue
}
delete(t.symbolsMap, k)
}
}

// DesymbolizeLabels decodes label references, with given symbols to labels.
func DesymbolizeLabels(labelRefs []uint32, symbols, buf []string) []string {
result := buf[:0]
for i := 0; i < len(labelRefs); i += 2 {
result = append(result, symbols[labelRefs[i]], symbols[labelRefs[i+1]])
}
return result
}
80 changes: 80 additions & 0 deletions exp/api/remote/genproto/v2/symbols_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (c) Bartłomiej Płotka @bwplotka
// Licensed under the Apache License 2.0.

// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Copyright 2024 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package writev2

import (
"testing"

"github.com/google/go-cmp/cmp"
)

func requireEqual(t testing.TB, expected, got any) {
if diff := cmp.Diff(expected, got); diff != "" {
t.Fatal(diff)
}
}

func TestSymbolsTable(t *testing.T) {
s := NewSymbolTable()
requireEqual(t, []string{""}, s.Symbols())
requireEqual(t, uint32(0), s.Symbolize(""))
requireEqual(t, []string{""}, s.Symbols())

requireEqual(t, uint32(1), s.Symbolize("abc"))
requireEqual(t, []string{"", "abc"}, s.Symbols())

requireEqual(t, uint32(2), s.Symbolize("__name__"))
requireEqual(t, []string{"", "abc", "__name__"}, s.Symbols())

requireEqual(t, uint32(3), s.Symbolize("foo"))
requireEqual(t, []string{"", "abc", "__name__", "foo"}, s.Symbols())

s.Reset()
requireEqual(t, []string{""}, s.Symbols())
requireEqual(t, uint32(0), s.Symbolize(""))

requireEqual(t, uint32(1), s.Symbolize("__name__"))
requireEqual(t, []string{"", "__name__"}, s.Symbols())

requireEqual(t, uint32(2), s.Symbolize("abc"))
requireEqual(t, []string{"", "__name__", "abc"}, s.Symbols())

ls := []string{"__name__", "qwer", "zxcv", "1234"}
encoded := s.SymbolizeLabels(ls, nil)
requireEqual(t, []uint32{1, 3, 4, 5}, encoded)
decoded := DesymbolizeLabels(encoded, s.Symbols(), nil)
requireEqual(t, ls, decoded)

// Different buf.
ls = []string{"__name__", "qwer", "zxcv2222", "1234"}
encoded = s.SymbolizeLabels(ls, []uint32{1, 3, 4, 5})
requireEqual(t, []uint32{1, 3, 6, 5}, encoded)
}
Loading