prompb: Generate client proto using Opaque API, for parsing only.

Signed-off-by: bwplotka <bwplotka@gmail.com>
This commit is contained in:
bwplotka 2024-12-29 16:25:40 +00:00
parent 281306765e
commit e7bde7a5dc
7 changed files with 1540 additions and 4079 deletions

View file

@ -23,17 +23,18 @@ import (
"strconv"
"strings"
"sync"
"time"
"unicode/utf8"
"github.com/gogo/protobuf/proto"
"github.com/gogo/protobuf/types"
"github.com/prometheus/common/model"
"google.golang.org/protobuf/encoding/protowire"
"google.golang.org/protobuf/proto"
"github.com/prometheus/prometheus/model/exemplar"
"github.com/prometheus/prometheus/model/histogram"
"github.com/prometheus/prometheus/model/labels"
dto "github.com/prometheus/prometheus/prompb/io/prometheus/client"
prompbdto "github.com/prometheus/prometheus/prompb/io/prometheus/client"
)
// floatFormatBufPool is exclusively used in formatOpenMetricsFloat.
@ -80,7 +81,7 @@ type ProtobufParser struct {
builder labels.ScratchBuilder // held here to reduce allocations when building Labels
mf *dto.MetricFamily
mf *prompbdto.MetricFamily
// Whether to also parse a classic histogram that is also present as a
// native histogram.
@ -95,7 +96,7 @@ func NewProtobufParser(b []byte, parseClassicHistograms bool, st *labels.SymbolT
return &ProtobufParser{
in: b,
state: EntryInvalid,
mf: &dto.MetricFamily{},
mf: &prompbdto.MetricFamily{},
metricBytes: &bytes.Buffer{},
parseClassicHistograms: parseClassicHistograms,
builder: labels.NewScratchBuilderWithSymbolTable(st, 16),
@ -111,13 +112,13 @@ func (p *ProtobufParser) Series() ([]byte, *int64, float64) {
v float64
)
switch p.mf.GetType() {
case dto.MetricType_COUNTER:
case prompbdto.MetricType_COUNTER:
v = m.GetCounter().GetValue()
case dto.MetricType_GAUGE:
case prompbdto.MetricType_GAUGE:
v = m.GetGauge().GetValue()
case dto.MetricType_UNTYPED:
case prompbdto.MetricType_UNTYPED:
v = m.GetUntyped().GetValue()
case dto.MetricType_SUMMARY:
case prompbdto.MetricType_SUMMARY:
s := m.GetSummary()
switch p.fieldPos {
case -2:
@ -131,7 +132,7 @@ func (p *ProtobufParser) Series() ([]byte, *int64, float64) {
default:
v = s.GetQuantile()[p.fieldPos].GetValue()
}
case dto.MetricType_HISTOGRAM, dto.MetricType_GAUGE_HISTOGRAM:
case prompbdto.MetricType_HISTOGRAM, prompbdto.MetricType_GAUGE_HISTOGRAM:
// This should only happen for a classic histogram.
h := m.GetHistogram()
switch p.fieldPos {
@ -214,7 +215,7 @@ func (p *ProtobufParser) Histogram() ([]byte, *int64, *histogram.Histogram, *his
fh.NegativeSpans[i].Offset = span.GetOffset()
fh.NegativeSpans[i].Length = span.GetLength()
}
if p.mf.GetType() == dto.MetricType_GAUGE_HISTOGRAM {
if p.mf.GetType() == prompbdto.MetricType_GAUGE_HISTOGRAM {
fh.CounterResetHint = histogram.GaugeType
}
fh.Compact(0)
@ -246,7 +247,7 @@ func (p *ProtobufParser) Histogram() ([]byte, *int64, *histogram.Histogram, *his
sh.NegativeSpans[i].Offset = span.GetOffset()
sh.NegativeSpans[i].Length = span.GetLength()
}
if p.mf.GetType() == dto.MetricType_GAUGE_HISTOGRAM {
if p.mf.GetType() == prompbdto.MetricType_GAUGE_HISTOGRAM {
sh.CounterResetHint = histogram.GaugeType
}
sh.Compact(0)
@ -269,15 +270,15 @@ func (p *ProtobufParser) Help() ([]byte, []byte) {
func (p *ProtobufParser) Type() ([]byte, model.MetricType) {
n := p.metricBytes.Bytes()
switch p.mf.GetType() {
case dto.MetricType_COUNTER:
case prompbdto.MetricType_COUNTER:
return n, model.MetricTypeCounter
case dto.MetricType_GAUGE:
case prompbdto.MetricType_GAUGE:
return n, model.MetricTypeGauge
case dto.MetricType_HISTOGRAM:
case prompbdto.MetricType_HISTOGRAM:
return n, model.MetricTypeHistogram
case dto.MetricType_GAUGE_HISTOGRAM:
case prompbdto.MetricType_GAUGE_HISTOGRAM:
return n, model.MetricTypeGaugeHistogram
case dto.MetricType_SUMMARY:
case prompbdto.MetricType_SUMMARY:
return n, model.MetricTypeSummary
}
return n, model.MetricTypeUnknown
@ -327,11 +328,11 @@ func (p *ProtobufParser) Exemplar(ex *exemplar.Exemplar) bool {
return false
}
m := p.mf.GetMetric()[p.metricPos]
var exProto *dto.Exemplar
var exProto *prompbdto.Exemplar
switch p.mf.GetType() {
case dto.MetricType_COUNTER:
case prompbdto.MetricType_COUNTER:
exProto = m.GetCounter().GetExemplar()
case dto.MetricType_HISTOGRAM, dto.MetricType_GAUGE_HISTOGRAM:
case prompbdto.MetricType_HISTOGRAM, prompbdto.MetricType_GAUGE_HISTOGRAM:
isClassic := p.state == EntrySeries
if !isClassic && len(m.GetHistogram().GetExemplars()) > 0 {
exs := m.GetHistogram().GetExemplars()
@ -393,22 +394,17 @@ func (p *ProtobufParser) Exemplar(ex *exemplar.Exemplar) bool {
// CreatedTimestamp returns CT or nil if CT is not present or
// invalid (as timestamp e.g. negative value) on counters, summaries or histograms.
func (p *ProtobufParser) CreatedTimestamp() *int64 {
var ct *types.Timestamp
var ct time.Time
switch p.mf.GetType() {
case dto.MetricType_COUNTER:
ct = p.mf.GetMetric()[p.metricPos].GetCounter().GetCreatedTimestamp()
case dto.MetricType_SUMMARY:
ct = p.mf.GetMetric()[p.metricPos].GetSummary().GetCreatedTimestamp()
case dto.MetricType_HISTOGRAM, dto.MetricType_GAUGE_HISTOGRAM:
ct = p.mf.GetMetric()[p.metricPos].GetHistogram().GetCreatedTimestamp()
case prompbdto.MetricType_COUNTER:
ct = p.mf.GetMetric()[p.metricPos].GetCounter().GetCreatedTimestamp().AsTime()
case prompbdto.MetricType_SUMMARY:
ct = p.mf.GetMetric()[p.metricPos].GetSummary().GetCreatedTimestamp().AsTime()
case prompbdto.MetricType_HISTOGRAM, prompbdto.MetricType_GAUGE_HISTOGRAM:
ct = p.mf.GetMetric()[p.metricPos].GetHistogram().GetCreatedTimestamp().AsTime()
default:
}
ctAsTime, err := types.TimestampFromProto(ct)
if err != nil {
// Errors means ct == nil or invalid timestamp, which we silently ignore.
return nil
}
ctMilis := ctAsTime.UnixMilli()
ctMilis := ct.UnixMilli()
return &ctMilis
}
@ -443,19 +439,19 @@ func (p *ProtobufParser) Next() (Entry, error) {
return EntryInvalid, fmt.Errorf("invalid help for metric %q: %s", name, help)
}
switch p.mf.GetType() {
case dto.MetricType_COUNTER,
dto.MetricType_GAUGE,
dto.MetricType_HISTOGRAM,
dto.MetricType_GAUGE_HISTOGRAM,
dto.MetricType_SUMMARY,
dto.MetricType_UNTYPED:
case prompbdto.MetricType_COUNTER,
prompbdto.MetricType_GAUGE,
prompbdto.MetricType_HISTOGRAM,
prompbdto.MetricType_GAUGE_HISTOGRAM,
prompbdto.MetricType_SUMMARY,
prompbdto.MetricType_UNTYPED:
// All good.
default:
return EntryInvalid, fmt.Errorf("unknown metric type for metric %q: %s", name, p.mf.GetType())
}
unit := p.mf.GetUnit()
if len(unit) > 0 {
if p.mf.GetType() == dto.MetricType_COUNTER && strings.HasSuffix(name, "_total") {
if p.mf.GetType() == prompbdto.MetricType_COUNTER && strings.HasSuffix(name, "_total") {
if !strings.HasSuffix(name[:len(name)-6], unit) || len(name)-6 < len(unit)+1 || name[len(name)-6-len(unit)-1] != '_' {
return EntryInvalid, fmt.Errorf("unit %q not a suffix of counter %q", unit, name)
}
@ -468,7 +464,7 @@ func (p *ProtobufParser) Next() (Entry, error) {
p.state = EntryHelp
case EntryHelp:
if p.mf.Unit != "" {
if p.mf.GetUnit() != "" {
p.state = EntryUnit
} else {
p.state = EntryType
@ -477,7 +473,7 @@ func (p *ProtobufParser) Next() (Entry, error) {
p.state = EntryType
case EntryType:
t := p.mf.GetType()
if (t == dto.MetricType_HISTOGRAM || t == dto.MetricType_GAUGE_HISTOGRAM) &&
if (t == prompbdto.MetricType_HISTOGRAM || t == prompbdto.MetricType_GAUGE_HISTOGRAM) &&
isNativeHistogram(p.mf.GetMetric()[0].GetHistogram()) {
p.state = EntryHistogram
} else {
@ -495,9 +491,9 @@ func (p *ProtobufParser) Next() (Entry, error) {
}
t := p.mf.GetType()
if p.state == EntrySeries && !p.fieldsDone &&
(t == dto.MetricType_SUMMARY ||
t == dto.MetricType_HISTOGRAM ||
t == dto.MetricType_GAUGE_HISTOGRAM) {
(t == prompbdto.MetricType_SUMMARY ||
t == prompbdto.MetricType_HISTOGRAM ||
t == prompbdto.MetricType_GAUGE_HISTOGRAM) {
p.fieldPos++
} else {
p.metricPos++
@ -508,7 +504,7 @@ func (p *ProtobufParser) Next() (Entry, error) {
// histograms, we have to switch back to native
// histograms after parsing a classic histogram.
if p.state == EntrySeries &&
(t == dto.MetricType_HISTOGRAM || t == dto.MetricType_GAUGE_HISTOGRAM) &&
(t == prompbdto.MetricType_HISTOGRAM || t == prompbdto.MetricType_GAUGE_HISTOGRAM) &&
isNativeHistogram(p.mf.GetMetric()[0].GetHistogram()) {
p.state = EntryHistogram
}
@ -558,7 +554,7 @@ func (p *ProtobufParser) updateMetricBytes() error {
// state.
func (p *ProtobufParser) getMagicName() string {
t := p.mf.GetType()
if p.state == EntryHistogram || (t != dto.MetricType_HISTOGRAM && t != dto.MetricType_GAUGE_HISTOGRAM && t != dto.MetricType_SUMMARY) {
if p.state == EntryHistogram || (t != prompbdto.MetricType_HISTOGRAM && t != prompbdto.MetricType_GAUGE_HISTOGRAM && t != prompbdto.MetricType_SUMMARY) {
return p.mf.GetName()
}
if p.fieldPos == -2 {
@ -567,7 +563,7 @@ func (p *ProtobufParser) getMagicName() string {
if p.fieldPos == -1 {
return p.mf.GetName() + "_sum"
}
if t == dto.MetricType_HISTOGRAM || t == dto.MetricType_GAUGE_HISTOGRAM {
if t == prompbdto.MetricType_HISTOGRAM || t == prompbdto.MetricType_GAUGE_HISTOGRAM {
return p.mf.GetName() + "_bucket"
}
return p.mf.GetName()
@ -580,12 +576,12 @@ func (p *ProtobufParser) getMagicLabel() (bool, string, string) {
return false, "", ""
}
switch p.mf.GetType() {
case dto.MetricType_SUMMARY:
case prompbdto.MetricType_SUMMARY:
qq := p.mf.GetMetric()[p.metricPos].GetSummary().GetQuantile()
q := qq[p.fieldPos]
p.fieldsDone = p.fieldPos == len(qq)-1
return true, model.QuantileLabel, formatOpenMetricsFloat(q.GetQuantile())
case dto.MetricType_HISTOGRAM, dto.MetricType_GAUGE_HISTOGRAM:
case prompbdto.MetricType_HISTOGRAM, prompbdto.MetricType_GAUGE_HISTOGRAM:
bb := p.mf.GetMetric()[p.metricPos].GetHistogram().GetBucket()
if p.fieldPos >= len(bb) {
p.fieldsDone = true
@ -605,11 +601,11 @@ var errInvalidVarint = errors.New("protobufparse: invalid varint encountered")
// specific to a MetricFamily, utilizes the more efficient gogo-protobuf
// unmarshaling, and acts on a byte slice directly without any additional
// staging buffers.
func readDelimited(b []byte, mf *dto.MetricFamily) (n int, err error) {
func readDelimited(b []byte, mf *prompbdto.MetricFamily) (n int, err error) {
if len(b) == 0 {
return 0, io.EOF
}
messageLength, varIntLength := proto.DecodeVarint(b)
messageLength, varIntLength := protowire.ConsumeVarint(b)
if varIntLength == 0 || varIntLength > binary.MaxVarintLen32 {
return 0, errInvalidVarint
}
@ -618,7 +614,7 @@ func readDelimited(b []byte, mf *dto.MetricFamily) (n int, err error) {
return 0, fmt.Errorf("protobufparse: insufficient length of buffer, expected at least %d bytes, got %d bytes", totalLength, len(b))
}
mf.Reset()
return totalLength, mf.Unmarshal(b[varIntLength:totalLength])
return totalLength, proto.Unmarshal(b[varIntLength:totalLength], mf)
}
// formatOpenMetricsFloat works like the usual Go string formatting of a float
@ -659,7 +655,7 @@ func formatOpenMetricsFloat(f float64) string {
// zero) to signal that the histogram is meant to be parsed as a native
// histogram. Failing to do so will cause Prometheus to parse it as a classic
// histogram as long as no observations have happened.
func isNativeHistogram(h *dto.Histogram) bool {
func isNativeHistogram(h *prompbdto.Histogram) bool {
return len(h.GetPositiveSpan()) > 0 ||
len(h.GetNegativeSpan()) > 0 ||
h.GetZeroThreshold() > 0 ||

11
prompb/buf.gen.yaml Normal file
View file

@ -0,0 +1,11 @@
version: v2
plugins:
- remote: buf.build/protocolbuffers/go:v1.36.1
out: .
opt:
- Mio/prometheus/client/metrics.proto=io/prometheus/client
- default_api_level=API_OPAQUE
inputs:
- directory: . # Pointing to the module in the buf.yaml.

File diff suppressed because it is too large Load diff

View file

@ -13,15 +13,20 @@
// This is copied and lightly edited from
// github.com/prometheus/client_model/io/prometheus/client/metrics.proto
// and finally converted to proto3 syntax to make it usable for the
// gogo-protobuf approach taken within prometheus/prometheus.
// and finally converted to proto3 syntax. Initially to make it usable for the
// gogo-protobuf approach taken within prometheus/prometheus, but now this copy
// is actually source of the truth for buf registry.
// TODO(bwplotka): Decide if we want to deprecate client_model or move proto and
// buf module publishing there. On top of that we need two generated Go code, one
// for efficient decoding with reusable strings and Opaque API, one designed to
// be used as a dto e.g. in client_golang.
syntax = "proto3";
package io.prometheus.client;
option go_package = "io_prometheus_client";
import "gogoproto/gogo.proto";
import "google/protobuf/timestamp.proto";
message LabelPair {
@ -63,7 +68,7 @@ message Quantile {
message Summary {
uint64 sample_count = 1;
double sample_sum = 2;
repeated Quantile quantile = 3 [(gogoproto.nullable) = false];
repeated Quantile quantile = 3;
google.protobuf.Timestamp created_timestamp = 4;
}
@ -77,7 +82,7 @@ message Histogram {
double sample_count_float = 4; // Overrides sample_count if > 0.
double sample_sum = 2;
// Buckets for the classic histogram.
repeated Bucket bucket = 3 [(gogoproto.nullable) = false]; // Ordered in increasing order of upper_bound, +Inf bucket is optional.
repeated Bucket bucket = 3; // Ordered in increasing order of upper_bound, +Inf bucket is optional.
google.protobuf.Timestamp created_timestamp = 15;
@ -95,7 +100,7 @@ message Histogram {
double zero_count_float = 8; // Overrides sb_zero_count if > 0.
// Negative buckets for the native histogram.
repeated BucketSpan negative_span = 9 [(gogoproto.nullable) = false];
repeated BucketSpan negative_span = 9;
// Use either "negative_delta" or "negative_count", the former for
// regular histograms with integer counts, the latter for float
// histograms.
@ -106,7 +111,7 @@ message Histogram {
// Use a no-op span (offset 0, length 0) for a native histogram without any
// observations yet and with a zero_threshold of 0. Otherwise, it would be
// indistinguishable from a classic histogram.
repeated BucketSpan positive_span = 12 [(gogoproto.nullable) = false];
repeated BucketSpan positive_span = 12;
// Use either "positive_delta" or "positive_count", the former for
// regular histograms with integer counts, the latter for float
// histograms.
@ -136,13 +141,13 @@ message BucketSpan {
}
message Exemplar {
repeated LabelPair label = 1 [(gogoproto.nullable) = false];
repeated LabelPair label = 1;
double value = 2;
google.protobuf.Timestamp timestamp = 3; // OpenMetrics-style.
}
message Metric {
repeated LabelPair label = 1 [(gogoproto.nullable) = false];
repeated LabelPair label = 1;
Gauge gauge = 2;
Counter counter = 3;
Summary summary = 4;
@ -155,6 +160,6 @@ message MetricFamily {
string name = 1;
string help = 2;
MetricType type = 3;
repeated Metric metric = 4 [(gogoproto.nullable) = false];
repeated Metric metric = 4;
string unit = 5;
}

View file

@ -0,0 +1,37 @@
// 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 io_prometheus_client //nolint
import (
"errors"
"google.golang.org/protobuf/reflect/protoreflect"
)
type nopTypeRegistry struct{}
func (nopTypeRegistry) RegisterMessage(protoreflect.MessageType) error { return nil }
func (nopTypeRegistry) RegisterEnum(protoreflect.EnumType) error { return nil }
func (nopTypeRegistry) RegisterExtension(protoreflect.ExtensionType) error { return nil }
type nopFileRegistry struct{}
func (nopFileRegistry) FindFileByPath(string) (protoreflect.FileDescriptor, error) {
return nil, errors.New("nop file registry, shouldn't be used for querying")
}
func (nopFileRegistry) FindDescriptorByName(protoreflect.FullName) (protoreflect.Descriptor, error) {
return nil, errors.New("nop file registry, shouldn't be used for querying")
}
func (nopFileRegistry) RegisterFile(protoreflect.FileDescriptor) error { return nil }

View file

@ -32,7 +32,6 @@ import (
"text/template"
"time"
"github.com/gogo/protobuf/proto"
"github.com/google/go-cmp/cmp"
"github.com/grafana/regexp"
"github.com/prometheus/client_golang/prometheus"
@ -43,6 +42,8 @@ import (
"github.com/prometheus/common/model"
"github.com/prometheus/common/promslog"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/encoding/prototext"
"google.golang.org/protobuf/proto"
"github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/discovery"
@ -2501,12 +2502,11 @@ metric: <
}
}
func textToProto(text string, buf *bytes.Buffer) error {
func textToProto(text string, buf io.Writer) error {
// In case of protobuf, we have to create the binary representation.
pb := &dto.MetricFamily{}
// From text to proto message.
err := proto.UnmarshalText(text, pb)
if err != nil {
if err := prototext.Unmarshal([]byte(text), pb); err != nil {
return err
}
// From proto message to binary protobuf.
@ -4201,20 +4201,7 @@ metric: <
if metricsText.contentType != "" {
w.Header().Set("Content-Type", `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`)
for _, text := range metricsText.text {
buf := &bytes.Buffer{}
// In case of protobuf, we have to create the binary representation.
pb := &dto.MetricFamily{}
// From text to proto message.
require.NoError(t, proto.UnmarshalText(text, pb))
// From proto message to binary protobuf.
protoBuf, err := proto.Marshal(pb)
require.NoError(t, err)
// Write first length, then binary protobuf.
varintBuf := binary.AppendUvarint(nil, uint64(len(protoBuf)))
buf.Write(varintBuf)
buf.Write(protoBuf)
w.Write(buf.Bytes())
require.NoError(t, textToProto(text, w))
}
} else {
for _, text := range metricsText.text {

View file

@ -10,27 +10,39 @@ if ! [[ "$0" =~ "scripts/genproto.sh" ]]; then
exit 255
fi
# TODO(bwplotka): Move to buf, this is not OSS agnostic, likely won't work locally.
if ! [[ $(protoc --version) =~ "3.15.8" ]]; then
echo "could not find protoc 3.15.8, is it installed + in PATH? Consider commenting out this check for local flow"
exit 255
fi
SED=$(which gsed 2>/dev/null || which sed)
# Since we run go install, go mod download, the go.sum will change.
# Make a backup.
cp go.sum go.sum.bak
INSTALL_PKGS="golang.org/x/tools/cmd/goimports github.com/gogo/protobuf/protoc-gen-gogofast github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger"
INSTALL_PKGS="golang.org/x/tools/cmd/goimports github.com/gogo/protobuf/protoc-gen-gogofast github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger github.com/bufbuild/buf/cmd/buf@v1.48.0"
for pkg in ${INSTALL_PKGS}; do
GO111MODULE=on go install "$pkg"
done
# Build client proto via buf.
pushd ./prompb
buf generate --path=./io/prometheus/client
# We have to hack global registry which blocks multiple generated types
# from the same proto file (the same path+file and full name).
${SED} -i.bak -E 's/protoimpl.DescBuilder\{/protoimpl.DescBuilder\{FileRegistry:nopFileRegistry{},/g' ./io/prometheus/client/*.pb.go
${SED} -i.bak -E 's/protoimpl.TypeBuilder\{/protoimpl.TypeBuilder\{TypeRegistry:nopTypeRegistry{},/g' ./io/prometheus/client/*.pb.go
goimports -w ./io/prometheus/client/*.go
rm -f ./io/prometheus/client/*.bak
popd
# TODO(bwplotka): Move write building to buf e.g. once moved out of gogo.
if ! [[ $(protoc --version) =~ "3.15.8" ]]; then
echo "could not find protoc 3.15.8, is it installed + in PATH? Consider commenting out this check for local flow"
exit 255
fi
PROM_ROOT="${PWD}"
PROM_PATH="${PROM_ROOT}/prompb"
GOGOPROTO_ROOT="$(GO111MODULE=on go list -mod=readonly -f '{{ .Dir }}' -m github.com/gogo/protobuf)"
GOGOPROTO_PATH="${GOGOPROTO_ROOT}:${GOGOPROTO_ROOT}/protobuf"
GRPC_GATEWAY_ROOT="$(GO111MODULE=on go list -mod=readonly -f '{{ .Dir }}' -m github.com/grpc-ecosystem/grpc-gateway)"
DIRS="prompb"
echo "generating code"
@ -44,14 +56,11 @@ for dir in ${DIRS}; do
protoc --gogofast_out=plugins=grpc:. -I=. \
-I="${GOGOPROTO_PATH}" \
./io/prometheus/write/v2/*.proto
protoc --gogofast_out=Mgoogle/protobuf/timestamp.proto=github.com/gogo/protobuf/types,paths=source_relative:. -I=. \
-I="${GOGOPROTO_PATH}" \
./io/prometheus/client/*.proto
sed -i.bak -E 's/import _ \"github.com\/gogo\/protobuf\/gogoproto\"//g' *.pb.go
sed -i.bak -E 's/import _ \"google\/protobuf\"//g' *.pb.go
sed -i.bak -E 's/\t_ \"google\/protobuf\"//g' *.pb.go
sed -i.bak -E 's/golang\/protobuf\/descriptor/gogo\/protobuf\/protoc-gen-gogo\/descriptor/g' *.go
sed -i.bak -E 's/golang\/protobuf/gogo\/protobuf/g' *.go
${SED} -i.bak -E 's/import _ \"github.com\/gogo\/protobuf\/gogoproto\"//g' *.pb.go
${SED} -i.bak -E 's/import _ \"google\/protobuf\"//g' *.pb.go
${SED} -i.bak -E 's/\t_ \"google\/protobuf\"//g' *.pb.go
${SED} -i.bak -E 's/golang\/protobuf\/descriptor/gogo\/protobuf\/protoc-gen-gogo\/descriptor/g' *.go
${SED} -i.bak -E 's/golang\/protobuf/gogo\/protobuf/g' *.go
rm -f -- *.bak
goimports -w ./*.go ./io/prometheus/client/*.go
popd