core: Record the time a node became active (#10489)
* core: Record the time a node became active * Update vault/core.go Co-authored-by: Nick Cabatoff <ncabatoff@hashicorp.com> * Add omitempty field * Update vendor * Added CL entry and fixed test * Fix test * Fix command package tests Co-authored-by: Nick Cabatoff <ncabatoff@hashicorp.com>
This commit is contained in:
parent
f137c945d7
commit
275ca323e8
|
@ -1,6 +1,9 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import "context"
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
func (c *Sys) Leader() (*LeaderResponse, error) {
|
func (c *Sys) Leader() (*LeaderResponse, error) {
|
||||||
r := c.c.NewRequest("GET", "/v1/sys/leader")
|
r := c.c.NewRequest("GET", "/v1/sys/leader")
|
||||||
|
@ -21,6 +24,7 @@ func (c *Sys) Leader() (*LeaderResponse, error) {
|
||||||
type LeaderResponse struct {
|
type LeaderResponse struct {
|
||||||
HAEnabled bool `json:"ha_enabled"`
|
HAEnabled bool `json:"ha_enabled"`
|
||||||
IsSelf bool `json:"is_self"`
|
IsSelf bool `json:"is_self"`
|
||||||
|
ActiveTime time.Time `json:"active_time"`
|
||||||
LeaderAddress string `json:"leader_address"`
|
LeaderAddress string `json:"leader_address"`
|
||||||
LeaderClusterAddress string `json:"leader_cluster_address"`
|
LeaderClusterAddress string `json:"leader_cluster_address"`
|
||||||
PerfStandby bool `json:"performance_standby"`
|
PerfStandby bool `json:"performance_standby"`
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:improvement
|
||||||
|
core: Added active since timestamp to the status output of active nodes.
|
||||||
|
```
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
"github.com/hashicorp/vault/api"
|
"github.com/hashicorp/vault/api"
|
||||||
|
@ -191,6 +192,9 @@ func (t TableFormatter) OutputSealStatusStruct(ui cli.Ui, secret *api.Secret, da
|
||||||
}
|
}
|
||||||
out = append(out, fmt.Sprintf("HA Mode | %s", mode))
|
out = append(out, fmt.Sprintf("HA Mode | %s", mode))
|
||||||
|
|
||||||
|
if status.IsSelf && !status.ActiveTime.IsZero() {
|
||||||
|
out = append(out, fmt.Sprintf("Active Since | %s", status.ActiveTime.Format(time.RFC3339Nano)))
|
||||||
|
}
|
||||||
// This is down here just to keep ordering consistent
|
// This is down here just to keep ordering consistent
|
||||||
if showLeaderAddr {
|
if showLeaderAddr {
|
||||||
out = append(out, fmt.Sprintf("Active Node Address | %s", status.LeaderAddress))
|
out = append(out, fmt.Sprintf("Active Node Address | %s", status.LeaderAddress))
|
||||||
|
@ -405,6 +409,7 @@ func OutputSealStatus(ui cli.Ui, client *api.Client, status *api.SealStatusRespo
|
||||||
// copy leaderStatus fields into sealStatusOutput for display later
|
// copy leaderStatus fields into sealStatusOutput for display later
|
||||||
sealStatusOutput.HAEnabled = leaderStatus.HAEnabled
|
sealStatusOutput.HAEnabled = leaderStatus.HAEnabled
|
||||||
sealStatusOutput.IsSelf = leaderStatus.IsSelf
|
sealStatusOutput.IsSelf = leaderStatus.IsSelf
|
||||||
|
sealStatusOutput.ActiveTime = leaderStatus.ActiveTime
|
||||||
sealStatusOutput.LeaderAddress = leaderStatus.LeaderAddress
|
sealStatusOutput.LeaderAddress = leaderStatus.LeaderAddress
|
||||||
sealStatusOutput.LeaderClusterAddress = leaderStatus.LeaderClusterAddress
|
sealStatusOutput.LeaderClusterAddress = leaderStatus.LeaderClusterAddress
|
||||||
sealStatusOutput.PerfStandby = leaderStatus.PerfStandby
|
sealStatusOutput.PerfStandby = leaderStatus.PerfStandby
|
||||||
|
@ -433,6 +438,7 @@ type SealStatusOutput struct {
|
||||||
api.SealStatusResponse
|
api.SealStatusResponse
|
||||||
HAEnabled bool `json:"ha_enabled"`
|
HAEnabled bool `json:"ha_enabled"`
|
||||||
IsSelf bool `json:"is_self,omitempty""`
|
IsSelf bool `json:"is_self,omitempty""`
|
||||||
|
ActiveTime time.Time `json:"active_time,omitempty""`
|
||||||
LeaderAddress string `json:"leader_address,omitempty"`
|
LeaderAddress string `json:"leader_address,omitempty"`
|
||||||
LeaderClusterAddress string `json:"leader_cluster_address,omitempty"`
|
LeaderClusterAddress string `json:"leader_cluster_address,omitempty"`
|
||||||
PerfStandby bool `json:"performance_standby,omitempty"`
|
PerfStandby bool `json:"performance_standby,omitempty"`
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
"github.com/hashicorp/vault/api"
|
"github.com/hashicorp/vault/api"
|
||||||
|
@ -178,6 +179,7 @@ func getMockStatusData(emptyFields bool) SealStatusOutput {
|
||||||
sealStatusResponseMock,
|
sealStatusResponseMock,
|
||||||
true, // HAEnabled
|
true, // HAEnabled
|
||||||
true, // IsSelf
|
true, // IsSelf
|
||||||
|
time.Time{}.UTC(), // ActiveTime
|
||||||
"leader address", // LeaderAddress
|
"leader address", // LeaderAddress
|
||||||
"leader cluster address", // LeaderClusterAddress
|
"leader cluster address", // LeaderClusterAddress
|
||||||
true, // PerfStandby
|
true, // PerfStandby
|
||||||
|
@ -208,6 +210,7 @@ func getMockStatusData(emptyFields bool) SealStatusOutput {
|
||||||
sealStatusResponseMock,
|
sealStatusResponseMock,
|
||||||
false, // HAEnabled
|
false, // HAEnabled
|
||||||
false, // IsSelf
|
false, // IsSelf
|
||||||
|
time.Time{}.UTC(), // ActiveTime
|
||||||
"", // LeaderAddress
|
"", // LeaderAddress
|
||||||
"", // LeaderClusterAddress
|
"", // LeaderClusterAddress
|
||||||
false, // PerfStandby
|
false, // PerfStandby
|
||||||
|
|
|
@ -2,6 +2,7 @@ package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/errwrap"
|
"github.com/hashicorp/errwrap"
|
||||||
"github.com/hashicorp/vault/vault"
|
"github.com/hashicorp/vault/vault"
|
||||||
|
@ -36,6 +37,9 @@ func handleSysLeaderGet(core *vault.Core, w http.ResponseWriter, r *http.Request
|
||||||
LeaderClusterAddress: clusterAddr,
|
LeaderClusterAddress: clusterAddr,
|
||||||
PerfStandby: core.PerfStandby(),
|
PerfStandby: core.PerfStandby(),
|
||||||
}
|
}
|
||||||
|
if isLeader {
|
||||||
|
resp.ActiveTime = core.ActiveTime()
|
||||||
|
}
|
||||||
if resp.PerfStandby {
|
if resp.PerfStandby {
|
||||||
resp.PerfStandbyLastRemoteWAL = vault.LastRemoteWAL(core)
|
resp.PerfStandbyLastRemoteWAL = vault.LastRemoteWAL(core)
|
||||||
} else if isLeader || !haEnabled {
|
} else if isLeader || !haEnabled {
|
||||||
|
@ -50,6 +54,7 @@ func handleSysLeaderGet(core *vault.Core, w http.ResponseWriter, r *http.Request
|
||||||
type LeaderResponse struct {
|
type LeaderResponse struct {
|
||||||
HAEnabled bool `json:"ha_enabled"`
|
HAEnabled bool `json:"ha_enabled"`
|
||||||
IsSelf bool `json:"is_self"`
|
IsSelf bool `json:"is_self"`
|
||||||
|
ActiveTime time.Time `json:"active_time,omitempty"`
|
||||||
LeaderAddress string `json:"leader_address"`
|
LeaderAddress string `json:"leader_address"`
|
||||||
LeaderClusterAddress string `json:"leader_cluster_address"`
|
LeaderClusterAddress string `json:"leader_cluster_address"`
|
||||||
PerfStandby bool `json:"performance_standby"`
|
PerfStandby bool `json:"performance_standby"`
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/vault/vault"
|
"github.com/hashicorp/vault/vault"
|
||||||
)
|
)
|
||||||
|
@ -27,10 +28,11 @@ func TestSysLeader_get(t *testing.T) {
|
||||||
"leader_cluster_address": "",
|
"leader_cluster_address": "",
|
||||||
"performance_standby": false,
|
"performance_standby": false,
|
||||||
"performance_standby_last_remote_wal": json.Number("0"),
|
"performance_standby_last_remote_wal": json.Number("0"),
|
||||||
|
"active_time": time.Time{}.UTC().Format(time.RFC3339),
|
||||||
}
|
}
|
||||||
testResponseStatus(t, resp, 200)
|
testResponseStatus(t, resp, 200)
|
||||||
testResponseBody(t, resp, &actual)
|
testResponseBody(t, resp, &actual)
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
t.Fatalf("bad: %#v", actual)
|
t.Fatalf("bad: %#v \n%#v", actual, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -535,6 +535,10 @@ type Core struct {
|
||||||
|
|
||||||
activityLogConfig ActivityLogCoreConfig
|
activityLogConfig ActivityLogCoreConfig
|
||||||
|
|
||||||
|
// activeTime is set on active nodes indicating the time at which this node
|
||||||
|
// became active.
|
||||||
|
activeTime time.Time
|
||||||
|
|
||||||
// KeyRotateGracePeriod is how long we allow an upgrade path
|
// KeyRotateGracePeriod is how long we allow an upgrade path
|
||||||
// for standby instances before we delete the upgrade keys
|
// for standby instances before we delete the upgrade keys
|
||||||
keyRotateGracePeriod *int64
|
keyRotateGracePeriod *int64
|
||||||
|
@ -1856,6 +1860,10 @@ func (s standardUnsealStrategy) unseal(ctx context.Context, logger log.Logger, c
|
||||||
c.clearForwardingClients()
|
c.clearForwardingClients()
|
||||||
c.requestForwardingConnectionLock.Unlock()
|
c.requestForwardingConnectionLock.Unlock()
|
||||||
|
|
||||||
|
// Mark the active time. We do this first so it can be correlated to the logs
|
||||||
|
// for the active startup.
|
||||||
|
c.activeTime = time.Now().UTC()
|
||||||
|
|
||||||
if err := postUnsealPhysical(c); err != nil {
|
if err := postUnsealPhysical(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -2047,6 +2055,7 @@ func (c *Core) preSeal() error {
|
||||||
|
|
||||||
// Clear any pending funcs
|
// Clear any pending funcs
|
||||||
c.postUnsealFuncs = nil
|
c.postUnsealFuncs = nil
|
||||||
|
c.activeTime = time.Time{}
|
||||||
|
|
||||||
// Clear any rekey progress
|
// Clear any rekey progress
|
||||||
c.barrierRekeyConfig = nil
|
c.barrierRekeyConfig = nil
|
||||||
|
|
|
@ -70,6 +70,13 @@ func (c *Core) PerfStandby() bool {
|
||||||
return perfStandby
|
return perfStandby
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Core) ActiveTime() time.Time {
|
||||||
|
c.stateLock.RLock()
|
||||||
|
activeTime := c.activeTime
|
||||||
|
c.stateLock.RUnlock()
|
||||||
|
return activeTime
|
||||||
|
}
|
||||||
|
|
||||||
// StandbyStates is meant as a way to avoid some extra locking on the very
|
// StandbyStates is meant as a way to avoid some extra locking on the very
|
||||||
// common sys/health check.
|
// common sys/health check.
|
||||||
func (c *Core) StandbyStates() (standby, perfStandby bool) {
|
func (c *Core) StandbyStates() (standby, perfStandby bool) {
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import "context"
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
func (c *Sys) Leader() (*LeaderResponse, error) {
|
func (c *Sys) Leader() (*LeaderResponse, error) {
|
||||||
r := c.c.NewRequest("GET", "/v1/sys/leader")
|
r := c.c.NewRequest("GET", "/v1/sys/leader")
|
||||||
|
@ -21,6 +24,7 @@ func (c *Sys) Leader() (*LeaderResponse, error) {
|
||||||
type LeaderResponse struct {
|
type LeaderResponse struct {
|
||||||
HAEnabled bool `json:"ha_enabled"`
|
HAEnabled bool `json:"ha_enabled"`
|
||||||
IsSelf bool `json:"is_self"`
|
IsSelf bool `json:"is_self"`
|
||||||
|
ActiveTime time.Time `json:"active_time"`
|
||||||
LeaderAddress string `json:"leader_address"`
|
LeaderAddress string `json:"leader_address"`
|
||||||
LeaderClusterAddress string `json:"leader_cluster_address"`
|
LeaderClusterAddress string `json:"leader_cluster_address"`
|
||||||
PerfStandby bool `json:"performance_standby"`
|
PerfStandby bool `json:"performance_standby"`
|
||||||
|
|
Loading…
Reference in New Issue