Skip to content

Commit

Permalink
DAOS-16070 tools: Include BuildInfo field in version output (#14609) (#…
Browse files Browse the repository at this point in the history
…14645)

When available, include more detailed build information in
the version output (most recent tag, etc).

Adds build details to the GetAttachInfo response message in
order to provide server build info to the dmg, daos_agent, and
daos tools (accessible via server-version subcommand).

Signed-off-by: Michael MacDonald <mjmac@google.com>
  • Loading branch information
mjmac committed Jun 26, 2024
1 parent 0fdd529 commit 833d393
Show file tree
Hide file tree
Showing 21 changed files with 1,028 additions and 287 deletions.
13 changes: 13 additions & 0 deletions src/control/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# pylint: disable=too-many-locals
import os
import socket
import subprocess # nosec
from binascii import b2a_hex
from datetime import datetime, timezone
from os import urandom
Expand Down Expand Up @@ -45,20 +46,32 @@ def gen_build_id():
return '0x' + buildid.decode()


def get_build_info():
"""Attempt to retrieve commit/tag details from the build environment."""
try:
cmd = subprocess.run(['git', 'describe', '--tags', '--dirty', '--always'],
stdout=subprocess.PIPE, check=True) # nosec
except (subprocess.CalledProcessError, FileNotFoundError):
return ''
return cmd.stdout.decode().strip()


def go_ldflags(benv):
"Create the ldflags option for the Go build."

build_host = ''
if not is_release_build(benv):
build_host = socket.getfqdn()
build_time = datetime.now(timezone.utc).astimezone().isoformat()
build_info = get_build_info()
Import('daos_version', 'conf_dir')
path = 'github.com/daos-stack/daos/src/control/build'
return ' '.join([f'-X {path}.DaosVersion={daos_version}',
f'-X {path}.ConfigDir={conf_dir}',
f'-X {path}.BuildHost={build_host}',
# NB: dynamic values should be enclosed in $(...$)
# to avoid unnecessary rebuilds
f'-X $({path}.BuildInfo={build_info}$)',
f'-X $({path}.BuildTime={build_time}$)',
f'-B $({gen_build_id()}$)'])

Expand Down
34 changes: 22 additions & 12 deletions src/control/build/string.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// (C) Copyright 2023 Intel Corporation.
// (C) Copyright 2023-2024 Intel Corporation.
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
Expand Down Expand Up @@ -36,7 +36,24 @@ func revString(version string) string {
// String returns a string containing the name, version, and for non-release builds,
// the revision of the binary.
func String(name string) string {
return fmt.Sprintf("%s version %s", name, revString(DaosVersion))
return VersionString(name, revString(DaosVersion))
}

// VersionString returns a string concatenation of the supplied name and version.
func VersionString(name, version string) string {
return fmt.Sprintf("%s version %s", name, version)
}

// Info contains a structured representation of the binary build info.
type Info struct {
Name string `json:"name"`
Version string `json:"version"`
Revision string `json:"revision,omitempty"`
Dirty bool `json:"dirty,omitempty"`
Release bool `json:"release,omitempty"`
BuildHost string `json:"build_host,omitempty"`
BuildTime *time.Time `json:"build_time,omitempty"`
BuildInfo string `json:"build_info,omitempty"`
}

// MarshalJSON returns a JSON string containing a structured representation of
Expand All @@ -45,21 +62,14 @@ func MarshalJSON(name string) ([]byte, error) {
// Not a fatal error if the build time can't be parsed.
buildTime, _ := time.Parse(time.RFC3339, BuildTime)

return json.Marshal(&struct {
Name string `json:"name"`
Version string `json:"version"`
Revision string `json:"revision,omitempty"`
Dirty bool `json:"dirty,omitempty"`
Release bool `json:"release,omitempty"`
BuildHost string `json:"build_host,omitempty"`
BuildTime time.Time `json:"build_time,omitempty"`
}{
return json.Marshal(&Info{
Name: name,
Version: DaosVersion,
Revision: Revision,
Dirty: DirtyBuild,
Release: ReleaseBuild,
BuildHost: BuildHost,
BuildTime: buildTime,
BuildTime: &buildTime,
BuildInfo: BuildInfo,
})
}
4 changes: 3 additions & 1 deletion src/control/build/variables.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// (C) Copyright 2020-2023 Intel Corporation.
// (C) Copyright 2020-2024 Intel Corporation.
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
Expand All @@ -18,6 +18,8 @@ var (
BuildTime = ""
// BuildHost should be set via linker flag using the value of BUILD_HOST.
BuildHost = ""
// BuildInfo should be set via linker flag using the value of BUILD_INFO.
BuildInfo = ""
// ControlPlaneName defines a consistent name for the control plane server.
ControlPlaneName = "DAOS Control Server"
// DataPlaneName defines a consistent name for the engine.
Expand Down
46 changes: 35 additions & 11 deletions src/control/cmd/daos/main.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// (C) Copyright 2021-2023 Intel Corporation.
// (C) Copyright 2021-2024 Intel Corporation.
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
Expand All @@ -24,16 +24,17 @@ import (
)

type cliOptions struct {
Debug bool `long:"debug" description:"enable debug output"`
Verbose bool `long:"verbose" description:"enable verbose output (when applicable)"`
JSON bool `long:"json" short:"j" description:"enable JSON output"`
Container containerCmd `command:"container" alias:"cont" description:"perform tasks related to DAOS containers"`
Pool poolCmd `command:"pool" description:"perform tasks related to DAOS pools"`
Filesystem fsCmd `command:"filesystem" alias:"fs" description:"POSIX filesystem operations"`
Object objectCmd `command:"object" alias:"obj" description:"DAOS object operations"`
System systemCmd `command:"system" alias:"sys" description:"DAOS system operations"`
Version versionCmd `command:"version" description:"print daos version"`
ManPage cmdutil.ManCmd `command:"manpage" hidden:"true"`
Debug bool `long:"debug" description:"enable debug output"`
Verbose bool `long:"verbose" description:"enable verbose output (when applicable)"`
JSON bool `long:"json" short:"j" description:"enable JSON output"`
Container containerCmd `command:"container" alias:"cont" description:"perform tasks related to DAOS containers"`
Pool poolCmd `command:"pool" description:"perform tasks related to DAOS pools"`
Filesystem fsCmd `command:"filesystem" alias:"fs" description:"POSIX filesystem operations"`
Object objectCmd `command:"object" alias:"obj" description:"DAOS object operations"`
System systemCmd `command:"system" alias:"sys" description:"DAOS system operations"`
Version versionCmd `command:"version" description:"print daos version"`
ServerVersion serverVersionCmd `command:"server-version" description:"Print server version"`
ManPage cmdutil.ManCmd `command:"manpage" hidden:"true"`
}

type versionCmd struct {
Expand All @@ -54,6 +55,29 @@ func (cmd *versionCmd) Execute(_ []string) error {
return nil
}

type serverVersionCmd struct {
daosCmd
cmdutil.JSONOutputCmd
}

func (cmd *serverVersionCmd) Execute(_ []string) error {
buildInfo, err := srvBuildInfo()
if err != nil {
return errors.Wrap(err, "failed to get server build info")
}

if cmd.JSONOutputEnabled() {
buf, err := json.Marshal(buildInfo)
if err != nil {
return err
}
return cmd.OutputJSON(json.RawMessage(buf), nil)
}

_, err = fmt.Println(build.VersionString(build.ControlPlaneName, buildInfo.Version))
return err
}

func exitWithError(log logging.Logger, err error) {
cmdName := path.Base(os.Args[0])
log.Errorf("%s: %v", cmdName, err)
Expand Down
22 changes: 21 additions & 1 deletion src/control/cmd/daos/util.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// (C) Copyright 2021-2023 Intel Corporation.
// (C) Copyright 2021-2024 Intel Corporation.
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
Expand All @@ -17,6 +17,7 @@ import (
"github.com/google/uuid"
"github.com/pkg/errors"

"github.com/daos-stack/daos/src/control/build"
"github.com/daos-stack/daos/src/control/common/cmdutil"
"github.com/daos-stack/daos/src/control/lib/daos"
"github.com/daos-stack/daos/src/control/logging"
Expand Down Expand Up @@ -62,6 +63,25 @@ func apiVersion() string {
)
}

func srvBuildInfo() (*build.Info, error) {
var major uint32
var minor uint32
var patch uint32
var tagPtr *C.char

rc := C.dc_mgmt_srv_version((*C.uint)(&major), (*C.uint)(&minor), (*C.uint)(&patch), &tagPtr)
if err := daosError(rc); err != nil {
return nil, err
}
tagStr := C.GoString(tagPtr)

return &build.Info{
Name: build.ControlPlaneName,
Version: (&build.Version{Major: int(major), Minor: int(minor), Patch: int(patch)}).String(),
BuildInfo: tagStr,
}, nil
}

func daosError(rc C.int) error {
if rc == 0 {
return nil
Expand Down
24 changes: 17 additions & 7 deletions src/control/cmd/daos_agent/attachinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package main

import (
"context"
"fmt"
"os"

Expand All @@ -17,11 +18,24 @@ import (
"github.com/daos-stack/daos/src/control/lib/txtfmt"
)

type dumpAttachInfoCmd struct {
type attachInfoCmd struct {
configCmd
ctlInvokerCmd
cmdutil.LogCmd
cmdutil.JSONOutputCmd
}

func (cmd *attachInfoCmd) getAttachInfo(ctx context.Context) (*control.GetAttachInfoResp, error) {
req := &control.GetAttachInfoReq{
AllRanks: true,
}
req.SetSystem(cmd.cfg.SystemName)
resp, err := control.GetAttachInfo(ctx, cmd.ctlInvoker, req)
return resp, errors.Wrap(err, "GetAttachInfo failed")
}

type dumpAttachInfoCmd struct {
attachInfoCmd
Output string `short:"o" long:"output" default:"stdout" description:"Dump output to this location"`
}

Expand All @@ -37,13 +51,9 @@ func (cmd *dumpAttachInfoCmd) Execute(_ []string) error {
}

ctx := cmd.MustLogCtx()
req := &control.GetAttachInfoReq{
AllRanks: true,
}
req.SetSystem(cmd.cfg.SystemName)
resp, err := control.GetAttachInfo(ctx, cmd.ctlInvoker, req)
resp, err := cmd.getAttachInfo(ctx)
if err != nil {
return errors.Wrap(err, "GetAttachInfo failed")
return err
}

if cmd.JSONOutputEnabled() {
Expand Down
57 changes: 42 additions & 15 deletions src/control/cmd/daos_agent/main.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// (C) Copyright 2018-2023 Intel Corporation.
// (C) Copyright 2018-2024 Intel Corporation.
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
Expand All @@ -26,20 +26,21 @@ import (
)

type cliOptions struct {
AllowProxy bool `long:"allow-proxy" description:"Allow proxy configuration via environment"`
Debug bool `short:"d" long:"debug" description:"Enable debug output"`
JSON bool `short:"j" long:"json" description:"Enable JSON output"`
JSONLogs bool `short:"J" long:"json-logging" description:"Enable JSON-formatted log output"`
ConfigPath string `short:"o" long:"config-path" description:"Path to agent configuration file"`
Insecure bool `short:"i" long:"insecure" description:"have agent attempt to connect without certificates"`
RuntimeDir string `short:"s" long:"runtime_dir" description:"Path to agent communications socket"`
LogFile string `short:"l" long:"logfile" description:"Full path and filename for daos agent log file"`
Start startCmd `command:"start" description:"Start daos_agent daemon (default behavior)"`
Version versionCmd `command:"version" description:"Print daos_agent version"`
DumpInfo dumpAttachInfoCmd `command:"dump-attachinfo" description:"Dump system attachinfo"`
DumpTopo hwprov.DumpTopologyCmd `command:"dump-topology" description:"Dump system topology"`
NetScan netScanCmd `command:"net-scan" description:"Perform local network fabric scan"`
Support supportCmd `command:"support" description:"Perform debug tasks to help support team"`
AllowProxy bool `long:"allow-proxy" description:"Allow proxy configuration via environment"`
Debug bool `short:"d" long:"debug" description:"Enable debug output"`
JSON bool `short:"j" long:"json" description:"Enable JSON output"`
JSONLogs bool `short:"J" long:"json-logging" description:"Enable JSON-formatted log output"`
ConfigPath string `short:"o" long:"config-path" description:"Path to agent configuration file"`
Insecure bool `short:"i" long:"insecure" description:"have agent attempt to connect without certificates"`
RuntimeDir string `short:"s" long:"runtime_dir" description:"Path to agent communications socket"`
LogFile string `short:"l" long:"logfile" description:"Full path and filename for daos agent log file"`
Start startCmd `command:"start" description:"Start daos_agent daemon (default behavior)"`
Version versionCmd `command:"version" description:"Print daos_agent version"`
ServerVersion serverVersionCmd `command:"server-version" description:"Print daos_server version"`
DumpInfo dumpAttachInfoCmd `command:"dump-attachinfo" description:"Dump system attachinfo"`
DumpTopo hwprov.DumpTopologyCmd `command:"dump-topology" description:"Dump system topology"`
NetScan netScanCmd `command:"net-scan" description:"Perform local network fabric scan"`
Support supportCmd `command:"support" description:"Perform debug tasks to help support team"`
}

type (
Expand Down Expand Up @@ -91,6 +92,32 @@ func (cmd *versionCmd) Execute(_ []string) error {
return err
}

type serverVersionCmd struct {
attachInfoCmd
}

func (cmd *serverVersionCmd) Execute(_ []string) error {
resp, err := cmd.getAttachInfo(cmd.MustLogCtx())
if err != nil {
return err
}

if cmd.JSONOutputEnabled() {
buf, err := json.Marshal(&build.Info{
Name: build.ControlPlaneName,
Version: resp.BuildInfo.VersionString(),
BuildInfo: resp.BuildInfo.Tag,
})
if err != nil {
return err
}
return cmd.OutputJSON(json.RawMessage(buf), nil)
}

_, err = fmt.Println(build.VersionString(build.ControlPlaneName, resp.BuildInfo.VersionString()))
return err
}

func exitWithError(log logging.Logger, err error) {
log.Errorf("%s: %v", path.Base(os.Args[0]), err)
os.Exit(1)
Expand Down
10 changes: 4 additions & 6 deletions src/control/cmd/daos_agent/mgmt_rpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
"google.golang.org/protobuf/proto"
Expand Down Expand Up @@ -218,11 +217,10 @@ func TestAgent_mgmtModule_getAttachInfo(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(tc.expResp, resp, cmpopts.IgnoreUnexported(
mgmtpb.GetAttachInfoResp{},
mgmtpb.GetAttachInfoResp_RankUri{},
mgmtpb.ClientNetHint{},
)); diff != "" {
cmpOpts := cmp.Options{
protocmp.Transform(),
}
if diff := cmp.Diff(tc.expResp, resp, cmpOpts...); diff != "" {
t.Fatalf("want-, got+:\n%s", diff)
}
})
Expand Down
4 changes: 3 additions & 1 deletion src/control/cmd/dmg/command_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// (C) Copyright 2019-2022 Intel Corporation.
// (C) Copyright 2019-2024 Intel Corporation.
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
Expand Down Expand Up @@ -173,6 +173,8 @@ func (bci *bridgeConnInvoker) InvokeUnaryRPC(ctx context.Context, uReq control.U
resp = control.MockMSResponse("", nil, &mgmtpb.DaosResp{})
case *control.SystemGetPropReq:
resp = control.MockMSResponse("", nil, &mgmtpb.SystemGetPropResp{})
case *control.GetAttachInfoReq:
resp = control.MockMSResponse("", nil, &mgmtpb.GetAttachInfoResp{})
case *control.NetworkScanReq:
resp = &control.UnaryResponse{
Responses: []*control.HostResponse{
Expand Down
Loading

0 comments on commit 833d393

Please sign in to comment.