-
Notifications
You must be signed in to change notification settings - Fork 330
Decoder: Remove use of global name validation and add validation #808
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for jumping into it!
Let's fix proto + escaping, thanks for tackling this, it's important.
However, given we had some issues/bugs recently and generally it's not trivial to develop with common because many stable libs and projects use this, let's perhaps:
- Make separate PRs for separate fixes so it's easier to manager and ensure we didn't miss anything.
- Be careful with breaking changes, this PR proposes 2 breaking changes I believe we could avoid.
- Don't try to avoid introducing logic that was not asked for (unless I don't have context) -- ideally we keep the maintenance surface minimal
Hope that makes sense 🙈 WDYT?
expfmt/decode.go
Outdated
@@ -72,12 +74,17 @@ func ResponseFormat(h http.Header) Format { | |||
|
|||
// NewDecoder returns a new decoder based on the given input format. | |||
// If the input format does not imply otherwise, a text format decoder is returned. | |||
func NewDecoder(r io.Reader, format Format) Decoder { | |||
func NewDecoder(r io.Reader, format Format) (Decoder, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is scary, let's be careful here, ideally, in common, we don't break API and if we do we do this is, we do this in a dedicated PR and with tests 😱
It's a signature and (hidden) logic breaking change on a popular API that will affect thousands indirectly of downstream projects and libraries. Signature because the new error handling (1), and hidden logic (2) breaking change, because for OpenMetrics format we suddenly return error and not attempt text like we did in the past. For majority of the existing users. the previous implementation, while broken, was "predictably" broken and we don't hear input to change it now, do we?
For (2), are we sure that no OpenMetrics attempt of parsing with text parser worked? I'm not, I believe for some basic metrics it could work just fine and users depended on it. Instead we should probably, long term should add automatic OM decoding here (no urgency though - users should use textparse from Prometheus ideally).
For (1), we don't need error handling if we skip (2) change. Another idea would be to create errDecoder
and error on Decode which is what OpenMetrics input would cause to text parse in some cases. Still this would cause (2) breaking change, so I would just keep NewDecoder
as is and only try to add safer parsers ONLY if there's strong use case.
If we really need safe decoder factory, we could add new NewSafeDecoder(io.Reader, Format) (Decoder, error)
, but then ensure default (unknown Format) returns error too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@beorn7 looked at Prometheus, and it does not use the common library to do parsing. I doubt anyone actually uses this decoder for openmetrics because it is broken for unknown/untyped metrics. I would have to look closer but I would bet it also doesn't support other openmetrics idioms like different quoting characters.
I think if we don't change the return signature, we should add a clear warning in the docstring that the decoder does not support openmetrics.
expfmt/encode.go
Outdated
@@ -153,7 +153,7 @@ func NewEncoder(w io.Writer, format Format, options ...EncoderOption) Encoder { | |||
case TypeProtoDelim: | |||
return encoderCloser{ | |||
encode: func(v *dto.MetricFamily) error { | |||
_, err := protodelim.MarshalTo(w, v) | |||
_, err := protodelim.MarshalTo(w, model.EscapeMetricFamily(v, escapingScheme)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's the main important bug fix here and it deserves as separate PR please 🙏🏽 🙈
expfmt/text_parse.go
Outdated
@@ -574,7 +574,14 @@ func (p *TextParser) readingType() stateFn { | |||
if p.readTokenUntilNewline(false); p.err != nil { | |||
return nil // Unexpected end of input. | |||
} | |||
metricType, ok := dto.MetricType_value[strings.ToUpper(p.currentToken.String())] | |||
typString := strings.ToUpper(p.currentToken.String()) | |||
// OpenMetrics uses UNKNOWN for untyped metrics instead of UNTYPED, so we add |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, this PR was erroring when somebody tries to use OM with text parse, but we still try to handle OM here? 🤔 What am I missing? Also a candidate for a separate PR that enables OM decoding, if we want
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was a bit of a stupid hack to try to return a better error message when someone does try to do this. I do agree it's in conflict with refusing to decode openmetrics
I am currently blocked on the issue of "delimited protos now follow the escaping scheme" and have opened a smaller PR that addresses only that issue here: #809 |
@bwplotka thanks for reviewing this! I agree this PR does too many things and for now we should focus on fixing the escaping bug. But I also think we might want to sit down and decide what the common library should be expected to do: if it can generate output, and has a decoder, should it not support reading that output? I believe we will find more bugs because right now the output is barely tested. |
Notes from slack discussion:
|
@@ -126,6 +129,7 @@ func (p *TextParser) TextToMetricFamilies(in io.Reader) (map[string]*dto.MetricF | |||
|
|||
func (p *TextParser) reset(in io.Reader) { | |||
p.metricFamiliesByName = map[string]*dto.MetricFamily{} | |||
p.currentLabelPairs = nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the fact that this member was not reset does not bode well for the correctness of the whole parser
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. It also proves it's not heavily used (:
p.currentLabelPairs = nil | ||
return nil | ||
} | ||
if !p.scheme.IsValidLabelName(p.currentLabelPair.GetName()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these checks were not done at all previously
e34a757
to
d3a8808
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some initial comments, thanks for improving this!
reworded / renamed again, because this PR keeps morphing. I think it still does something useful? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!!! LGTM, one nit only.
@@ -126,6 +129,7 @@ func (p *TextParser) TextToMetricFamilies(in io.Reader) (map[string]*dto.MetricF | |||
|
|||
func (p *TextParser) reset(in io.Reader) { | |||
p.metricFamiliesByName = map[string]*dto.MetricFamily{} | |||
p.currentLabelPairs = nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. It also proves it's not heavily used (:
b6fe849
to
6f7f949
Compare
This change removes the use of the global variable to determine name validation, using the provided format instead. Also: * adds validity checks to the text parser, which previously did not check for name validity. * Returns error if the caller tries to create a decoder for prototext format, which is not supported. Signed-off-by: Owen Williams <[email protected]>
6f7f949
to
a683105
Compare
This change removes the use of the global variable to determine name validation, using the provided format instead.
Also:
Signed-off-by: Owen Williams [email protected]