Skip to content

Commit

Permalink
core: Record the time a node became active (hashicorp#10489)
Browse files Browse the repository at this point in the history
* core: Record the time a node became active

* Update vault/core.go

Co-authored-by: Nick Cabatoff <[email protected]>

* Add omitempty field

* Update vendor

* Added CL entry and fixed test

* Fix test

* Fix command package tests

Co-authored-by: Nick Cabatoff <[email protected]>
  • Loading branch information
briankassouf and ncabatoff authored Dec 12, 2020
1 parent 30eeac6 commit 9f5babf
Show file tree
Hide file tree
Showing 9 changed files with 89 additions and 46 deletions.
24 changes: 14 additions & 10 deletions api/sys_leader.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package api

import "context"
import (
"context"
"time"
)

func (c *Sys) Leader() (*LeaderResponse, error) {
r := c.c.NewRequest("GET", "/v1/sys/leader")
Expand All @@ -19,13 +22,14 @@ func (c *Sys) Leader() (*LeaderResponse, error) {
}

type LeaderResponse struct {
HAEnabled bool `json:"ha_enabled"`
IsSelf bool `json:"is_self"`
LeaderAddress string `json:"leader_address"`
LeaderClusterAddress string `json:"leader_cluster_address"`
PerfStandby bool `json:"performance_standby"`
PerfStandbyLastRemoteWAL uint64 `json:"performance_standby_last_remote_wal"`
LastWAL uint64 `json:"last_wal"`
RaftCommittedIndex uint64 `json:"raft_committed_index,omitempty"`
RaftAppliedIndex uint64 `json:"raft_applied_index,omitempty"`
HAEnabled bool `json:"ha_enabled"`
IsSelf bool `json:"is_self"`
ActiveTime time.Time `json:"active_time"`
LeaderAddress string `json:"leader_address"`
LeaderClusterAddress string `json:"leader_cluster_address"`
PerfStandby bool `json:"performance_standby"`
PerfStandbyLastRemoteWAL uint64 `json:"performance_standby_last_remote_wal"`
LastWAL uint64 `json:"last_wal"`
RaftCommittedIndex uint64 `json:"raft_committed_index,omitempty"`
RaftAppliedIndex uint64 `json:"raft_applied_index,omitempty"`
}
3 changes: 3 additions & 0 deletions changelog/10489.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
core: Added active since timestamp to the status output of active nodes.
```
24 changes: 15 additions & 9 deletions command/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"sort"
"strings"
"time"

"github.com/ghodss/yaml"
"github.com/hashicorp/vault/api"
Expand Down Expand Up @@ -191,6 +192,9 @@ func (t TableFormatter) OutputSealStatusStruct(ui cli.Ui, secret *api.Secret, da
}
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
if showLeaderAddr {
out = append(out, fmt.Sprintf("Active Node Address | %s", status.LeaderAddress))
Expand Down Expand Up @@ -405,6 +409,7 @@ func OutputSealStatus(ui cli.Ui, client *api.Client, status *api.SealStatusRespo
// copy leaderStatus fields into sealStatusOutput for display later
sealStatusOutput.HAEnabled = leaderStatus.HAEnabled
sealStatusOutput.IsSelf = leaderStatus.IsSelf
sealStatusOutput.ActiveTime = leaderStatus.ActiveTime
sealStatusOutput.LeaderAddress = leaderStatus.LeaderAddress
sealStatusOutput.LeaderClusterAddress = leaderStatus.LeaderClusterAddress
sealStatusOutput.PerfStandby = leaderStatus.PerfStandby
Expand All @@ -431,13 +436,14 @@ func looksLikeDuration(k string) bool {
// Currently we are adding the fields from api.LeaderResponse
type SealStatusOutput struct {
api.SealStatusResponse
HAEnabled bool `json:"ha_enabled"`
IsSelf bool `json:"is_self,omitempty""`
LeaderAddress string `json:"leader_address,omitempty"`
LeaderClusterAddress string `json:"leader_cluster_address,omitempty"`
PerfStandby bool `json:"performance_standby,omitempty"`
PerfStandbyLastRemoteWAL uint64 `json:"performance_standby_last_remote_wal,omitempty"`
LastWAL uint64 `json:"last_wal,omitempty"`
RaftCommittedIndex uint64 `json:"raft_committed_index,omitempty"`
RaftAppliedIndex uint64 `json:"raft_applied_index,omitempty"`
HAEnabled bool `json:"ha_enabled"`
IsSelf bool `json:"is_self,omitempty""`
ActiveTime time.Time `json:"active_time,omitempty""`
LeaderAddress string `json:"leader_address,omitempty"`
LeaderClusterAddress string `json:"leader_cluster_address,omitempty"`
PerfStandby bool `json:"performance_standby,omitempty"`
PerfStandbyLastRemoteWAL uint64 `json:"performance_standby_last_remote_wal,omitempty"`
LastWAL uint64 `json:"last_wal,omitempty"`
RaftCommittedIndex uint64 `json:"raft_committed_index,omitempty"`
RaftAppliedIndex uint64 `json:"raft_applied_index,omitempty"`
}
21 changes: 12 additions & 9 deletions command/format_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"strings"
"testing"
"time"

"github.com/ghodss/yaml"
"github.com/hashicorp/vault/api"
Expand Down Expand Up @@ -178,6 +179,7 @@ func getMockStatusData(emptyFields bool) SealStatusOutput {
sealStatusResponseMock,
true, // HAEnabled
true, // IsSelf
time.Time{}.UTC(), // ActiveTime
"leader address", // LeaderAddress
"leader cluster address", // LeaderClusterAddress
true, // PerfStandby
Expand Down Expand Up @@ -206,15 +208,16 @@ func getMockStatusData(emptyFields bool) SealStatusOutput {
// must initialize this struct without explicit field names due to embedding
status = SealStatusOutput{
sealStatusResponseMock,
false, // HAEnabled
false, // IsSelf
"", // LeaderAddress
"", // LeaderClusterAddress
false, // PerfStandby
0, // PerfStandbyLastRemoteWAL
0, // LastWAL
0, // RaftCommittedIndex
0, // RaftAppliedIndex
false, // HAEnabled
false, // IsSelf
time.Time{}.UTC(), // ActiveTime
"", // LeaderAddress
"", // LeaderClusterAddress
false, // PerfStandby
0, // PerfStandbyLastRemoteWAL
0, // LastWAL
0, // RaftCommittedIndex
0, // RaftAppliedIndex
}
}
return status
Expand Down
19 changes: 12 additions & 7 deletions http/sys_leader.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package http

import (
"net/http"
"time"

"github.com/hashicorp/errwrap"
"github.com/hashicorp/vault/vault"
Expand Down Expand Up @@ -36,6 +37,9 @@ func handleSysLeaderGet(core *vault.Core, w http.ResponseWriter, r *http.Request
LeaderClusterAddress: clusterAddr,
PerfStandby: core.PerfStandby(),
}
if isLeader {
resp.ActiveTime = core.ActiveTime()
}
if resp.PerfStandby {
resp.PerfStandbyLastRemoteWAL = vault.LastRemoteWAL(core)
} else if isLeader || !haEnabled {
Expand All @@ -48,13 +52,14 @@ func handleSysLeaderGet(core *vault.Core, w http.ResponseWriter, r *http.Request
}

type LeaderResponse struct {
HAEnabled bool `json:"ha_enabled"`
IsSelf bool `json:"is_self"`
LeaderAddress string `json:"leader_address"`
LeaderClusterAddress string `json:"leader_cluster_address"`
PerfStandby bool `json:"performance_standby"`
PerfStandbyLastRemoteWAL uint64 `json:"performance_standby_last_remote_wal"`
LastWAL uint64 `json:"last_wal,omitempty"`
HAEnabled bool `json:"ha_enabled"`
IsSelf bool `json:"is_self"`
ActiveTime time.Time `json:"active_time,omitempty"`
LeaderAddress string `json:"leader_address"`
LeaderClusterAddress string `json:"leader_cluster_address"`
PerfStandby bool `json:"performance_standby"`
PerfStandbyLastRemoteWAL uint64 `json:"performance_standby_last_remote_wal"`
LastWAL uint64 `json:"last_wal,omitempty"`

// Raft Indexes for this node
RaftCommittedIndex uint64 `json:"raft_committed_index,omitempty"`
Expand Down
4 changes: 3 additions & 1 deletion http/sys_leader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net/http"
"reflect"
"testing"
"time"

"github.com/hashicorp/vault/vault"
)
Expand All @@ -27,10 +28,11 @@ func TestSysLeader_get(t *testing.T) {
"leader_cluster_address": "",
"performance_standby": false,
"performance_standby_last_remote_wal": json.Number("0"),
"active_time": time.Time{}.UTC().Format(time.RFC3339),
}
testResponseStatus(t, resp, 200)
testResponseBody(t, resp, &actual)
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: %#v", actual)
t.Fatalf("bad: %#v \n%#v", actual, expected)
}
}
9 changes: 9 additions & 0 deletions vault/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,10 @@ type Core struct {

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
// for standby instances before we delete the upgrade keys
keyRotateGracePeriod *int64
Expand Down Expand Up @@ -1856,6 +1860,10 @@ func (s standardUnsealStrategy) unseal(ctx context.Context, logger log.Logger, c
c.clearForwardingClients()
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 {
return err
}
Expand Down Expand Up @@ -2047,6 +2055,7 @@ func (c *Core) preSeal() error {

// Clear any pending funcs
c.postUnsealFuncs = nil
c.activeTime = time.Time{}

// Clear any rekey progress
c.barrierRekeyConfig = nil
Expand Down
7 changes: 7 additions & 0 deletions vault/ha.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ func (c *Core) PerfStandby() bool {
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
// common sys/health check.
func (c *Core) StandbyStates() (standby, perfStandby bool) {
Expand Down
24 changes: 14 additions & 10 deletions vendor/github.com/hashicorp/vault/api/sys_leader.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 9f5babf

Please sign in to comment.