mirror of
https://github.com/prymitive/karma
synced 2026-05-05 03:16:51 +00:00
258 lines
7.8 KiB
Go
258 lines
7.8 KiB
Go
package main
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/prymitive/karma/internal/alertmanager"
|
|
|
|
httpmock "gopkg.in/jarcoal/httpmock.v1"
|
|
)
|
|
|
|
// httptest.NewRecorder() doesn't implement http.CloseNotifier
|
|
type closeNotifyingRecorder struct {
|
|
*httptest.ResponseRecorder
|
|
closed chan bool
|
|
}
|
|
|
|
func newCloseNotifyingRecorder() *closeNotifyingRecorder {
|
|
return &closeNotifyingRecorder{
|
|
httptest.NewRecorder(),
|
|
make(chan bool, 1),
|
|
}
|
|
}
|
|
|
|
func (c *closeNotifyingRecorder) CloseNotify() <-chan bool {
|
|
return c.closed
|
|
}
|
|
|
|
type proxyTest struct {
|
|
method string
|
|
localPath string
|
|
upstreamURI string
|
|
code int
|
|
response string
|
|
}
|
|
|
|
var proxyTests = []proxyTest{
|
|
// valid alertmanager and methods
|
|
{
|
|
method: "POST",
|
|
localPath: "/proxy/alertmanager/dummy/api/v1/silences",
|
|
upstreamURI: "http://localhost:9093/api/v1/silences",
|
|
code: 200,
|
|
response: "{\"status\":\"success\",\"data\":{\"silenceId\":\"d8a61ca8-ee2e-4076-999f-276f1e986bf3\"}}",
|
|
},
|
|
{
|
|
method: "DELETE",
|
|
localPath: "/proxy/alertmanager/dummy/api/v1/silence/d8a61ca8-ee2e-4076-999f-276f1e986bf3",
|
|
upstreamURI: "http://localhost:9093/api/v1/silence/d8a61ca8-ee2e-4076-999f-276f1e986bf3",
|
|
code: 200,
|
|
response: "{\"status\":\"success\"}",
|
|
},
|
|
// invalid alertmanager name
|
|
{
|
|
method: "POST",
|
|
localPath: "/proxy/alertmanager/INVALID/api/v1/silences",
|
|
upstreamURI: "",
|
|
code: 404,
|
|
response: "404 page not found",
|
|
},
|
|
{
|
|
method: "DELETE",
|
|
localPath: "/proxy/alertmanager/INVALID/api/v1/silence/d8a61ca8-ee2e-4076-999f-276f1e986bf3",
|
|
upstreamURI: "http://localhost:9093/api/v1/silence/d8a61ca8-ee2e-4076-999f-276f1e986bf3",
|
|
code: 404,
|
|
response: "404 page not found",
|
|
},
|
|
// valid alertmanager name, but invalid method
|
|
{
|
|
method: "GET",
|
|
localPath: "/proxy/alertmanager/dummy/api/v1/silences",
|
|
upstreamURI: "",
|
|
code: 404,
|
|
response: "404 page not found",
|
|
},
|
|
{
|
|
method: "GET",
|
|
localPath: "/proxy/alertmanager/dummy/api/v1/silence/d8a61ca8-ee2e-4076-999f-276f1e986bf3",
|
|
upstreamURI: "http://localhost:9093/api/v1/silence/d8a61ca8-ee2e-4076-999f-276f1e986bf3",
|
|
code: 404,
|
|
response: "404 page not found",
|
|
},
|
|
}
|
|
|
|
func TestProxy(t *testing.T) {
|
|
r := ginTestEngine()
|
|
am, err := alertmanager.NewAlertmanager(
|
|
"dummy",
|
|
"http://localhost:9093",
|
|
alertmanager.WithRequestTimeout(time.Second*5),
|
|
alertmanager.WithProxy(true),
|
|
)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
err = setupRouterProxyHandlers(r, am)
|
|
if err != nil {
|
|
t.Errorf("Failed to setup proxy for Alertmanager %s: %s", am.Name, err)
|
|
}
|
|
|
|
httpmock.Activate()
|
|
defer httpmock.DeactivateAndReset()
|
|
|
|
for _, testCase := range proxyTests {
|
|
httpmock.Reset()
|
|
if testCase.upstreamURI != "" {
|
|
httpmock.RegisterResponder(testCase.method, testCase.upstreamURI, httpmock.NewStringResponder(testCase.code, testCase.response))
|
|
}
|
|
req := httptest.NewRequest(testCase.method, testCase.localPath, nil)
|
|
resp := newCloseNotifyingRecorder()
|
|
r.ServeHTTP(resp, req)
|
|
if resp.Code != testCase.code {
|
|
t.Errorf("%s %s proxied to %s returned status %d while %d was expected",
|
|
testCase.method, testCase.localPath, testCase.upstreamURI, resp.Code, testCase.code)
|
|
}
|
|
body := resp.Body.String()
|
|
if body != testCase.response {
|
|
t.Errorf("%s %s proxied to %s returned content '%s' while '%s' was expected",
|
|
testCase.method, testCase.localPath, testCase.upstreamURI, body, testCase.response)
|
|
}
|
|
}
|
|
}
|
|
|
|
type proxyHeaderTest struct {
|
|
method string
|
|
localPath string
|
|
upstreamURI string
|
|
code int
|
|
alertmanagerURI string
|
|
alertmanagerHost string
|
|
authUser string
|
|
authPass string
|
|
}
|
|
|
|
var proxyHeaderTests = []proxyHeaderTest{
|
|
{
|
|
method: "POST",
|
|
localPath: "/proxy/alertmanager/dummy/api/v1/silences",
|
|
upstreamURI: "http://localhost:9093/api/v1/silences",
|
|
code: 200,
|
|
alertmanagerURI: "http://localhost:9093",
|
|
alertmanagerHost: "localhost:9093",
|
|
},
|
|
{
|
|
method: "POST",
|
|
localPath: "/proxy/alertmanager/dummy/api/v1/silences",
|
|
upstreamURI: "http://alertmanager.example.com/api/v1/silences",
|
|
code: 200,
|
|
alertmanagerURI: "http://alertmanager.example.com",
|
|
alertmanagerHost: "alertmanager.example.com",
|
|
},
|
|
{
|
|
method: "POST",
|
|
localPath: "/proxy/alertmanager/dummy/api/v1/silences",
|
|
upstreamURI: "http://alertmanager.example.com/api/v1/silences",
|
|
code: 200,
|
|
alertmanagerURI: "http://foo:bar@alertmanager.example.com",
|
|
alertmanagerHost: "alertmanager.example.com",
|
|
authUser: "foo",
|
|
authPass: "bar",
|
|
},
|
|
{
|
|
method: "POST",
|
|
localPath: "/proxy/alertmanager/dummy/api/v1/silences",
|
|
upstreamURI: "http://alertmanager.example.com/api/v1/silences",
|
|
code: 200,
|
|
alertmanagerURI: "http://foo@alertmanager.example.com",
|
|
alertmanagerHost: "alertmanager.example.com",
|
|
authUser: "foo",
|
|
authPass: "",
|
|
},
|
|
}
|
|
|
|
func TestProxyHeaders(t *testing.T) {
|
|
httpmock.Activate()
|
|
defer httpmock.DeactivateAndReset()
|
|
|
|
for _, testCase := range proxyHeaderTests {
|
|
r := ginTestEngine()
|
|
am, err := alertmanager.NewAlertmanager(
|
|
"dummy",
|
|
testCase.alertmanagerURI,
|
|
alertmanager.WithRequestTimeout(time.Second*5),
|
|
alertmanager.WithProxy(true),
|
|
)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
err = setupRouterProxyHandlers(r, am)
|
|
if err != nil {
|
|
t.Errorf("Failed to setup proxy for Alertmanager %s: %s", am.Name, err)
|
|
}
|
|
|
|
httpmock.Reset()
|
|
httpmock.RegisterResponder(testCase.method, testCase.upstreamURI, func(req *http.Request) (*http.Response, error) {
|
|
if req.Host != testCase.alertmanagerHost {
|
|
t.Errorf("req.Host is '%s' while '%s' was expected", req.Host, testCase.alertmanagerHost)
|
|
}
|
|
if req.Header.Get("Host") != "" {
|
|
t.Errorf("req.Header.Host is '%s' while '%s' was expected", req.Header.Get("Host"), testCase.alertmanagerHost)
|
|
}
|
|
if testCase.authUser != "" || testCase.authPass != "" {
|
|
user, password, _ := req.BasicAuth()
|
|
if testCase.authUser != "" && testCase.authUser != user {
|
|
t.Errorf("%s %s proxied to %s was expected to have Basic Auth user '%s', got '%s'",
|
|
testCase.method, testCase.localPath, testCase.upstreamURI, testCase.authUser, user)
|
|
}
|
|
if testCase.authPass != "" && testCase.authPass != password {
|
|
t.Errorf("%s %s proxied to %s was expected to have Basic Auth password '%s', got '%s'",
|
|
testCase.method, testCase.localPath, testCase.upstreamURI, testCase.authPass, password)
|
|
}
|
|
}
|
|
return httpmock.NewStringResponse(testCase.code, "ok"), nil
|
|
})
|
|
|
|
req := httptest.NewRequest(testCase.method, testCase.localPath, nil)
|
|
resp := newCloseNotifyingRecorder()
|
|
r.ServeHTTP(resp, req)
|
|
if resp.Code != testCase.code {
|
|
t.Errorf("%s %s proxied to %s returned status %d while %d was expected",
|
|
testCase.method, testCase.localPath, testCase.upstreamURI, resp.Code, testCase.code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestProxyToSubURIAlertmanager(t *testing.T) {
|
|
httpmock.Activate()
|
|
defer httpmock.DeactivateAndReset()
|
|
|
|
r := ginTestEngine()
|
|
am, err := alertmanager.NewAlertmanager(
|
|
"suburi",
|
|
"http://alertmanager.example.com/suburi",
|
|
alertmanager.WithRequestTimeout(time.Second*5),
|
|
alertmanager.WithProxy(true),
|
|
)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
err = setupRouterProxyHandlers(r, am)
|
|
if err != nil {
|
|
t.Errorf("Failed to setup proxy for Alertmanager %s: %s", am.Name, err)
|
|
}
|
|
|
|
httpmock.RegisterResponder("POST", "http://alertmanager.example.com/suburi/api/v1/silences", func(req *http.Request) (*http.Response, error) {
|
|
return httpmock.NewStringResponse(200, "ok"), nil
|
|
})
|
|
|
|
req := httptest.NewRequest("POST", "/proxy/alertmanager/suburi/api/v1/silences", nil)
|
|
resp := newCloseNotifyingRecorder()
|
|
r.ServeHTTP(resp, req)
|
|
if resp.Code != 200 {
|
|
t.Errorf("Got response code %d instead of 200", resp.Code)
|
|
}
|
|
}
|