api: add the ability to specify a path prefix (#12914)
Specifically meant for when consul is behind a reverse proxy / API gateway Co-authored-by: Evan Culver <eculver@hashicorp.com>
This commit is contained in:
parent
63a9175bd6
commit
386106a139
|
@ -0,0 +1,3 @@
|
|||
```release-note:enhancement
|
||||
api: add the ability to specify a path prefix for when consul is behind a reverse proxy or API gateway
|
||||
```
|
19
api/api.go
19
api/api.go
|
@ -320,6 +320,11 @@ type Config struct {
|
|||
// Scheme is the URI scheme for the Consul server
|
||||
Scheme string
|
||||
|
||||
// Prefix for URIs for when consul is behind an API gateway (reverse
|
||||
// proxy). The API gateway must strip off the PathPrefix before
|
||||
// passing the request onto consul.
|
||||
PathPrefix string
|
||||
|
||||
// Datacenter to use. If not provided, the default agent datacenter is used.
|
||||
Datacenter string
|
||||
|
||||
|
@ -712,6 +717,18 @@ func NewClient(config *Config) (*Client, error) {
|
|||
return nil, fmt.Errorf("Unknown protocol scheme: %s", parts[0])
|
||||
}
|
||||
config.Address = parts[1]
|
||||
|
||||
// separate out a reverse proxy prefix, if it is present.
|
||||
// NOTE: Rewriting this code to use url.Parse() instead of
|
||||
// strings.SplitN() breaks existing test cases.
|
||||
switch parts[0] {
|
||||
case "http", "https":
|
||||
parts := strings.SplitN(parts[1], "/", 2)
|
||||
if len(parts) == 2 {
|
||||
config.Address = parts[0]
|
||||
config.PathPrefix = "/" + parts[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the TokenFile is set, always use that, even if a Token is configured.
|
||||
|
@ -953,7 +970,7 @@ func (c *Client) newRequest(method, path string) *request {
|
|||
url: &url.URL{
|
||||
Scheme: c.config.Scheme,
|
||||
Host: c.config.Address,
|
||||
Path: path,
|
||||
Path: c.config.PathPrefix + path,
|
||||
},
|
||||
params: make(map[string][]string),
|
||||
header: c.Headers(),
|
||||
|
|
|
@ -1119,6 +1119,62 @@ func TestAPI_GenerateEnvHTTPS(t *testing.T) {
|
|||
require.Equal(t, expected, c.GenerateEnv())
|
||||
}
|
||||
|
||||
// TestAPI_PrefixPath() validates that Config.Address is split into
|
||||
// Config.Address and Config.PathPrefix as expected. If we want to add end to
|
||||
// end testing in the future this will require configuring and running an
|
||||
// API gateway / reverse proxy (e.g. nginx)
|
||||
func TestAPI_PrefixPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
addr string
|
||||
expectAddr string
|
||||
expectPrefix string
|
||||
}{
|
||||
{
|
||||
name: "with http and prefix",
|
||||
addr: "http://reverse.proxy.com/consul/path/prefix",
|
||||
expectAddr: "reverse.proxy.com",
|
||||
expectPrefix: "/consul/path/prefix",
|
||||
},
|
||||
{
|
||||
name: "with https and prefix",
|
||||
addr: "https://reverse.proxy.com/consul/path/prefix",
|
||||
expectAddr: "reverse.proxy.com",
|
||||
expectPrefix: "/consul/path/prefix",
|
||||
},
|
||||
{
|
||||
name: "with http and no prefix",
|
||||
addr: "http://localhost",
|
||||
expectAddr: "localhost",
|
||||
expectPrefix: "",
|
||||
},
|
||||
{
|
||||
name: "with https and no prefix",
|
||||
addr: "https://localhost",
|
||||
expectAddr: "localhost",
|
||||
expectPrefix: "",
|
||||
},
|
||||
{
|
||||
name: "no scheme and no prefix",
|
||||
addr: "localhost",
|
||||
expectAddr: "localhost",
|
||||
expectPrefix: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
c := &Config{Address: tc.addr}
|
||||
client, err := NewClient(c)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.expectAddr, client.config.Address)
|
||||
require.Equal(t, tc.expectPrefix, client.config.PathPrefix)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func getExpectedCaPoolByDir(t *testing.T) *x509.CertPool {
|
||||
pool := x509.NewCertPool()
|
||||
entries, err := os.ReadDir("../test/ca_path")
|
||||
|
|
Loading…
Reference in New Issue