Add build date (#14957)

* add BuildDate to version base

* populate BuildDate with ldflags

* include BuildDate in FullVersionNumber

* add BuildDate to seal-status and associated status cmd

* extend core/versions entries to include BuildDate

* include BuildDate in version-history API and CLI

* fix version history tests

* fix sys status tests

* fix TestStatusFormat

* remove extraneous LD_FLAGS from build.sh

* add BuildDate to build.bat

* fix TestSysUnseal_Reset

* attempt to add build-date to release builds

* add branch to github build workflow

* add get-build-date to build-* job needs

* fix release build command vars

* add missing quote in release build command

* Revert "add branch to github build workflow"

This reverts commit b835699ecb7c2c632757fa5fe64b3d5f60d2a886.

* add changelog entry
This commit is contained in:
Chris Capurso 2022-04-19 14:28:08 -04:00 committed by GitHub
parent 4ac57e104b
commit e69f89c279
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 163 additions and 109 deletions

View File

@ -27,6 +27,18 @@ jobs:
echo "::set-output name=product-version::$(make version)"
echo "::set-output name=product-base-version::${BASE_VERSION}"
get-build-date:
runs-on: ubuntu-latest
outputs:
build-date: ${{ steps.get-build-date.outputs.build-date }}
steps:
- uses: actions/checkout@v2
- name: get build date
id: get-build-date
run: |
make build-date
echo "::set-output name=build-date::$(make build-date)"
generate-metadata-file:
needs: get-product-version
runs-on: ubuntu-latest
@ -48,7 +60,7 @@ jobs:
path: ${{ steps.generate-metadata-file.outputs.filepath }}
build-other:
needs: get-product-version
needs: [ get-product-version, get-build-date ]
runs-on: ubuntu-latest
strategy:
matrix:
@ -92,7 +104,7 @@ jobs:
CGO_ENABLED: 0
run: |
mkdir dist out
GO_TAGS="${{ env.GO_TAGS }}" VAULT_VERSION=${{ needs.get-product-version.outputs.product-base-version }} VAULT_REVISION="$(git rev-parse HEAD)" make build
GO_TAGS="${{ env.GO_TAGS }}" VAULT_VERSION=${{ needs.get-product-version.outputs.product-base-version }} VAULT_REVISION="$(git rev-parse HEAD)" VAULT_BUILD_DATE="${{ needs.get-build-date.outputs.build-date }}" make build
zip -r -j out/${{ env.PKG_NAME }}_${{ needs.get-product-version.outputs.product-version }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip dist/
- uses: actions/upload-artifact@v2
with:
@ -100,7 +112,7 @@ jobs:
path: out/${{ env.PKG_NAME }}_${{ needs.get-product-version.outputs.product-version }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip
build-linux:
needs: get-product-version
needs: [ get-product-version, get-build-date ]
runs-on: ubuntu-latest
strategy:
matrix:
@ -138,7 +150,7 @@ jobs:
CGO_ENABLED: 0
run: |
mkdir dist out
GO_TAGS="${{ env.GO_TAGS }}" VAULT_VERSION=${{ needs.get-product-version.outputs.product-base-version }} VAULT_REVISION="$(git rev-parse HEAD)" make build
GO_TAGS="${{ env.GO_TAGS }}" VAULT_VERSION=${{ needs.get-product-version.outputs.product-base-version }} VAULT_REVISION="$(git rev-parse HEAD)" VAULT_BUILD_DATE="${{ needs.get-build-date.outputs.build-date }}" make build
zip -r -j out/${{ env.PKG_NAME }}_${{ needs.get-product-version.outputs.product-version }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip dist/
- uses: actions/upload-artifact@v2
with:
@ -177,7 +189,7 @@ jobs:
path: out/${{ env.DEB_PACKAGE }}
build-darwin:
needs: get-product-version
needs: [ get-product-version, get-build-date ]
runs-on: macos-latest
strategy:
matrix:
@ -214,7 +226,7 @@ jobs:
CGO_ENABLED: 0
run: |
mkdir dist out
GO_TAGS="${{ env.GO_TAGS }}" VAULT_VERSION=${{ needs.get-product-version.outputs.product-base-version }} VAULT_REVISION="$(git rev-parse HEAD)" make build
GO_TAGS="${{ env.GO_TAGS }}" VAULT_VERSION=${{ needs.get-product-version.outputs.product-base-version }} VAULT_REVISION="$(git rev-parse HEAD)" VAULT_BUILD_DATE="${{ needs.get-build-date.outputs.build-date }}" make build
zip -r -j out/${{ env.PKG_NAME }}_${{ needs.get-product-version.outputs.product-version }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip dist/
- uses: actions/upload-artifact@v2
with:

View File

@ -267,9 +267,14 @@ ci-verify:
# This is used for release builds by .github/workflows/build.yml
build:
@echo "--> Building Vault $(VAULT_VERSION)"
@go build -v -tags "$(GO_TAGS)" -ldflags " -X github.com/hashicorp/vault/sdk/version.Version=$(VAULT_VERSION) -X github.com/hashicorp/vault/sdk/version.GitCommit=$(VAULT_REVISION)" -o dist/
@go build -v -tags "$(GO_TAGS)" -ldflags " -X github.com/hashicorp/vault/sdk/version.Version=$(VAULT_VERSION) -X github.com/hashicorp/vault/sdk/version.GitCommit=$(VAULT_REVISION) -X github.com/hashicorp/vault/sdk/version.BuildDate=$(VAULT_BUILD_DATE)" -o dist/
.PHONY: version
# This is used for release builds by .github/workflows/build.yml
version:
@$(CURDIR)/scripts/version.sh sdk/version/version_base.go
.PHONY: build-date
# This is used for release builds by .github/workflows/build.yml
build-date:
@$(CURDIR)/scripts/build_date.sh

View File

@ -101,6 +101,7 @@ type SealStatusResponse struct {
Progress int `json:"progress"`
Nonce string `json:"nonce"`
Version string `json:"version"`
BuildDate string `json:"build_date"`
Migration bool `json:"migration"`
ClusterName string `json:"cluster_name,omitempty"`
ClusterID string `json:"cluster_id,omitempty"`

3
changelog/14957.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
core: Include build date in `sys/seal-status` and `sys/version-history` endpoints.
```

View File

@ -259,6 +259,7 @@ func (t TableFormatter) OutputSealStatusStruct(ui cli.Ui, secret *api.Secret, da
}
out = append(out, fmt.Sprintf("Version | %s", status.Version))
out = append(out, fmt.Sprintf("Build Date | %s", status.BuildDate))
out = append(out, fmt.Sprintf("Storage Type | %s", status.StorageType))
if status.ClusterName != "" && status.ClusterID != "" {

View File

@ -111,6 +111,7 @@ Unseal Progress 3/1
Unseal Nonce nonce
Seal Migration in Progress true
Version version
Build Date build date
Storage Type storage type
Cluster Name cluster name
Cluster ID cluster id
@ -141,6 +142,7 @@ Unseal Progress 3/1
Unseal Nonce nonce
Seal Migration in Progress true
Version version
Build Date build date
Storage Type n/a
HA Enabled false`
@ -166,6 +168,7 @@ func getMockStatusData(emptyFields bool) SealStatusOutput {
Progress: 3,
Nonce: "nonce",
Version: "version",
BuildDate: "build date",
Migration: true,
ClusterName: "cluster name",
ClusterID: "cluster id",
@ -197,6 +200,7 @@ func getMockStatusData(emptyFields bool) SealStatusOutput {
Progress: 3,
Nonce: "nonce",
Version: "version",
BuildDate: "build date",
Migration: true,
ClusterName: "",
ClusterID: "",

View File

@ -100,7 +100,7 @@ func (c *VersionHistoryCommand) Run(args []string) int {
return 2
}
table := []string{"Version | Installation Time"}
table := []string{"Version | Installation Time | Build Date"}
columnConfig := columnize.DefaultConfig()
for _, versionRaw := range keys {
@ -119,7 +119,7 @@ func (c *VersionHistoryCommand) Run(args []string) int {
return 2
}
table = append(table, fmt.Sprintf("%s | %s", version, versionInfo["timestamp_installed"]))
table = append(table, fmt.Sprintf("%s | %s | %s", version, versionInfo["timestamp_installed"], versionInfo["build_date"]))
}
c.UI.Warn("")

View File

@ -11,6 +11,7 @@ import (
"github.com/go-test/deep"
"github.com/hashicorp/vault/helper/namespace"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/sdk/version"
"github.com/hashicorp/vault/vault"
)
@ -36,6 +37,7 @@ func TestSysSealStatus(t *testing.T) {
"recovery_seal": false,
"initialized": true,
"migration": false,
"build_date": version.BuildDate,
}
testResponseStatus(t, resp, 200)
testResponseBody(t, resp, &actual)
@ -120,6 +122,7 @@ func TestSysUnseal(t *testing.T) {
"recovery_seal": false,
"initialized": true,
"migration": false,
"build_date": version.BuildDate,
}
if i == len(keys)-1 {
expected["sealed"] = false
@ -201,6 +204,7 @@ func TestSysUnseal_Reset(t *testing.T) {
"recovery_seal": false,
"initialized": true,
"migration": false,
"build_date": version.BuildDate,
}
testResponseStatus(t, resp, 200)
testResponseBody(t, resp, &actual)
@ -240,6 +244,7 @@ func TestSysUnseal_Reset(t *testing.T) {
"type": "shamir",
"recovery_seal": false,
"initialized": true,
"build_date": version.BuildDate,
"migration": false,
}
testResponseStatus(t, resp, 200)

View File

@ -7,8 +7,9 @@ GO_CMD=${GO_CMD:-go}
# Get the parent directory of where this script is.
SOURCE="${BASH_SOURCE[0]}"
SOURCE_DIR=$( dirname "$SOURCE" )
while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )"
DIR="$( cd -P "$SOURCE_DIR/.." && pwd )"
# Change into that directory
cd "$DIR"
@ -20,6 +21,8 @@ BUILD_TAGS="${BUILD_TAGS:-"vault"}"
GIT_COMMIT="$(git rev-parse HEAD)"
GIT_DIRTY="$(test -n "`git status --porcelain`" && echo "+CHANGES" || true)"
BUILD_DATE=$("$SOURCE_DIR"/build_date.sh)
# If its dev mode, only build for ourself
if [ "${VAULT_DEV_BUILD}x" != "x" ] && [ "${XC_OSARCH}x" == "x" ]; then
XC_OS=$(${GO_CMD} env GOOS)
@ -54,7 +57,7 @@ echo "==> Building..."
gox \
-osarch="${XC_OSARCH}" \
-gcflags "${GCFLAGS}" \
-ldflags "${LD_FLAGS}-X github.com/hashicorp/vault/sdk/version.GitCommit='${GIT_COMMIT}${GIT_DIRTY}'" \
-ldflags "${LD_FLAGS}-X github.com/hashicorp/vault/sdk/version.GitCommit='${GIT_COMMIT}${GIT_DIRTY}' -X github.com/hashicorp/vault/sdk/version.BuildDate=${BUILD_DATE}" \
-output "pkg/{{.OS}}_{{.Arch}}/vault" \
${GOX_PARALLEL_BUILDS+-parallel="${GOX_PARALLEL_BUILDS}"} \
-tags="${BUILD_TAGS}" \

6
scripts/build_date.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/bash
# it's tricky to do an RFC3339 format in a cross platform way, so we hardcode UTC
DATE_FORMAT="%Y-%m-%dT%H:%M:%SZ"
# we're using this for build date because it's stable across platform builds
git show -s --format=%cd --date=format:"$DATE_FORMAT" HEAD

View File

@ -13,6 +13,7 @@ md bin 2>nul
:: Get the git commit
set _GIT_COMMIT_FILE=%TEMP%\vault-git_commit.txt
set _GIT_DIRTY_FILE=%TEMP%\vault-git_dirty.txt
set _GIT_COMMIT_DATE_FILE=%TEMP%\vault-git_commit_date.txt
set _NUL_CMP_FILE=%TEMP%\vault-nul_cmp.txt
type nul >%_NUL_CMP_FILE%
@ -21,6 +22,10 @@ git rev-parse HEAD >"%_GIT_COMMIT_FILE%"
set /p _GIT_COMMIT=<"%_GIT_COMMIT_FILE%"
del /f "%_GIT_COMMIT_FILE%" 2>nul
git show -s --format=%cd --date=format:"%Y-%m-%dT%H:%M:%SZ" HEAD >"%_GIT_COMMIT__DATE_FILE%"
set /p _BUILD_DATE=<"%_GIT_COMMIT_DATE_FILE%"
del /f "%_GIT_COMMIT_DATE_FILE%" 2>nul
set _GIT_DIRTY=
git status --porcelain >"%_GIT_DIRTY_FILE%"
fc "%_GIT_DIRTY_FILE%" "%_NUL_CMP_FILE%" >nul
@ -60,7 +65,7 @@ echo ==^> Building...
gox^
-os="%_XC_OS%"^
-arch="%_XC_ARCH%"^
-ldflags "-X github.com/hashicorp/vault/sdk/version.GitCommit=%_GIT_COMMIT%%_GIT_DIRTY%"^
-ldflags "-X github.com/hashicorp/vault/sdk/version.GitCommit=%_GIT_COMMIT%%_GIT_DIRTY% -X github.com/hashicorp/vault/sdk/version.BuildDate=%_BUILD_DATE%"^
-output "pkg/{{.OS}}_{{.Arch}}/vault"^
.

View File

@ -11,6 +11,7 @@ type VersionInfo struct {
Version string `json:"version,omitempty"`
VersionPrerelease string `json:"version_prerelease,omitempty"`
VersionMetadata string `json:"version_metadata,omitempty"`
BuildDate string `json:"build_date,omitempty"`
}
func GetVersion() *VersionInfo {
@ -29,6 +30,7 @@ func GetVersion() *VersionInfo {
Version: ver,
VersionPrerelease: rel,
VersionMetadata: md,
BuildDate: BuildDate,
}
}
@ -70,5 +72,9 @@ func (c *VersionInfo) FullVersionNumber(rev bool) string {
fmt.Fprintf(&versionString, " (%s)", c.Revision)
}
if c.BuildDate != "" {
fmt.Fprintf(&versionString, ", built %s", c.BuildDate)
}
return versionString.String()
}

View File

@ -5,6 +5,9 @@ var (
GitCommit string
GitDescribe string
// The compilation date. This will be filled in by the compiler.
BuildDate string
// Whether cgo is enabled or not; set at build time
CgoEnabled bool

View File

@ -611,11 +611,12 @@ type Core struct {
// disableSSCTokens is used to disable server side consistent token creation/usage
disableSSCTokens bool
// versionTimestamps is a map of vault versions to timestamps when the version
// versionHistory is a map of vault versions to VaultVersion. The
// VaultVersion.TimestampInstalled when the version will denote when the version
// was first run. Note that because perf standbys should be upgraded first, and
// only the active node will actually write the new version timestamp, a perf
// standby shouldn't rely on the stored version timestamps being present.
versionTimestamps map[string]time.Time
versionHistory map[string]VaultVersion
}
func (c *Core) HAState() consts.HAState {
@ -1112,9 +1113,9 @@ func NewCore(conf *CoreConfig) (*Core, error) {
return nil, err
}
if c.versionTimestamps == nil {
c.logger.Info("Initializing versionTimestamps for core")
c.versionTimestamps = make(map[string]time.Time)
if c.versionHistory == nil {
c.logger.Info("Initializing version history cache for core")
c.versionHistory = make(map[string]VaultVersion)
}
return c, nil
@ -1124,15 +1125,22 @@ func NewCore(conf *CoreConfig) (*Core, error) {
// storage, and then loads all versions and upgrade timestamps out from storage.
func (c *Core) handleVersionTimeStamps(ctx context.Context) error {
currentTime := time.Now().UTC()
isUpdated, err := c.storeVersionTimestamp(ctx, version.Version, currentTime, false)
vaultVersion := &VaultVersion{
TimestampInstalled: currentTime,
Version: version.Version,
BuildDate: version.BuildDate,
}
isUpdated, err := c.storeVersionEntry(ctx, vaultVersion, false)
if err != nil {
return fmt.Errorf("error storing vault version: %w", err)
}
if isUpdated {
c.logger.Info("Recorded vault version", "vault version", version.Version, "upgrade time", currentTime)
c.logger.Info("Recorded vault version", "vault version", version.Version, "upgrade time", currentTime, "build date", version.BuildDate)
}
// Finally, load the versions into core fields
err = c.loadVersionTimestamps(ctx)
// Finally, populate the version history cache
err = c.loadVersionHistory(ctx)
if err != nil {
return err
}

View File

@ -56,17 +56,20 @@ func TestSealConfig_Invalid(t *testing.T) {
}
}
// TestCore_HasVaultVersion checks that versionTimestamps are correct and initialized
// TestCore_HasVaultVersion checks that versionHistory is correct and initialized
// after a core has been unsealed.
func TestCore_HasVaultVersion(t *testing.T) {
c, _, _ := TestCoreUnsealed(t)
if c.versionTimestamps == nil {
if c.versionHistory == nil {
t.Fatalf("Version timestamps for core were not initialized for a new core")
}
upgradeTime, ok := c.versionTimestamps[version.Version]
versionEntry, ok := c.versionHistory[version.Version]
if !ok {
t.Fatalf("%s upgrade time not found", version.Version)
}
upgradeTime := versionEntry.TimestampInstalled
if upgradeTime.After(time.Now()) || upgradeTime.Before(time.Now().Add(-1*time.Hour)) {
t.Fatalf("upgrade time isn't within reasonable bounds of new core initialization. " +
fmt.Sprintf("time is: %+v, upgrade time is %+v", time.Now(), upgradeTime))
@ -408,22 +411,19 @@ func TestCore_PreOneTen_BatchTokens(t *testing.T) {
// load up some versions and ensure that 1.9 is the most recent one by timestamp (even though this isn't realistic)
upgradeTimePlusEpsilon := time.Now().UTC()
versionEntries := []struct {
version string
ts time.Time
}{
{"1.10.1", upgradeTimePlusEpsilon.Add(-4 * time.Hour)},
{"1.9.2", upgradeTimePlusEpsilon.Add(2 * time.Hour)},
versionEntries := []VaultVersion{
{Version: "1.10.1", TimestampInstalled: upgradeTimePlusEpsilon.Add(-4 * time.Hour)},
{Version: "1.9.2", TimestampInstalled: upgradeTimePlusEpsilon.Add(2 * time.Hour)},
}
for _, entry := range versionEntries {
_, err := c.storeVersionTimestamp(context.Background(), entry.version, entry.ts, false)
_, err := c.storeVersionEntry(context.Background(), &entry, false)
if err != nil {
t.Fatalf("failed to write version entry %#v, err: %s", entry, err.Error())
}
}
err := c.loadVersionTimestamps(c.activeContext)
err := c.loadVersionHistory(c.activeContext)
if err != nil {
t.Fatalf("failed to populate version history cache, err: %s", err.Error())
}
@ -461,22 +461,19 @@ func TestCore_OneTenPlus_BatchTokens(t *testing.T) {
// load up some versions and ensure that 1.10 is the most recent version
upgradeTimePlusEpsilon := time.Now().UTC()
versionEntries := []struct {
version string
ts time.Time
}{
{"1.9.2", upgradeTimePlusEpsilon.Add(-4 * time.Hour)},
{"1.10.1", upgradeTimePlusEpsilon.Add(2 * time.Hour)},
versionEntries := []VaultVersion{
{Version: "1.9.2", TimestampInstalled: upgradeTimePlusEpsilon.Add(-4 * time.Hour)},
{Version: "1.10.1", TimestampInstalled: upgradeTimePlusEpsilon.Add(2 * time.Hour)},
}
for _, entry := range versionEntries {
_, err := c.storeVersionTimestamp(context.Background(), entry.version, entry.ts, false)
_, err := c.storeVersionEntry(context.Background(), &entry, false)
if err != nil {
t.Fatalf("failed to write version entry %#v, err: %s", entry, err.Error())
}
}
err := c.loadVersionTimestamps(c.activeContext)
err := c.loadVersionHistory(c.activeContext)
if err != nil {
t.Fatalf("failed to populate version history cache, err: %s", err.Error())
}

View File

@ -4159,6 +4159,7 @@ type SealStatusResponse struct {
Progress int `json:"progress"`
Nonce string `json:"nonce"`
Version string `json:"version"`
BuildDate string `json:"build_date"`
Migration bool `json:"migration"`
ClusterName string `json:"cluster_name,omitempty"`
ClusterID string `json:"cluster_id,omitempty"`
@ -4192,6 +4193,7 @@ func (core *Core) GetSealStatus(ctx context.Context) (*SealStatusResponse, error
RecoverySeal: core.SealAccess().RecoveryKeySupported(),
StorageType: core.StorageType(),
Version: version.GetVersion().VersionNumber(),
BuildDate: version.BuildDate,
}, nil
}
@ -4220,6 +4222,7 @@ func (core *Core) GetSealStatus(ctx context.Context) (*SealStatusResponse, error
Progress: progress,
Nonce: nonce,
Version: version.GetVersion().VersionNumber(),
BuildDate: version.BuildDate,
Migration: core.IsInSealMigrationMode() && !core.IsSealMigrated(),
ClusterName: clusterName,
ClusterID: clusterID,
@ -4401,11 +4404,8 @@ func (b *SystemBackend) handleVersionHistoryList(ctx context.Context, req *logic
versions := make([]VaultVersion, 0)
respKeys := make([]string, 0)
for versionString, ts := range b.Core.versionTimestamps {
versions = append(versions, VaultVersion{
Version: versionString,
TimestampInstalled: ts,
})
for _, versionEntry := range b.Core.versionHistory {
versions = append(versions, versionEntry)
}
sort.Slice(versions, func(i, j int) bool {
@ -4419,6 +4419,7 @@ func (b *SystemBackend) handleVersionHistoryList(ctx context.Context, req *logic
entry := map[string]interface{}{
"timestamp_installed": v.TimestampInstalled.Format(time.RFC3339),
"build_date": v.BuildDate,
"previous_version": nil,
}

View File

@ -5,4 +5,5 @@ import "time"
type VaultVersion struct {
TimestampInstalled time.Time
Version string
BuildDate string
}

View File

@ -15,17 +15,16 @@ const (
vaultVersionPath string = "core/versions/"
)
// storeVersionTimestamp will store the version and timestamp pair to storage
// storeVersionEntry will store the version, timestamp, and build date to storage
// only if no entry for that version already exists in storage. Version
// timestamps were initially stored in local time. UTC should be used. Existing
// entries can be overwritten via the force flag. A bool will be returned
// denoting whether the entry was updated
func (c *Core) storeVersionTimestamp(ctx context.Context, version string, timestampInstalled time.Time, force bool) (bool, error) {
key := vaultVersionPath + version
func (c *Core) storeVersionEntry(ctx context.Context, vaultVersion *VaultVersion, force bool) (bool, error) {
key := vaultVersionPath + vaultVersion.Version
vaultVersion := VaultVersion{
TimestampInstalled: timestampInstalled.UTC(),
Version: version,
if vaultVersion.TimestampInstalled.Location() != time.UTC {
vaultVersion.TimestampInstalled = vaultVersion.TimestampInstalled.UTC()
}
marshalledVaultVersion, err := json.Marshal(vaultVersion)
@ -70,44 +69,44 @@ func (c *Core) storeVersionTimestamp(ctx context.Context, version string, timest
// FindOldestVersionTimestamp searches for the vault version with the oldest
// upgrade timestamp from storage. The earliest version this can be is 1.9.0.
func (c *Core) FindOldestVersionTimestamp() (string, time.Time, error) {
if c.versionTimestamps == nil {
return "", time.Time{}, fmt.Errorf("version timestamps are not initialized")
if c.versionHistory == nil {
return "", time.Time{}, fmt.Errorf("version history is not initialized")
}
oldestUpgradeTime := time.Now().UTC()
var oldestVersion string
for version, upgradeTime := range c.versionTimestamps {
if upgradeTime.Before(oldestUpgradeTime) {
oldestVersion = version
oldestUpgradeTime = upgradeTime
for versionStr, versionEntry := range c.versionHistory {
if versionEntry.TimestampInstalled.Before(oldestUpgradeTime) {
oldestVersion = versionStr
oldestUpgradeTime = versionEntry.TimestampInstalled
}
}
return oldestVersion, oldestUpgradeTime, nil
}
func (c *Core) FindNewestVersionTimestamp() (string, time.Time, error) {
if c.versionTimestamps == nil {
return "", time.Time{}, fmt.Errorf("version timestamps are not initialized")
if c.versionHistory == nil {
return "", time.Time{}, fmt.Errorf("version history is not initialized")
}
var newestUpgradeTime time.Time
var newestVersion string
for version, upgradeTime := range c.versionTimestamps {
if upgradeTime.After(newestUpgradeTime) {
newestVersion = version
newestUpgradeTime = upgradeTime
for versionStr, versionEntry := range c.versionHistory {
if versionEntry.TimestampInstalled.After(newestUpgradeTime) {
newestVersion = versionStr
newestUpgradeTime = versionEntry.TimestampInstalled
}
}
return newestVersion, newestUpgradeTime, nil
}
// loadVersionTimestamps loads all the vault versions and associated upgrade
// timestamps from storage. Version timestamps were originally stored in local
// time. A timestamp that is not in UTC will be rewritten to storage as UTC.
func (c *Core) loadVersionTimestamps(ctx context.Context) error {
// loadVersionHistory loads all the vault versions entries from storage.
// Version timestamps were originally stored in local time. A timestamp
// that is not in UTC will be rewritten to storage as UTC.
func (c *Core) loadVersionHistory(ctx context.Context) error {
vaultVersions, err := c.barrier.List(ctx, vaultVersionPath)
if err != nil {
return fmt.Errorf("unable to retrieve vault versions from storage: %w", err)
@ -130,23 +129,22 @@ func (c *Core) loadVersionTimestamps(ctx context.Context) error {
return fmt.Errorf("found empty serialized vault version at path %s", versionPath)
}
timestampInstalled := vaultVersion.TimestampInstalled
// self-heal entries that were not stored in UTC
if timestampInstalled.Location() != time.UTC {
timestampInstalled = timestampInstalled.UTC()
isUpdated, err := c.storeVersionTimestamp(ctx, vaultVersion.Version, timestampInstalled, true)
if vaultVersion.TimestampInstalled.Location() != time.UTC {
vaultVersion.TimestampInstalled = vaultVersion.TimestampInstalled.UTC()
isUpdated, err := c.storeVersionEntry(ctx, &vaultVersion, true)
if err != nil {
c.logger.Warn("failed to rewrite vault version timestamp as UTC", "error", err)
}
if isUpdated {
c.logger.Info("self-healed pre-existing vault version in UTC",
"vault version", vaultVersion.Version, "UTC time", timestampInstalled)
"vault version", vaultVersion.Version, "UTC time", vaultVersion.TimestampInstalled)
}
}
c.versionTimestamps[vaultVersion.Version] = timestampInstalled
c.versionHistory[vaultVersion.Version] = vaultVersion
}
return nil
}

View File

@ -13,16 +13,20 @@ import (
func TestVersionStore_StoreMultipleVaultVersions(t *testing.T) {
c, _, _ := TestCoreUnsealed(t)
upgradeTimePlusEpsilon := time.Now().UTC()
wasStored, err := c.storeVersionTimestamp(context.Background(), version.Version, upgradeTimePlusEpsilon.Add(30*time.Hour), false)
vaultVersion := &VaultVersion{
Version: version.Version,
TimestampInstalled: upgradeTimePlusEpsilon.Add(30 * time.Hour),
}
wasStored, err := c.storeVersionEntry(context.Background(), vaultVersion, false)
if err != nil || wasStored {
t.Fatalf("vault version was re-stored: %v, err is: %s", wasStored, err.Error())
}
upgradeTime, ok := c.versionTimestamps[version.Version]
versionEntry, ok := c.versionHistory[version.Version]
if !ok {
t.Fatalf("no %s version timestamp found", version.Version)
}
if upgradeTime.After(upgradeTimePlusEpsilon) {
t.Fatalf("upgrade time for %s is incorrect: got %+v, expected less than %+v", version.Version, upgradeTime, upgradeTimePlusEpsilon)
if versionEntry.TimestampInstalled.After(upgradeTimePlusEpsilon) {
t.Fatalf("upgrade time for %s is incorrect: got %+v, expected less than %+v", version.Version, versionEntry.TimestampInstalled, upgradeTimePlusEpsilon)
}
}
@ -33,28 +37,25 @@ func TestVersionStore_GetOldestVersion(t *testing.T) {
upgradeTimePlusEpsilon := time.Now().UTC()
// 1.6.2 is stored before 1.6.1, so even though it is a higher number, it should be returned.
versionEntries := []struct {
version string
ts time.Time
}{
{"1.6.2", upgradeTimePlusEpsilon.Add(-4 * time.Hour)},
{"1.6.1", upgradeTimePlusEpsilon.Add(2 * time.Hour)},
versionEntries := []VaultVersion{
{Version: "1.6.2", TimestampInstalled: upgradeTimePlusEpsilon.Add(-4 * time.Hour)},
{Version: "1.6.1", TimestampInstalled: upgradeTimePlusEpsilon.Add(2 * time.Hour)},
}
for _, entry := range versionEntries {
_, err := c.storeVersionTimestamp(context.Background(), entry.version, entry.ts, false)
_, err := c.storeVersionEntry(context.Background(), &entry, false)
if err != nil {
t.Fatalf("failed to write version entry %#v, err: %s", entry, err.Error())
}
}
err := c.loadVersionTimestamps(c.activeContext)
err := c.loadVersionHistory(c.activeContext)
if err != nil {
t.Fatalf("failed to populate version history cache, err: %s", err.Error())
}
if len(c.versionTimestamps) != 3 {
t.Fatalf("expected 3 entries in timestamps map after refresh, found: %d", len(c.versionTimestamps))
if len(c.versionHistory) != 3 {
t.Fatalf("expected 3 entries in timestamps map after refresh, found: %d", len(c.versionHistory))
}
v, tm, err := c.FindOldestVersionTimestamp()
if err != nil {
@ -75,28 +76,25 @@ func TestVersionStore_GetNewestVersion(t *testing.T) {
upgradeTimePlusEpsilon := time.Now().UTC()
// 1.6.1 is stored after 1.6.2, so even though it is a lower number, it should be returned.
versionEntries := []struct {
version string
ts time.Time
}{
{"1.6.2", upgradeTimePlusEpsilon.Add(-4 * time.Hour)},
{"1.6.1", upgradeTimePlusEpsilon.Add(2 * time.Hour)},
versionEntries := []VaultVersion{
{Version: "1.6.2", TimestampInstalled: upgradeTimePlusEpsilon.Add(-4 * time.Hour)},
{Version: "1.6.1", TimestampInstalled: upgradeTimePlusEpsilon.Add(2 * time.Hour)},
}
for _, entry := range versionEntries {
_, err := c.storeVersionTimestamp(context.Background(), entry.version, entry.ts, false)
_, err := c.storeVersionEntry(context.Background(), &entry, false)
if err != nil {
t.Fatalf("failed to write version entry %#v, err: %s", entry, err.Error())
}
}
err := c.loadVersionTimestamps(c.activeContext)
err := c.loadVersionHistory(c.activeContext)
if err != nil {
t.Fatalf("failed to populate version history cache, err: %s", err.Error())
}
if len(c.versionTimestamps) != 3 {
t.Fatalf("expected 3 entries in timestamps map after refresh, found: %d", len(c.versionTimestamps))
if len(c.versionHistory) != 3 {
t.Fatalf("expected 3 entries in timestamps map after refresh, found: %d", len(c.versionHistory))
}
v, tm, err := c.FindNewestVersionTimestamp()
if err != nil {
@ -119,29 +117,26 @@ func TestVersionStore_SelfHealUTC(t *testing.T) {
nowEST := time.Now().In(estLoc)
versionEntries := []struct {
version string
ts time.Time
}{
{"1.9.0", nowEST.Add(24 * time.Hour)},
{"1.9.1", nowEST.Add(48 * time.Hour)},
versionEntries := []VaultVersion{
{Version: "1.9.0", TimestampInstalled: nowEST.Add(24 * time.Hour)},
{Version: "1.9.1", TimestampInstalled: nowEST.Add(48 * time.Hour)},
}
for _, entry := range versionEntries {
_, err := c.storeVersionTimestamp(context.Background(), entry.version, entry.ts, false)
_, err := c.storeVersionEntry(context.Background(), &entry, false)
if err != nil {
t.Fatalf("failed to write version entry %#v, err: %s", entry, err.Error())
}
}
err = c.loadVersionTimestamps(c.activeContext)
err = c.loadVersionHistory(c.activeContext)
if err != nil {
t.Fatalf("failed to load version timestamps, err: %s", err.Error())
}
for versionStr, ts := range c.versionTimestamps {
if ts.Location() != time.UTC {
t.Fatalf("failed to convert %s timestamp %s to UTC", versionStr, ts)
for _, entry := range c.versionHistory {
if entry.TimestampInstalled.Location() != time.UTC {
t.Fatalf("failed to convert %s timestamp %s to UTC", entry.Version, entry.TimestampInstalled)
}
}
}