diff --git a/vault/core.go b/vault/core.go index 9791412c08..3c67f63fad 100644 --- a/vault/core.go +++ b/vault/core.go @@ -328,6 +328,21 @@ func NewCore(conf *CoreConfig) (*Core, error) { return c, nil } +// Shutdown is invoked when the Vault instance is about to be terminated. It +// should not be accessible as part of an API call as it will cause an availability +// problem. It is only used to gracefully quit in the case of HA so that failover +// happens as quickly as possible. +func (c *Core) Shutdown() error { + c.stateLock.Lock() + defer c.stateLock.Unlock() + if c.sealed { + return nil + } + + // Seal the Vault, causes a leader stepdown + return c.sealInternal() +} + // HandleRequest is used to handle a new incoming request func (c *Core) HandleRequest(req *logical.Request) (resp *logical.Response, err error) { c.stateLock.RLock() @@ -930,6 +945,14 @@ func (c *Core) Seal(token string) error { return err } + // Seal the Vault + return c.sealInternal() +} + +// sealInternal is an internal method used to seal the vault. +// It does not do any authorization checking. The stateLock must +// be held prior to calling. +func (c *Core) sealInternal() error { // Enable that we are sealed to prevent furthur transactions c.sealed = true diff --git a/vault/core_test.go b/vault/core_test.go index 25cebf1943..4b48c59acd 100644 --- a/vault/core_test.go +++ b/vault/core_test.go @@ -348,6 +348,17 @@ func TestCore_SealUnseal(t *testing.T) { } } +// Attempt to shutdown after unseal +func TestCore_Shutdown(t *testing.T) { + c, _, _ := TestCoreUnsealed(t) + if err := c.Shutdown(); err != nil { + t.Fatalf("err: %v", err) + } + if sealed, err := c.Sealed(); err != nil || !sealed { + t.Fatalf("err: %v", err) + } +} + // Attempt to seal bad token func TestCore_Seal_BadToken(t *testing.T) { c, _, _ := TestCoreUnsealed(t)