Skip to content

Commit e3ec67f

Browse files
Add support for extra-headers to cortextool rules commands (#288)
1 parent 8898eb3 commit e3ec67f

File tree

4 files changed

+124
-16
lines changed

4 files changed

+124
-16
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ Order should be `CHANGE`, `FEATURE`, `ENHANCEMENT`, and `BUGFIX`
44

55
## unreleased/master
66

7+
* [FEATURE] Add `--extra-headers` support for `cortextool rules` commands. #288
8+
79
## v0.11.0
810

911
* [FEATURE] Support Arm64 on Darwin for all binaries (benchtool etc). #215

pkg/client/client.go

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,21 @@ type Config struct {
3333
Address string `yaml:"address"`
3434
ID string `yaml:"id"`
3535
TLS tls.ClientConfig
36-
UseLegacyRoutes bool `yaml:"use_legacy_routes"`
37-
AuthToken string `yaml:"auth_token"`
36+
UseLegacyRoutes bool `yaml:"use_legacy_routes"`
37+
AuthToken string `yaml:"auth_token"`
38+
ExtraHeaders map[string]string `yaml:"extra_headers"`
3839
}
3940

4041
// CortexClient is used to get and load rules into a cortex ruler
4142
type CortexClient struct {
42-
user string
43-
key string
44-
id string
45-
endpoint *url.URL
46-
Client http.Client
47-
apiPath string
48-
authToken string
43+
user string
44+
key string
45+
id string
46+
endpoint *url.URL
47+
Client http.Client
48+
apiPath string
49+
authToken string
50+
extraHeaders map[string]string
4951
}
5052

5153
// New returns a new Client
@@ -87,13 +89,14 @@ func New(cfg Config) (*CortexClient, error) {
8789
}
8890

8991
return &CortexClient{
90-
user: cfg.User,
91-
key: cfg.Key,
92-
id: cfg.ID,
93-
endpoint: endpoint,
94-
Client: client,
95-
apiPath: path,
96-
authToken: cfg.AuthToken,
92+
user: cfg.User,
93+
key: cfg.Key,
94+
id: cfg.ID,
95+
endpoint: endpoint,
96+
Client: client,
97+
apiPath: path,
98+
authToken: cfg.AuthToken,
99+
extraHeaders: cfg.ExtraHeaders,
97100
}, nil
98101
}
99102

@@ -137,6 +140,10 @@ func (r *CortexClient) doRequest(ctx context.Context, path, method string, paylo
137140
req.Header.Add("Authorization", "Bearer "+r.authToken)
138141
}
139142

143+
for k, v := range r.extraHeaders {
144+
req.Header.Add(k, v)
145+
}
146+
140147
req.Header.Add("X-Scope-OrgID", r.id)
141148

142149
log.WithFields(log.Fields{

pkg/client/client_test.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package client
22

33
import (
44
"context"
5+
"fmt"
56
"net/http"
7+
"net/http/httptest"
68
"net/url"
79
"testing"
810

@@ -94,3 +96,98 @@ func TestBuildURL(t *testing.T) {
9496
}
9597

9698
}
99+
100+
func TestDoRequest(t *testing.T) {
101+
requestCh := make(chan *http.Request, 1)
102+
103+
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
104+
requestCh <- r
105+
fmt.Fprintln(w, "hello")
106+
}))
107+
defer ts.Close()
108+
109+
for _, tc := range []struct {
110+
name string
111+
user string
112+
key string
113+
id string
114+
authToken string
115+
extraHeaders map[string]string
116+
expectedErr string
117+
validate func(t *testing.T, req *http.Request)
118+
}{
119+
{
120+
name: "basic headers only, no extra headers",
121+
id: "my-tenant-id",
122+
validate: func(t *testing.T, req *http.Request) {
123+
require.Equal(t, "my-tenant-id", req.Header.Get("X-Scope-OrgID"))
124+
// Verify no extra headers were added
125+
require.Empty(t, req.Header.Get("key1"))
126+
},
127+
},
128+
{
129+
name: "extraHeaders are added",
130+
id: "my-tenant-id",
131+
extraHeaders: map[string]string{
132+
"key1": "value1",
133+
"key2": "value2",
134+
"X-Scope-OrgID": "first-tenant-id",
135+
},
136+
validate: func(t *testing.T, req *http.Request) {
137+
require.Equal(t, "value1", req.Header.Get("key1"))
138+
require.Equal(t, "value2", req.Header.Get("key2"))
139+
require.Equal(t, []string{"first-tenant-id", "my-tenant-id"}, req.Header.Values("X-Scope-OrgID"))
140+
},
141+
},
142+
{
143+
name: "auth token with extra headers",
144+
id: "my-tenant-id",
145+
authToken: "my-auth-token",
146+
extraHeaders: map[string]string{
147+
"Custom-Header": "custom-value",
148+
},
149+
validate: func(t *testing.T, req *http.Request) {
150+
require.Equal(t, "Bearer my-auth-token", req.Header.Get("Authorization"))
151+
require.Equal(t, "my-tenant-id", req.Header.Get("X-Scope-OrgID"))
152+
require.Equal(t, "custom-value", req.Header.Get("Custom-Header"))
153+
},
154+
},
155+
{
156+
name: "authorization header in extra headers is ignored when auth token is set",
157+
id: "my-tenant-id",
158+
authToken: "my-auth-token",
159+
extraHeaders: map[string]string{
160+
"Authorization": "Bearer should-be-ignored",
161+
},
162+
validate: func(t *testing.T, req *http.Request) {
163+
// The Authorization header from extraHeaders should be overwritten
164+
require.Equal(t, "Bearer my-auth-token", req.Header.Get("Authorization"))
165+
},
166+
},
167+
} {
168+
t.Run(tc.name, func(t *testing.T) {
169+
ctx := context.Background()
170+
client, err := New(Config{
171+
Address: ts.URL,
172+
User: tc.user,
173+
Key: tc.key,
174+
AuthToken: tc.authToken,
175+
ID: tc.id,
176+
ExtraHeaders: tc.extraHeaders,
177+
})
178+
require.NoError(t, err)
179+
180+
res, err := client.doRequest(ctx, "/test", http.MethodGet, nil)
181+
182+
if tc.expectedErr != "" {
183+
require.Error(t, err)
184+
require.Contains(t, err.Error(), tc.expectedErr)
185+
} else {
186+
require.NoError(t, err)
187+
require.Equal(t, http.StatusOK, res.StatusCode)
188+
req := <-requestCh
189+
tc.validate(t, req)
190+
}
191+
})
192+
}
193+
}

pkg/commands/rules.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ func (r *RuleCommand) Register(app *kingpin.Application) {
9393
rulesCmd.Flag("user", "API user to use when contacting cortex, alternatively set CORTEX_API_USER. If empty, CORTEX_TENANT_ID will be used instead.").Default("").Envar("CORTEX_API_USER").StringVar(&r.ClientConfig.User)
9494
rulesCmd.Flag("key", "API key to use when contacting cortex, alternatively set CORTEX_API_KEY.").Default("").Envar("CORTEX_API_KEY").StringVar(&r.ClientConfig.Key)
9595
rulesCmd.Flag("backend", "Backend type to interact with: <cortex|loki>").Default("cortex").EnumVar(&r.Backend, backends...)
96+
r.ClientConfig.ExtraHeaders = map[string]string{}
97+
rulesCmd.Flag("extra-headers", "Extra headers to add to the requests in header=value format, alternatively set newline separated CORTEX_EXTRA_HEADERS.").Envar("CORTEX_EXTRA_HEADERS").StringMapVar(&r.ClientConfig.ExtraHeaders)
9698

9799
// Register rule commands
98100
listCmd := rulesCmd.

0 commit comments

Comments
 (0)