Skip to content
This repository was archived by the owner on Jul 22, 2020. It is now read-only.

Add support for proxying user connection to Alertmanager - #202

Merged
prymitive merged 8 commits into
masterfrom
proxy
Jan 9, 2018
Merged

Add support for proxying user connection to Alertmanager#202
prymitive merged 8 commits into
masterfrom
proxy

Conversation

@prymitive

Copy link
Copy Markdown
Contributor

Fixes #190.

With this feature unsee can be configured to proxy requests to selected Alertmanager instances, if it's enabled unsee silence form will send a request via unsee rather than directly. This allows users to manage silences in environments where they have access to unsee but not to Alertmanager. Only silences endpoints on Alertmanager API are proxied.

Comment thread proxy.go Outdated
log "github.com/sirupsen/logrus"
)

func newProxyServer(domain string) *httputil.ReverseProxy {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You may wish to take an http.RoundTripper (to set as ReverseProxy.Transport) as an option here so you can observe and influence the behavior of the proxy in test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not sure if that's really needed for tests, I got some now and didn't need it for that

Comment thread proxy.go Outdated
} else if !upstream.ProxyRequests {
c.String(http.StatusBadRequest, "Alertmanager instance named '%s' does not allow proxying\n", name)
} else {
proxy := newProxyServer(upstream.URI)

@terinjokes terinjokes Dec 8, 2017

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You probably don't want to create a whole new proxy for each request. An alternative architecture could rely on the fact that ReverseProxy satisfies the http.Handler interface, and that you can add an http.Handler to a Gin router with gin.WrapH(http.Handler) gin.HandlerFunc. Then you can direct the two routes to the upstream by implementing ReverseProxy.Director.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated, is this better? I also got some tests this time

@prymitive
prymitive force-pushed the proxy branch 2 times, most recently from 27ac5e8 to ccfa04f Compare December 23, 2017 11:58
@prymitive prymitive removed this from the v0.9 milestone Dec 29, 2017
Fixes #190.

With this feature unsee can be configured to proxy requests to selected Alertmanager instances, if it's enabled unsee silence form will send a request via unsee rather than directly. This allows users to manage silences in environments where they have access to unsee but not to Alertmanager. Only silences endpoints on Alertmanager API are proxied.

@terinjokes terinjokes left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Looking better, but I think there's room for some improvements!

Comment thread proxy.go Outdated
if err != nil {
return err
}
router.POST(fmt.Sprintf("%s/api/v1/silences", proxyPathPrefix(alertmanager.Name)), gin.WrapH(proxy))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is this not clearer without nesting Sprintfs?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's not very readable, how about now?

Comment thread proxy.go Outdated
Director: func(req *http.Request) {
req.URL.Scheme = upstreamURL.Scheme
req.URL.Host = upstreamURL.Host
req.URL.Path = strings.TrimPrefix(req.URL.Path, proxyPathPrefix(alertmanager.Name))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Use net/http.StripPrefix instead.

Comment thread proxy.go
// upstream, there's a gzip middleware that's global so we don't want it
// to gzip twice
req.Header.Del("Accept-Encoding")
log.Debugf("[%s] Proxy request for %s", alertmanager.Name, req.URL.Path)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

It is possible to accept a logger?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't see how that would be useful right now

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Mostly useful for configuring the logging for proxy specifically. But mostly a nit.

Comment thread proxy_test.go Outdated

var proxyTests = []proxyTest{
// valid alertmanager and methods
proxyTest{

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

struct names are unneeded inside a slice.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

TIL

Comment thread proxy_test.go
)

// httptest.NewRecorder() doesn't implement http.CloseNotifier
type closeNotifyingRecorder struct {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I don't see you using the CloseNotify method. Is this recorder necessary?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm not 100% sure as I didn't go that deep into the issue, but testes were failing because of it and after some research I wasn't sure if this is gin issue or httptest

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I don't have time to research myself, but if you eventually figure it out, I'd like to know.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Is that blocking this merge?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No.

Comment thread internal/alertmanager/upstream.go Outdated

// NewAlertmanager creates a new Alertmanager instance
func NewAlertmanager(name, uri string, timeout time.Duration) error {
func NewAlertmanager(name, uri string, timeout time.Duration, proxyRequests bool) error {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I'd suggest using the functional options pattern instead. While reviewing consumer code, it's not clear what the lonesome false means.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Comment thread internal/alertmanager/upstream.go Outdated
// a custom timeout for Alertmanager upstream requests
func WithRequestTimeout(timeout time.Duration) Option {
return func(am *Alertmanager) {
am.Timeout = timeout

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Any reason not to change the struct member name as well? Any reason to set a request timeout rather than using a context.WithTimeout?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

  1. None, renamed
  2. I know about context but didn't really use it for anything so lack any experience and obvious patterns I could use it for

@cloudflare cloudflare deleted a comment from prymitive Jan 8, 2018

@terinjokes terinjokes left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Assuming the CI tests pass, I think this is reasonable to merge. See my note about a good follow-up PR.

Comment thread internal/alertmanager/upstream.go Outdated

// NewAlertmanager creates a new Alertmanager instance
func NewAlertmanager(name, uri string, timeout time.Duration) error {
func NewAlertmanager(name, uri string, opts ...Option) error {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit: I'd argue this should be AddAlertmanager because while you do allocate you don't actually get the Alertmanager instance back.

For testing reasons (you're creating the Alertmanger manually in test) perhaps it should, and leave it up to the consumer to add it to the upstreams slice/type. I'll accept this as a separate PR though.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I'm refactoring this

@prymitive

Copy link
Copy Markdown
Contributor Author

If it doesn't passed I can't merge it anyway. 983c7f5 should address last comment

log.Infof("[%s] Configured Alertmanager source at %s", name, uri)
// RegisterAlertmanager will add an Alertmanager instance to the list of
// instances used when pulling alerts from upstreams
func RegisterAlertmanager(am *Alertmanager) error {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Would be great if this was on a collection type, rather than modifying a global variable. Again, happy to see it as another change.

@prymitive
prymitive merged commit a3af1ec into master Jan 9, 2018
@prymitive
prymitive deleted the proxy branch January 9, 2018 00:23
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants