[Vault-5736] Add (*Client).WithNamespace() for temporary namespace handling (#14963)

temporary namespace calls
This commit is contained in:
Vinny Mannello 2022-04-14 09:50:21 -07:00 committed by GitHub
parent 299d3f096e
commit 3e6665f65d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 113 additions and 1 deletions

View File

@ -819,10 +819,39 @@ func (c *Client) setNamespace(namespace string) {
c.headers.Set(consts.NamespaceHeaderName, namespace) c.headers.Set(consts.NamespaceHeaderName, namespace)
} }
// ClearNamespace removes the namespace header if set.
func (c *Client) ClearNamespace() { func (c *Client) ClearNamespace() {
c.modifyLock.Lock() c.modifyLock.Lock()
defer c.modifyLock.Unlock() defer c.modifyLock.Unlock()
c.headers.Del(consts.NamespaceHeaderName) if c.headers != nil {
c.headers.Del(consts.NamespaceHeaderName)
}
}
// Namespace returns the namespace currently set in this client. It will
// return an empty string if there is no namespace set.
func (c *Client) Namespace() string {
c.modifyLock.Lock()
defer c.modifyLock.Unlock()
if c.headers == nil {
return ""
}
return c.headers.Get(consts.NamespaceHeaderName)
}
// WithNamespace makes a shallow copy of Client, modifies it to use
// the given namespace, and returns it. Passing an empty string will
// temporarily unset the namespace.
func (c *Client) WithNamespace(namespace string) *Client {
c2 := *c
c2.modifyLock = sync.RWMutex{}
c2.headers = c.Headers()
if namespace == "" {
c2.ClearNamespace()
} else {
c2.SetNamespace(namespace)
}
return &c2
} }
// Token returns the access token being used by this client. It will // Token returns the access token being used by this client. It will
@ -1141,12 +1170,22 @@ func (c *Client) rawRequestWithContext(ctx context.Context, r *Request) (*Respon
checkRetry := c.config.CheckRetry checkRetry := c.config.CheckRetry
backoff := c.config.Backoff backoff := c.config.Backoff
httpClient := c.config.HttpClient httpClient := c.config.HttpClient
ns := c.headers.Get(consts.NamespaceHeaderName)
outputCurlString := c.config.OutputCurlString outputCurlString := c.config.OutputCurlString
logger := c.config.Logger logger := c.config.Logger
c.config.modifyLock.RUnlock() c.config.modifyLock.RUnlock()
c.modifyLock.RUnlock() c.modifyLock.RUnlock()
// ensure that the most current namespace setting is used at the time of the call
// e.g. calls using (*Client).WithNamespace
switch ns {
case "":
r.Headers.Del(consts.NamespaceHeaderName)
default:
r.Headers.Set(consts.NamespaceHeaderName, ns)
}
for _, cb := range c.requestCallbacks { for _, cb := range c.requestCallbacks {
cb(r) cb(r)
} }
@ -1278,13 +1317,19 @@ func (c *Client) httpRequestWithContext(ctx context.Context, r *Request) (*Respo
limiter := c.config.Limiter limiter := c.config.Limiter
httpClient := c.config.HttpClient httpClient := c.config.HttpClient
outputCurlString := c.config.OutputCurlString outputCurlString := c.config.OutputCurlString
// add headers
if c.headers != nil { if c.headers != nil {
for header, vals := range c.headers { for header, vals := range c.headers {
for _, val := range vals { for _, val := range vals {
req.Header.Add(header, val) req.Header.Add(header, val)
} }
} }
// explicitly set the namespace header to current client
if ns := c.headers.Get(consts.NamespaceHeaderName); ns != "" {
r.Headers.Set(consts.NamespaceHeaderName, ns)
}
} }
c.config.modifyLock.RUnlock() c.config.modifyLock.RUnlock()
c.modifyLock.RUnlock() c.modifyLock.RUnlock()

View File

@ -1136,3 +1136,67 @@ func TestClient_SetCloneToken(t *testing.T) {
}) })
} }
} }
func TestClientWithNamespace(t *testing.T) {
var ns string
handler := func(w http.ResponseWriter, req *http.Request) {
ns = req.Header.Get(consts.NamespaceHeaderName)
}
config, ln := testHTTPServer(t, http.HandlerFunc(handler))
defer ln.Close()
// set up a client with a namespace
client, err := NewClient(config)
if err != nil {
t.Fatalf("err: %s", err)
}
ogNS := "test"
client.SetNamespace(ogNS)
_, err = client.rawRequestWithContext(
context.Background(),
client.NewRequest(http.MethodGet, "/"))
if err != nil {
t.Fatalf("err: %s", err)
}
if ns != ogNS {
t.Fatalf("Expected namespace: \"%s\", got \"%s\"", ogNS, ns)
}
// make a call with a temporary namespace
newNS := "new-namespace"
_, err = client.WithNamespace(newNS).rawRequestWithContext(
context.Background(),
client.NewRequest(http.MethodGet, "/"))
if err != nil {
t.Fatalf("err: %s", err)
}
if ns != newNS {
t.Fatalf("Expected new namespace: \"%s\", got \"%s\"", newNS, ns)
}
// ensure client has not been modified
_, err = client.rawRequestWithContext(
context.Background(),
client.NewRequest(http.MethodGet, "/"))
if err != nil {
t.Fatalf("err: %s", err)
}
if ns != ogNS {
t.Fatalf("Expected original namespace: \"%s\", got \"%s\"", ogNS, ns)
}
// make call with empty ns
_, err = client.WithNamespace("").rawRequestWithContext(
context.Background(),
client.NewRequest(http.MethodGet, "/"))
if err != nil {
t.Fatalf("err: %s", err)
}
if ns != "" {
t.Fatalf("Expected no namespace, got \"%s\"", ns)
}
// ensure client has not been modified
if client.Namespace() != ogNS {
t.Fatalf("Expected original namespace: \"%s\", got \"%s\"", ogNS, client.Namespace())
}
}

3
changelog/14963.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
api: Provide a helper method WithNamespace to create a cloned client with a new NS
```