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

Expose current Prometheus config via /status/config #2711

Merged
merged 1 commit into from
Aug 14, 2017

Conversation

mxinden
Copy link
Member

@mxinden mxinden commented May 11, 2017

This PR adds the /status/config endpoint which exposes the currently
loaded Prometheus config. This is the same config that is displayed on
/config in the UI. The response payload looks like such:

{
  "status": "success",
  "data": {
    "yaml": "<CONFIG>"
  }
}

Related to PR #2600 and issue #2467

@@ -103,17 +103,21 @@ type API struct {
targetRetriever targetRetriever
alertmanagerRetriever alertmanagerRetriever

now func() model.Time
now func() model.Time
retrieveConfigString configStringFunc
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just call it config and make it a plain func() string. Not everything needs a dedicated type, especially if it's such a one-off thing.

@mxinden
Copy link
Member Author

mxinden commented May 15, 2017

@fabxc Updated PR, renamed to configString like in web/web.go as config is already taken by API endpoint function. This exposes the same string as the UI. Does this need any further security considerations?

@fabxc
Copy link
Contributor

fabxc commented May 15, 2017

#2722 might make us want to reconsider this and switch to properly marshalled configs.

@mxinden
Copy link
Member Author

mxinden commented May 29, 2017

@fabxc As #2775 and #2722 are merged and closed. Can you re-review this PR? Rebased with current master.

@@ -436,6 +440,17 @@ func (api *API) alertmanagers(r *http.Request) (interface{}, *apiError) {
return ams, nil
}

type prometheusConfig struct {
RawFormat string `json:"rawFormat"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if rawFormat is the best name here. I might be wrong, but isn't this the representation without comments, secrets and reformatted by our printer?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed, as this is parsed and marshaled again to yaml, it makes most sense to call this maybe yaml instead

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to YAML and yaml. Thanks for the review!

@brancz
Copy link
Member

brancz commented Jun 22, 2017

lgtm

@fabxc
Copy link
Contributor

fabxc commented Jun 22, 2017

I thought we changed our mind about this since we are no longer preserving the actual text input but actually re-marshal the parsed YAML. Without the original comments there's no real value in providing embedded YAML over JSON here.

@mxinden
Copy link
Member Author

mxinden commented Jun 22, 2017

@fabxc You are right. Sorry about that. Do you want to provide both, or just JSON?

@fabxc
Copy link
Contributor

fabxc commented Jun 22, 2017

No worries. I think any client that wants to show YAML can just implement the conversion themselves, so just JSON seems fine. I'm wondering though, since we have quite a few more things in our status tab in the UI, whether we really want to have a distinct endpoint for each or whether we should just have a single one that has an object with all pieces of data we show on those pages.

The endpoint should be hit very rarely (for health checks we want to implement a dedicated endpoint), so I don't see any performance concerns around that.

@brian-brazil @beorn7

@brian-brazil
Copy link
Contributor

My instinct would be one endpoint for each type. Some of the things we're going on have on the status pages as we grow them will get pretty heavy, and not as dumb as the config file.

@fabxc
Copy link
Contributor

fabxc commented Jun 23, 2017

Okay, fine too.

@mxinden mxinden force-pushed the api-config branch 9 times, most recently from e4f28db to e5bd75c Compare June 23, 2017 17:13
@mxinden
Copy link
Member Author

mxinden commented Jun 23, 2017

@fabxc @brian-brazil I have added the following changes:

  1. Expose JSON instead of YAML in api/v1/status/config
  2. Clean up Handler struct. Removing configString and externalLabels
  3. Elide secret in JSON marsheling
  4. Add test for secret hiding in JSON marsheling
  5. Add JSON format string to Config struct and all sub structs

Let me know what you think. Make sure to also review config/config.go even though it is hidden due to the size of the diff.

@brian-brazil
Copy link
Contributor

Elide secret in JSON marsheling

Duplicating this logic is risky security wise, especially as features created here will be copied by other repos. I think we should stick with one format.

@mxinden
Copy link
Member Author

mxinden commented Jun 26, 2017

@brian-brazil You mean either changing the front-end to JSON or exposing YAML via the API endpoint?

@brian-brazil
Copy link
Contributor

I'd go for the latter. Mixing JSON and YAML would cause confusion I expect.

@mxinden
Copy link
Member Author

mxinden commented Jun 26, 2017

@brian-brazil I think exposing YAML in a pure JSON api is not a great user experience.
I do see the risk of having the elide secret logic twice in our code.

One workaround would be to first marshal the config as YAML, which would elide all secrets, and then convert YAML to JSON which is then exposed via the API. It turns out to be quite costly to implement that from scratch, so I would prefer using something like https://github.com/ghodss/yaml.

Second alternative would be to switch the Prometheus config.go entirely to JSON and use the library above to convert incoming YAML to JSON. This sounds a lot cleaner to me.

What do you think?

@fabxc
Copy link
Contributor

fabxc commented Jun 26, 2017

@brian-brazil more precisely, the YAML unmarshaller creates map[interface{}]interface{} for nested objects where encoding/json only can deal with map[string]interface{}. The conversion to work around this is here.

@brian-brazil
Copy link
Contributor

I think exposing YAML in a pure JSON api is not a great user experience.

I don't think there's a perfect solution here. I'd view it more as an opaque config string (which happens to be in YAML), being returned via an RPC mechanism (which happens to be JSON).

@fabxc
Copy link
Contributor

fabxc commented Jun 26, 2017

The config is structured data. What's the point of exposing it as an opaque string?

@brian-brazil
Copy link
Contributor

If the goal is to view the current configuration (as we currently have on the status page), then that's sufficient. I'm not imagining too much processing of the config beyond checking that the right config is loaded.

@mxinden mxinden mentioned this pull request Jul 14, 2017
@mxinden
Copy link
Member Author

mxinden commented Jul 14, 2017

@fabxc @brian-brazil I still find it inconsistent to (only) expose YAML on a JSON API. E.g. for the Prometheus Operator we would like to process the returned config.

Would one of these two options work:

  1. Adding more tests to make sure no secrets are leaked. (Does not cover copies of other repos)
  2. Using https://github.com/ghodss/yaml to convert the YAML with elided secrets to JSON.

@brian-brazil
Copy link
Contributor

I still find it inconsistent to (only) expose YAML on a JSON API.

That's kinda like arguing that if you're returning HTML over a JSON API you should convert its XML structure to JSON first. I don't see an inconsistency here, what serialisation protocol you're using for RPCs shouldn't automatically affect what data you're transporting.

@fabxc
Copy link
Contributor

fabxc commented Jul 14, 2017 via email

@fabxc
Copy link
Contributor

fabxc commented Jul 14, 2017 via email

@brian-brazil
Copy link
Contributor

brian-brazil commented Jul 14, 2017

When we've the proto/GRPC variant of this API, would you have that also return JSON or should it be a proto version of the config?

@fabxc
Copy link
Contributor

fabxc commented Jul 14, 2017 via email

@brian-brazil
Copy link
Contributor

You're internally consistent anyway, but that'd be a silly amount of maintenance effort as you'd have to keep a separate copy of the config structure in proto to get all the field numbers right - and that'd also not be forwards compatible for any new fields added.

I don't think changing the packet format to match that of the frame is at all sane.

@brian-brazil
Copy link
Contributor

Looking at this from another direction, what would be the objection to expecting users of this to parse the YAML on their end? What are the use cases for doing so?

Keep in mind that it is our goal ultimately to have comments back in the marshalled config, we just can't do that currently due to secrets.

@mxinden mxinden force-pushed the api-config branch 2 times, most recently from 732434d to e5d1824 Compare August 7, 2017 21:20
@mxinden
Copy link
Member Author

mxinden commented Aug 7, 2017

I have updated the PR to expose the config in YAML format.
@fabxc @brian-brazil @brancz Let me know if this looks good to you. Very happy for any further feedback.

Just in case we ever want to expose the config as JSON, instead of writing the JSON format strings all over again, they can be found here: https://github.com/mxinden/prometheus/tree/api-config-json-backup

@brian-brazil
Copy link
Contributor

👍

You have conflicts.

@mxinden
Copy link
Member Author

mxinden commented Aug 7, 2017

@brian-brazil Thanks for the hint. Rebased.

@@ -74,7 +74,7 @@ func (h *Handler) federation(w http.ResponseWriter, req *http.Request) {
}
sort.Sort(byName(vector))

externalLabels := h.externalLabels.Clone()
externalLabels := h.config.GlobalConfig.ExternalLabels.Clone()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is intended to be part of this PR right? I think something may have gone wrong when rebasing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brancz Instead of having configString and externalLabels in the Handler struct, I propose with this PR just having the config itself in there. Whenever we need externalLabels and configString we can get it from Handler.config.String() and Handler.config.GlobalConfig.ExternalLabels.

I find this cleaner. In addition it enables us to access more than just the external labels and the config string in both web.go and api.go in the future. @brancz What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha, fine with it, just wanted to make sure this was intentional as you just rebased.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brancz Appreciate the mindfulness!

@brancz
Copy link
Member

brancz commented Aug 8, 2017

lgtm 👍

@mxinden
Copy link
Member Author

mxinden commented Aug 9, 2017

I am not a member of the Prometheus GitHub organization. Thereby I am not able to merge here.

@brancz @fabxc @brian-brazil In case you have no further objections, could you merge this PR?

@brian-brazil
Copy link
Contributor

Hmm, you're on -team so you should be in the org. Invite sent.

@mxinden
Copy link
Member Author

mxinden commented Aug 13, 2017

@brian-brazil Thanks for the invitation!

@brancz and @brian-brazil I had to do one more fix here: YAML string 'yaml:"yaml"' -> YAML string 'json:"yaml"' . Please merge in case you are fine with all of this.

Copy link
Contributor

@brian-brazil brian-brazil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@@ -540,15 +540,27 @@ func TestLoadConfig(t *testing.T) {
if !reflect.DeepEqual(c, expectedConf) {
t.Fatalf("%s: unexpected config result: \n\n%s\n expected\n\n%s", "testdata/conf.good.yml", bgot, bexp)
}
}

// YAML marsheling must not reveal authentication credentials.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

marshalling

This PR adds the `/status/config` endpoint which exposes the currently
loaded Prometheus config. This is the same config that is displayed on
`/config` in the UI in YAML format. The response payload looks like
such:
```
{
  "status": "success",
  "data": {
    "yaml": <CONFIG>
  }
}
```
@mxinden mxinden merged commit 3101606 into prometheus:master Aug 14, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants