Merge pull request #13357 from hashicorp/ma/add-build-date-oss

Add build date (oss)
This commit is contained in:
Mark Anderson 2022-06-13 08:43:20 -07:00 committed by GitHub
commit 6cb05666e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 209 additions and 22 deletions

4
.changelog/13357.txt Normal file
View File

@ -0,0 +1,4 @@
```release-note:feature
agent: Added information about build date alongside other version information for Consul. Extended /agent/self endpoint and `consul version` commands
to report this. Agent also reports build date in log on startup.
```

View File

@ -4,4 +4,7 @@ export GIT_COMMIT=$(git rev-parse --short HEAD)
export GIT_COMMIT_YEAR=$(git show -s --format=%cd --date=format:%Y HEAD)
export GIT_DIRTY=$(test -n "`git status --porcelain`" && echo "+CHANGES" || true)
export GIT_IMPORT=github.com/hashicorp/consul/version
export GOLDFLAGS="-X ${GIT_IMPORT}.GitCommit=${GIT_COMMIT}${GIT_DIRTY}"
# we're using this for build date because it's stable across platform builds
# the env -i and -noprofile are used to ensure we don't try to recursively call this profile when starting bash
export GIT_DATE=$(env -i /bin/bash --noprofile -norc ${CIRCLE_WORKING_DIRECTORY}/build-support/scripts/build-date.sh)
export GOLDFLAGS="-X ${GIT_IMPORT}.GitCommit=${GIT_COMMIT}${GIT_DIRTY} -X ${GIT_IMPORT}.BuildDate=${GIT_DATE}"

View File

@ -15,6 +15,7 @@ jobs:
runs-on: ubuntu-latest
outputs:
product-version: ${{ steps.get-product-version.outputs.product-version }}
product-date: ${{ steps.get-product-version.outputs.product-date }}
pre-version: ${{ steps.get-product-version.outputs.pre-version }}
pkg-version: ${{ steps.get-product-version.outputs.pkg-version }}
shared-ldflags: ${{ steps.shared-ldflags.outputs.shared-ldflags }}
@ -24,6 +25,7 @@ jobs:
id: get-product-version
run: |
CONSUL_VERSION=$(build-support/scripts/version.sh -r)
CONSUL_DATE=$(build-support/scripts/build-date.sh)
## TODO: This assumes `make version` outputs 1.1.1+ent-prerel
IFS="+" read VERSION _other <<< "$CONSUL_VERSION"
IFS="-" read _other PREREL_VERSION <<< "$CONSUL_VERSION"
@ -32,12 +34,15 @@ jobs:
## [version]{-prerelease}+ent before then, we'll need to add
## logic to handle presense/absence of the prerelease
echo "::set-output name=product-version::${CONSUL_VERSION}"
echo "::set-output name=product-date::${CONSUL_DATE}"
echo "::set-output name=pre-version::${PREREL_VERSION}"
echo "::set-output name=pkg-version::${VERSION}"
- name: Set shared -ldflags
id: shared-ldflags
run: echo "::set-output name=shared-ldflags::-X github.com/hashicorp/consul/version.GitCommit=${GITHUB_SHA::8} -X github.com/hashicorp/consul/version.GitDescribe=${{ steps.get-product-version.outputs.product-version }}"
run: |
T="github.com/hashicorp/consul/version"
echo "::set-output name=shared-ldflags::-X ${T}.GitCommit=${GITHUB_SHA::8} -X ${T}.GitDescribe=${{ steps.get-product-version.outputs.product-version }} -X ${T}.BuildDate=${{ steps.get-product-version.outputs.product-date }}"
generate-metadata-file:
needs: get-product-version
@ -95,9 +100,11 @@ jobs:
- name: Build UI
run: |
CONSUL_VERSION=${{ needs.get-product-version.outputs.product-version }}
CONSUL_DATE=${{ needs.get-product-version.outputs.product-date }}
CONSUL_BINARY_TYPE=${CONSUL_BINARY_TYPE}
CONSUL_COPYRIGHT_YEAR=$(git show -s --format=%cd --date=format:%Y HEAD)
echo "consul_version is ${CONSUL_VERSION}"
echo "consul_date is ${CONSUL_DATE}"
echo "consul binary type is ${CONSUL_BINARY_TYPE}"
echo "consul copyright year is ${CONSUL_COPYRIGHT_YEAR}"
cd ui && make && cd ..

View File

@ -25,7 +25,9 @@ GIT_COMMIT?=$(shell git rev-parse --short HEAD)
GIT_COMMIT_YEAR?=$(shell git show -s --format=%cd --date=format:%Y HEAD)
GIT_DIRTY?=$(shell test -n "`git status --porcelain`" && echo "+CHANGES" || true)
GIT_IMPORT=github.com/hashicorp/consul/version
GOLDFLAGS=-X $(GIT_IMPORT).GitCommit=$(GIT_COMMIT)$(GIT_DIRTY)
DATE_FORMAT="%Y-%m-%dT%H:%M:%SZ" # it's tricky to do an RFC3339 format in a cross platform way, so we hardcode UTC
GIT_DATE=$(shell $(CURDIR)/build-support/scripts/build-date.sh) # we're using this for build date because it's stable across platform builds
GOLDFLAGS=-X $(GIT_IMPORT).GitCommit=$(GIT_COMMIT)$(GIT_DIRTY) -X $(GIT_IMPORT).BuildDate=$(GIT_DATE)
ifeq ($(FORCE_REBUILD),1)
NOCACHE=--no-cache

View File

@ -91,6 +91,7 @@ func (s *HTTPHandlers) AgentSelf(resp http.ResponseWriter, req *http.Request) (i
Revision string
Server bool
Version string
BuildDate string
}{
Datacenter: s.agent.config.Datacenter,
PrimaryDatacenter: s.agent.config.PrimaryDatacenter,
@ -101,7 +102,9 @@ func (s *HTTPHandlers) AgentSelf(resp http.ResponseWriter, req *http.Request) (i
Server: s.agent.config.ServerMode,
// We expect the ent version to be part of the reported version string, and that's now part of the metadata, not the actual version.
Version: s.agent.config.VersionWithMetadata(),
BuildDate: s.agent.config.BuildDate.Format(time.RFC3339),
}
return Self{
Config: config,
DebugConfig: s.agent.config.Sanitized(),

View File

@ -804,6 +804,8 @@ func (b *builder) build() (rt RuntimeConfig, err error) {
Version: stringVal(c.Version),
VersionPrerelease: stringVal(c.VersionPrerelease),
VersionMetadata: stringVal(c.VersionMetadata),
// What is a sensible default for BuildDate?
BuildDate: timeValWithDefault(c.BuildDate, time.Date(1970, 1, 00, 00, 00, 01, 0, time.UTC)),
// consul configuration
ConsulCoordinateUpdateBatchSize: intVal(c.Consul.Coordinate.UpdateBatchSize),
@ -1946,6 +1948,13 @@ func stringVal(v *string) string {
return *v
}
func timeValWithDefault(v *time.Time, defaultVal time.Time) time.Time {
if v == nil {
return defaultVal
}
return *v
}
func float64ValWithDefault(v *float64, defaultVal float64) float64 {
if v == nil {
return defaultVal

View File

@ -3,6 +3,7 @@ package config
import (
"encoding/json"
"fmt"
"time"
"github.com/hashicorp/consul/agent/consul"
@ -273,6 +274,7 @@ type Config struct {
Version *string `mapstructure:"version"`
VersionPrerelease *string `mapstructure:"version_prerelease"`
VersionMetadata *string `mapstructure:"version_metadata"`
BuildDate *time.Time `mapstructure:"build_date"`
// Enterprise Only
Audit Audit `mapstructure:"audit"`

View File

@ -2,6 +2,7 @@ package config
import (
"strconv"
"time"
"github.com/hashicorp/raft"
@ -210,7 +211,7 @@ func NonUserSource() Source {
// versionSource creates a config source for the version parameters.
// This should be merged in the tail since these values are not
// user configurable.
func versionSource(rev, ver, verPre, meta string) Source {
func versionSource(rev, ver, verPre, meta string, buildDate time.Time) Source {
return LiteralSource{
Name: "version",
Config: Config{
@ -218,6 +219,7 @@ func versionSource(rev, ver, verPre, meta string) Source {
Version: &ver,
VersionPrerelease: &verPre,
VersionMetadata: &meta,
BuildDate: &buildDate,
},
}
}
@ -225,7 +227,8 @@ func versionSource(rev, ver, verPre, meta string) Source {
// defaultVersionSource returns the version config source for the embedded
// version numbers.
func defaultVersionSource() Source {
return versionSource(version.GitCommit, version.Version, version.VersionPrerelease, version.VersionMetadata)
buildDate, _ := time.Parse(time.RFC3339, version.BuildDate) // This has been checked elsewhere
return versionSource(version.GitCommit, version.Version, version.VersionPrerelease, version.VersionMetadata, buildDate)
}
// DefaultConsulSource returns the default configuration for the consul agent.

View File

@ -62,6 +62,7 @@ type RuntimeConfig struct {
Version string
VersionPrerelease string
VersionMetadata string
BuildDate time.Time
// consul config
ConsulCoordinateUpdateMaxBatches int
@ -1700,6 +1701,10 @@ func sanitize(name string, v reflect.Value) reflect.Value {
x := v.Interface().(time.Duration)
return reflect.ValueOf(x.String())
case isTime(typ):
x := v.Interface().(time.Time)
return reflect.ValueOf(x.String())
case isString(typ):
if strings.HasPrefix(name, "RetryJoinLAN[") || strings.HasPrefix(name, "RetryJoinWAN[") {
x := v.Interface().(string)
@ -1771,6 +1776,7 @@ func sanitize(name string, v reflect.Value) reflect.Value {
}
func isDuration(t reflect.Type) bool { return t == reflect.TypeOf(time.Second) }
func isTime(t reflect.Type) bool { return t == reflect.TypeOf(time.Time{}) }
func isMap(t reflect.Type) bool { return t.Kind() == reflect.Map }
func isNetAddr(t reflect.Type) bool { return t.Implements(reflect.TypeOf((*net.Addr)(nil)).Elem()) }
func isPtr(t reflect.Type) bool { return t.Kind() == reflect.Ptr }

View File

@ -5661,6 +5661,7 @@ func TestLoad_FullConfig(t *testing.T) {
Version: "R909Hblt",
VersionPrerelease: "ZT1JOQLn",
VersionMetadata: "GtTCa13",
BuildDate: time.Date(2019, 11, 20, 5, 0, 0, 0, time.UTC),
// consul configuration
ConsulCoordinateUpdateBatchSize: 128,
@ -6447,7 +6448,8 @@ func TestLoad_FullConfig(t *testing.T) {
ConfigFiles: []string{"testdata/full-config." + format},
HCL: []string{fmt.Sprintf(`data_dir = "%s"`, dataDir)},
}
opts.Overrides = append(opts.Overrides, versionSource("JNtPSav3", "R909Hblt", "ZT1JOQLn", "GtTCa13"))
opts.Overrides = append(opts.Overrides, versionSource("JNtPSav3", "R909Hblt", "ZT1JOQLn", "GtTCa13",
time.Date(2019, 11, 20, 5, 0, 0, 0, time.UTC)))
r, err := Load(opts)
require.NoError(t, err)
prototest.AssertDeepEqual(t, expected, r.RuntimeConfig)
@ -6641,6 +6643,7 @@ func parseCIDR(t *testing.T, cidr string) *net.IPNet {
func TestRuntimeConfig_Sanitize(t *testing.T) {
rt := RuntimeConfig{
BindAddr: &net.IPAddr{IP: net.ParseIP("127.0.0.1")},
BuildDate: time.Date(2019, 11, 20, 5, 0, 0, 0, time.UTC),
CheckOutputMaxSize: checks.DefaultBufSize,
SerfAdvertiseAddrLAN: &net.TCPAddr{IP: net.ParseIP("1.2.3.4"), Port: 5678},
DNSAddrs: []net.Addr{

View File

@ -76,6 +76,7 @@
"BindAddr": "127.0.0.1",
"Bootstrap": false,
"BootstrapExpect": 0,
"BuildDate": "2019-11-20 05:00:00 +0000 UTC",
"Cache": {
"EntryFetchMaxBurst": 42,
"EntryFetchRate": 0.334,

View File

@ -274,6 +274,42 @@ function git_branch {
return ${ret}
}
function git_date {
# Arguments:
# $1 - Path to the git repo (optional - assumes pwd is git repo otherwise)
#
# Returns:
# 0 - success
# * - failure
#
# Notes:
# Echos the date of the last git commit in
local gdir="$(pwd)"
if test -d "$1"
then
gdir="$1"
fi
pushd "${gdir}" > /dev/null
local ret=0
# it's tricky to do an RFC3339 format in a cross platform way, so we hardcode UTC
local date_format="%Y-%m-%dT%H:%M:%SZ"
# we're using this for build date because it's stable across platform builds
local date="$(TZ=UTC0 git show -s --format=%cd --date=format-local:"$date_format" HEAD)" || ret=1
##local head="$(git status -b --porcelain=v2 | awk '{if ($1 == "#" && $2 =="branch.head") { print $3 }}')" || ret=1
popd > /dev/null
test ${ret} -eq 0 && echo "$date"
return ${ret}
}
function is_git_clean {
# Arguments:
# $1 - Path to git repo
@ -325,7 +361,8 @@ function update_git_env {
export GIT_COMMIT=$(git rev-parse --short HEAD)
export GIT_DIRTY=$(test -n "$(git status --porcelain)" && echo "+CHANGES")
export GIT_IMPORT=github.com/hashicorp/consul/version
export GOLDFLAGS="-X ${GIT_IMPORT}.GitCommit=${GIT_COMMIT}${GIT_DIRTY}"
export GIT_DATE=$(git_date "$1")
export GOLDFLAGS="-X ${GIT_IMPORT}.GitCommit=${GIT_COMMIT}${GIT_DIRTY} -X ${T}.BuildDate=${GIT_DATE}"
return 0
}

View File

@ -0,0 +1,72 @@
#!/bin/bash
readonly SCRIPT_NAME="$(basename ${BASH_SOURCE[0]})"
readonly SCRIPT_DIR="$(dirname ${BASH_SOURCE[0]})"
readonly SOURCE_DIR="$(dirname "$(dirname "${SCRIPT_DIR}")")"
readonly FN_DIR="$(dirname "${SCRIPT_DIR}")/functions"
source "${SCRIPT_DIR}/functions.sh"
function usage {
cat <<-EOF
Usage: ${SCRIPT_NAME} [<options ...>]
Description:
This script uses the date of the last checkin on the branch as the build date. This
is to make the date consistent across the various platforms we build on, even if they
start at different times. In practice this is the commit where the version string is set.
Options:
-s | --source DIR Path to source to build.
Defaults to "${SOURCE_DIR}"
EOF
}
function err_usage {
err "$1"
err ""
err "$(usage)"
}
function main {
declare sdir="${SOURCE_DIR}"
declare -i date=0
while test $# -gt 0
do
case "$1" in
-h | --help )
usage
return 0
;;
-s | --source )
if test -z "$2"
then
err_usage "ERROR: option -s/--source requires an argument"
return 1
fi
if ! test -d "$2"
then
err_usage "ERROR: '$2' is not a directory and not suitable for the value of -s/--source"
return 1
fi
sdir="$2"
shift 2
;;
*)
err_usage "ERROR: Unknown argument: '$1'"
return 1
;;
esac
done
git_date "${sdir}" || return 1
return 0
}
main "$@"
exit $?

View File

@ -27,12 +27,19 @@ import (
)
func New(ui cli.Ui) *cmd {
buildDate, err := time.Parse(time.RFC3339, consulversion.BuildDate)
if err != nil {
ui.Error(fmt.Sprintf("Fatal error with internal time set; check makefile for build date %v %v \n", buildDate, err))
return nil
}
c := &cmd{
ui: ui,
revision: consulversion.GitCommit,
version: consulversion.Version,
versionPrerelease: consulversion.VersionPrerelease,
versionHuman: consulversion.GetHumanVersion(),
buildDate: buildDate,
flags: flag.NewFlagSet("", flag.ContinueOnError),
}
config.AddFlags(c.flags, &c.configLoadOpts)
@ -53,6 +60,7 @@ type cmd struct {
version string
versionPrerelease string
versionHuman string
buildDate time.Time
configLoadOpts config.LoadOpts
logger hclog.InterceptLogger
}
@ -194,6 +202,10 @@ func (c *cmd) run(args []string) int {
segment = "<all>"
}
ui.Info(fmt.Sprintf(" Version: '%s'", c.versionHuman))
if strings.Contains(c.versionHuman, "dev") {
ui.Info(fmt.Sprintf(" Revision: '%s'", c.revision))
}
ui.Info(fmt.Sprintf(" Build Date: '%s'", c.buildDate))
ui.Info(fmt.Sprintf(" Node ID: '%s'", config.NodeID))
ui.Info(fmt.Sprintf(" Node name: '%s'", config.NodeName))
if ap := config.PartitionOrEmpty(); ap != "" {

View File

@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"time"
)
const (
@ -43,6 +44,8 @@ func (_ *prettyFormatter) Format(info *VersionInfo) (string, error) {
buffer.WriteString(fmt.Sprintf("Revision %s\n", info.Revision))
}
buffer.WriteString(fmt.Sprintf("Build Date %s\n", info.BuildDate.Format(time.RFC3339)))
var supplement string
if info.RPC.Default < info.RPC.Max {
supplement = fmt.Sprintf(" (agent will automatically use protocol >%d when speaking to compatible agents)",

View File

@ -6,6 +6,7 @@ import (
"io/ioutil"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/require"
)
@ -31,11 +32,13 @@ func golden(t *testing.T, name, got string) string {
}
func TestFormat(t *testing.T) {
buildDate, _ := time.Parse(time.RFC3339, "2022-06-01T13:18:45Z")
info := VersionInfo{
HumanVersion: "1.99.3-beta1",
Version: "1.99.3",
Prerelease: "beta1",
Revision: "5e5dbedd47a5f875b60e241c5555a9caab595246",
BuildDate: buildDate,
RPC: RPCVersionInfo{
Default: 2,
Min: 1,

View File

@ -2,6 +2,7 @@
"Version": "1.99.3",
"Revision": "5e5dbedd47a5f875b60e241c5555a9caab595246",
"Prerelease": "beta1",
"BuildDate": "2022-06-01T13:18:45Z",
"RPC": {
"Default": 2,
"Min": 1,

View File

@ -1,3 +1,4 @@
Consul v1.99.3-beta1
Revision 5e5dbedd47a5f875b60e241c5555a9caab595246
Build Date 2022-06-01T13:18:45Z
Protocol 2 spoken by default, understands 1 to 3 (agent will automatically use protocol >2 when speaking to compatible agents)

View File

@ -4,6 +4,7 @@ import (
"flag"
"fmt"
"strings"
"time"
"github.com/hashicorp/consul/agent/consul"
"github.com/hashicorp/consul/command/flags"
@ -46,6 +47,7 @@ type VersionInfo struct {
Version string
Revision string
Prerelease string
BuildDate time.Time
RPC RPCVersionInfo
}
@ -59,11 +61,20 @@ func (c *cmd) Run(args []string) int {
c.UI.Error(err.Error())
return 1
}
// We parse this here because consul version is used in our 'smoke' tests and we want to fail early
buildDate, err := time.Parse(time.RFC3339, version.BuildDate)
if err != nil {
c.UI.Error(err.Error())
return 1
}
out, err := formatter.Format(&VersionInfo{
HumanVersion: version.GetHumanVersion(),
Version: version.Version,
Revision: version.GitCommit,
Prerelease: version.VersionPrerelease,
BuildDate: buildDate,
RPC: RPCVersionInfo{
Default: consul.DefaultRPCProtocol,
Min: int(consul.ProtocolVersionMin),

View File

@ -23,6 +23,10 @@ var (
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
VersionPrerelease = "dev"
// The date/time of the build (actually the HEAD commit in git, to preserve stability)
// This isn't just informational, but is also used by the licensing system. Default is chosen to be flagantly wrong.
BuildDate string = "1970-01-01T00:00:01Z"
)
// GetHumanVersion composes the parts of the version in a way that's suitable