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:
parent
4ac57e104b
commit
e69f89c279
|
@ -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:
|
||||
|
|
7
Makefile
7
Makefile
|
@ -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
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
```release-note:improvement
|
||||
core: Include build date in `sys/seal-status` and `sys/version-history` endpoints.
|
||||
```
|
|
@ -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 != "" {
|
||||
|
|
|
@ -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: "",
|
||||
|
|
|
@ -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("")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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}" \
|
||||
|
|
|
@ -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
|
|
@ -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"^
|
||||
.
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
@ -5,4 +5,5 @@ import "time"
|
|||
type VaultVersion struct {
|
||||
TimestampInstalled time.Time
|
||||
Version string
|
||||
BuildDate string
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue