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

Support http.Headers in util functions #3776

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion notify/discord/discord.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
return false, err
}

resp, err := notify.PostJSON(ctx, n.client, url, &payload)
resp, err := notify.PostJSON(ctx, n.client, url, nil, &payload)
if err != nil {
return true, notify.RedactURL(err)
}
Expand Down
4 changes: 2 additions & 2 deletions notify/msteams/msteams.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ type Notifier struct {
client *http.Client
retrier *notify.Retrier
webhookURL *config.SecretURL
postJSONFunc func(ctx context.Context, client *http.Client, url string, body io.Reader) (*http.Response, error)
postJSONFunc func(ctx context.Context, client *http.Client, url string, headers http.Header, body io.Reader) (*http.Response, error)
}

// Message card reference can be found at https://learn.microsoft.com/en-us/outlook/actionable-messages/message-card-reference.
Expand Down Expand Up @@ -141,7 +141,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
return false, err
}

resp, err := n.postJSONFunc(ctx, n.client, url, &payload)
resp, err := n.postJSONFunc(ctx, n.client, url, nil, &payload)
if err != nil {
return true, notify.RedactURL(err)
}
Expand Down
2 changes: 1 addition & 1 deletion notify/msteams/msteams_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ func TestNotifier_Notify_WithReason(t *testing.T) {
)
require.NoError(t, err)

notifier.postJSONFunc = func(ctx context.Context, client *http.Client, url string, body io.Reader) (*http.Response, error) {
notifier.postJSONFunc = func(ctx context.Context, client *http.Client, url string, headers http.Header, body io.Reader) (*http.Response, error) {
resp := httptest.NewRecorder()
resp.WriteString(tt.responseContent)
resp.WriteHeader(tt.statusCode)
Expand Down
4 changes: 2 additions & 2 deletions notify/pagerduty/pagerduty.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ func (n *Notifier) notifyV1(
return false, err
}

resp, err := notify.PostJSON(ctx, n.client, n.apiV1, &encodedMsg)
resp, err := notify.PostJSON(ctx, n.client, n.apiV1, nil, &encodedMsg)
if err != nil {
return true, fmt.Errorf("failed to post message to PagerDuty v1: %w", err)
}
Expand Down Expand Up @@ -290,7 +290,7 @@ func (n *Notifier) notifyV2(
return false, err
}

resp, err := notify.PostJSON(ctx, n.client, n.conf.URL.String(), &encodedMsg)
resp, err := notify.PostJSON(ctx, n.client, n.conf.URL.String(), nil, &encodedMsg)
if err != nil {
return true, fmt.Errorf("failed to post message to PagerDuty: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion notify/pushover/pushover.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
u.RawQuery = parameters.Encode()
// Don't log the URL as it contains secret data (see #1825).
level.Debug(n.logger).Log("msg", "Sending message", "incident", key)
resp, err := notify.PostText(ctx, n.client, u.String(), nil)
resp, err := notify.PostText(ctx, n.client, u.String(), nil, nil)
Copy link
Contributor

Choose a reason for hiding this comment

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

That is a lot of "nil" for a caller. What if one just passes the *http.Request to a function like notify.Do?

if err != nil {
return true, notify.RedactURL(err)
}
Expand Down
4 changes: 2 additions & 2 deletions notify/slack/slack.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ type Notifier struct {
client *http.Client
retrier *notify.Retrier

postJSONFunc func(ctx context.Context, client *http.Client, url string, body io.Reader) (*http.Response, error)
postJSONFunc func(ctx context.Context, client *http.Client, url string, headers http.Header, body io.Reader) (*http.Response, error)
}

// New returns a new Slack notification handler.
Expand Down Expand Up @@ -205,7 +205,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
u = strings.TrimSpace(string(content))
}

resp, err := n.postJSONFunc(ctx, n.client, u, &buf)
resp, err := n.postJSONFunc(ctx, n.client, u, nil, &buf)
if err != nil {
return true, notify.RedactURL(err)
}
Expand Down
2 changes: 1 addition & 1 deletion notify/slack/slack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ func TestNotifier_Notify_WithReason(t *testing.T) {
)
require.NoError(t, err)

notifier.postJSONFunc = func(ctx context.Context, client *http.Client, url string, body io.Reader) (*http.Response, error) {
notifier.postJSONFunc = func(ctx context.Context, client *http.Client, url string, headers http.Header, body io.Reader) (*http.Response, error) {
resp := httptest.NewRecorder()
if strings.HasPrefix(tt.responseBody, "{") {
resp.Header().Add("Content-Type", "application/json; charset=utf-8")
Expand Down
35 changes: 23 additions & 12 deletions notify/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,33 +48,44 @@ func RedactURL(err error) error {
}

// Get sends a GET request to the given URL.
func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
return request(ctx, client, http.MethodGet, url, "", nil)
func Get(ctx context.Context, client *http.Client, url string, headers http.Header) (*http.Response, error) {
return request(ctx, client, http.MethodGet, url, headers, nil)
}

// PostJSON sends a POST request with JSON payload to the given URL.
func PostJSON(ctx context.Context, client *http.Client, url string, body io.Reader) (*http.Response, error) {
return post(ctx, client, url, "application/json", body)
func PostJSON(ctx context.Context, client *http.Client, url string, headers http.Header, body io.Reader) (*http.Response, error) {
if headers == nil {
headers = http.Header{}
}
headers.Set("Content-Type", "application/json")
return post(ctx, client, url, headers, body)
}

// PostText sends a POST request with text payload to the given URL.
func PostText(ctx context.Context, client *http.Client, url string, body io.Reader) (*http.Response, error) {
return post(ctx, client, url, "text/plain", body)
func PostText(ctx context.Context, client *http.Client, url string, headers http.Header, body io.Reader) (*http.Response, error) {
if headers == nil {
headers = http.Header{}
}
headers.Set("Content-Type", "text/plain")
return post(ctx, client, url, headers, body)
}

func post(ctx context.Context, client *http.Client, url, bodyType string, body io.Reader) (*http.Response, error) {
return request(ctx, client, http.MethodPost, url, bodyType, body)
func post(ctx context.Context, client *http.Client, url string, headers http.Header, body io.Reader) (*http.Response, error) {
return request(ctx, client, http.MethodPost, url, headers, body)
}

func request(ctx context.Context, client *http.Client, method, url, bodyType string, body io.Reader) (*http.Response, error) {
func request(ctx context.Context, client *http.Client, method, url string, headers http.Header, body io.Reader) (*http.Response, error) {
req, err := http.NewRequest(method, url, body)
if err != nil {
return nil, err
}
req.Header.Set("User-Agent", UserAgentHeader)
if bodyType != "" {
req.Header.Set("Content-Type", bodyType)
for k, vals := range headers {
for _, v := range vals {
// Use last write wins unless we have an explicit need for Add.
req.Header.Set(k, v)
}
}
req.Header.Set("User-Agent", UserAgentHeader)
return client.Do(req.WithContext(ctx))
}

Expand Down
60 changes: 60 additions & 0 deletions notify/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ package notify

import (
"bytes"
"context"
"fmt"
"io"
"net/http"
"net/http/httptest"
"path"
"reflect"
"runtime"
"testing"
"time"

"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -216,3 +219,60 @@ func TestRetrierCheck(t *testing.T) {
})
}
}

func TestPostJSON(t *testing.T) {
tests := []struct {
name string
headers http.Header
expectedHeaders http.Header
data []byte
}{{
name: "No headers",
headers: nil,
expectedHeaders: http.Header{
"Accept-Encoding": []string{"gzip"},
"Content-Length": []string{"13"},
"Content-Type": []string{"application/json"},
"User-Agent": []string{"Alertmanager/"},
},
data: []byte("Hello, world!"),
}, {
name: "With headers",
headers: http.Header{
"X-Test-PostJSON": []string{"true"},
},
expectedHeaders: http.Header{
"Accept-Encoding": []string{"gzip"},
"Content-Length": []string{"13"},
"Content-Type": []string{"application/json"},
"User-Agent": []string{"Alertmanager/"},
"X-Test-Postjson": []string{"true"},
},
data: []byte("Hello, world!"),
}}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var (
receivedHeaders http.Header
receivedData []byte
)
// Start an HTTP test server to record the headers and data from the request.
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
receivedHeaders = r.Header
receivedData, _ = io.ReadAll(r.Body)
}))
defer s.Close()

ctx, cancelFunc := context.WithTimeout(context.Background(), 5*time.Second)
defer cancelFunc()

resp, err := PostJSON(ctx, http.DefaultClient, s.URL, test.headers, bytes.NewReader(test.data))
require.NoError(t, err)
require.NoError(t, resp.Body.Close())
require.Equal(t, http.StatusOK, resp.StatusCode)

require.Equal(t, test.expectedHeaders, receivedHeaders)
require.Equal(t, test.data, receivedData)
})
}
}
2 changes: 1 addition & 1 deletion notify/victorops/victorops.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
return true, err
}

resp, err := notify.PostJSON(ctx, n.client, apiURL.String(), buf)
resp, err := notify.PostJSON(ctx, n.client, apiURL.String(), nil, buf)
if err != nil {
return true, notify.RedactURL(err)
}
Expand Down
2 changes: 1 addition & 1 deletion notify/webex/webex.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
return false, err
}

resp, err := notify.PostJSON(ctx, n.client, n.conf.APIURL.String(), &payload)
resp, err := notify.PostJSON(ctx, n.client, n.conf.APIURL.String(), nil, &payload)
if err != nil {
return true, notify.RedactURL(err)
}
Expand Down
2 changes: 1 addition & 1 deletion notify/webhook/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func (n *Notifier) Notify(ctx context.Context, alerts ...*types.Alert) (bool, er
url = strings.TrimSpace(string(content))
}

resp, err := notify.PostJSON(ctx, n.client, url, &buf)
resp, err := notify.PostJSON(ctx, n.client, url, nil, &buf)
if err != nil {
return true, notify.RedactURL(err)
}
Expand Down
4 changes: 2 additions & 2 deletions notify/wechat/wechat.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
u.Path += "gettoken"
u.RawQuery = parameters.Encode()

resp, err := notify.Get(ctx, n.client, u.String())
resp, err := notify.Get(ctx, n.client, u.String(), nil)
if err != nil {
return true, notify.RedactURL(err)
}
Expand Down Expand Up @@ -161,7 +161,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
q.Set("access_token", n.accessToken)
postMessageURL.RawQuery = q.Encode()

resp, err := notify.PostJSON(ctx, n.client, postMessageURL.String(), &buf)
resp, err := notify.PostJSON(ctx, n.client, postMessageURL.String(), nil, &buf)
if err != nil {
return true, notify.RedactURL(err)
}
Expand Down
Loading