Merge pull request #8268 from hashicorp/feature/improved-version-output
Add Revision to version CLI output and add JSON support
This commit is contained in:
commit
c9ccbab65a
|
@ -220,6 +220,6 @@ func init() {
|
|||
Register("tls cert", func(ui cli.Ui) (cli.Command, error) { return tlscert.New(), nil })
|
||||
Register("tls cert create", func(ui cli.Ui) (cli.Command, error) { return tlscertcreate.New(ui), nil })
|
||||
Register("validate", func(ui cli.Ui) (cli.Command, error) { return validate.New(ui), nil })
|
||||
Register("version", func(ui cli.Ui) (cli.Command, error) { return version.New(ui, verHuman), nil })
|
||||
Register("version", func(ui cli.Ui) (cli.Command, error) { return version.New(ui), nil })
|
||||
Register("watch", func(ui cli.Ui) (cli.Command, error) { return watch.New(ui, MakeShutdownCh()), nil })
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
package version
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
PrettyFormat string = "pretty"
|
||||
JSONFormat string = "json"
|
||||
)
|
||||
|
||||
type Formatter interface {
|
||||
Format(info *VersionInfo) (string, error)
|
||||
}
|
||||
|
||||
func GetSupportedFormats() []string {
|
||||
return []string{PrettyFormat, JSONFormat}
|
||||
}
|
||||
|
||||
func NewFormatter(format string) (Formatter, error) {
|
||||
switch format {
|
||||
case PrettyFormat:
|
||||
return newPrettyFormatter(), nil
|
||||
case JSONFormat:
|
||||
return newJSONFormatter(), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("Unknown format: %s", format)
|
||||
}
|
||||
}
|
||||
|
||||
type prettyFormatter struct{}
|
||||
|
||||
func newPrettyFormatter() Formatter {
|
||||
return &prettyFormatter{}
|
||||
}
|
||||
|
||||
func (_ *prettyFormatter) Format(info *VersionInfo) (string, error) {
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteString(fmt.Sprintf("Consul %s\n", info.HumanVersion))
|
||||
if info.Revision != "" {
|
||||
buffer.WriteString(fmt.Sprintf("Revision %s\n", info.Revision))
|
||||
}
|
||||
|
||||
var supplement string
|
||||
if info.RPC.Default < info.RPC.Max {
|
||||
supplement = fmt.Sprintf(" (agent will automatically use protocol >%d when speaking to compatible agents)",
|
||||
info.RPC.Default)
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf("Protocol %d spoken by default, understands %d to %d%s\n",
|
||||
info.RPC.Default, info.RPC.Min, info.RPC.Max, supplement))
|
||||
|
||||
return buffer.String(), nil
|
||||
}
|
||||
|
||||
type jsonFormatter struct{}
|
||||
|
||||
func newJSONFormatter() Formatter {
|
||||
return &jsonFormatter{}
|
||||
}
|
||||
|
||||
func (_ *jsonFormatter) Format(info *VersionInfo) (string, error) {
|
||||
b, err := json.MarshalIndent(info, "", " ")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to marshal version info: %v", err)
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package version
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// update allows golden files to be updated based on the current output.
|
||||
var update = flag.Bool("update", false, "update golden files")
|
||||
|
||||
// golden reads and optionally writes the expected data to the golden file,
|
||||
// returning the contents as a string.
|
||||
func golden(t *testing.T, name, got string) string {
|
||||
t.Helper()
|
||||
|
||||
golden := filepath.Join("testdata", name+".golden")
|
||||
if *update && got != "" {
|
||||
err := ioutil.WriteFile(golden, []byte(got), 0644)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
expected, err := ioutil.ReadFile(golden)
|
||||
require.NoError(t, err)
|
||||
|
||||
return string(expected)
|
||||
}
|
||||
|
||||
func TestFormat(t *testing.T) {
|
||||
info := VersionInfo{
|
||||
HumanVersion: "1.99.3-beta1",
|
||||
Version: "1.99.3",
|
||||
Prerelease: "beta1",
|
||||
Revision: "5e5dbedd47a5f875b60e241c5555a9caab595246",
|
||||
RPC: RPCVersionInfo{
|
||||
Default: 2,
|
||||
Min: 1,
|
||||
Max: 3,
|
||||
},
|
||||
}
|
||||
|
||||
formatters := map[string]Formatter{
|
||||
"pretty": newPrettyFormatter(),
|
||||
// the JSON formatter ignores the showMeta
|
||||
"json": newJSONFormatter(),
|
||||
}
|
||||
|
||||
for fmtName, formatter := range formatters {
|
||||
t.Run(fmtName, func(t *testing.T) {
|
||||
actual, err := formatter.Format(&info)
|
||||
require.NoError(t, err)
|
||||
|
||||
gName := fmt.Sprintf("%s", fmtName)
|
||||
|
||||
expected := golden(t, gName, actual)
|
||||
require.Equal(t, expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"Version": "1.99.3",
|
||||
"Revision": "5e5dbedd47a5f875b60e241c5555a9caab595246",
|
||||
"Prerelease": "beta1",
|
||||
"RPC": {
|
||||
"Default": 2,
|
||||
"Min": 1,
|
||||
"Max": 3
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
Consul 1.99.3-beta1
|
||||
Revision 5e5dbedd47a5f875b60e241c5555a9caab595246
|
||||
Protocol 2 spoken by default, understands 1 to 3 (agent will automatically use protocol >2 when speaking to compatible agents)
|
|
@ -1,34 +1,80 @@
|
|||
package version
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/consul/agent/consul"
|
||||
"github.com/hashicorp/consul/command/flags"
|
||||
"github.com/hashicorp/consul/version"
|
||||
"github.com/mitchellh/cli"
|
||||
)
|
||||
|
||||
func New(ui cli.Ui, version string) *cmd {
|
||||
return &cmd{UI: ui, version: version}
|
||||
func New(ui cli.Ui) *cmd {
|
||||
c := &cmd{UI: ui}
|
||||
c.init()
|
||||
return c
|
||||
}
|
||||
|
||||
type cmd struct {
|
||||
UI cli.Ui
|
||||
version string
|
||||
UI cli.Ui
|
||||
flags *flag.FlagSet
|
||||
format string
|
||||
help string
|
||||
}
|
||||
|
||||
func (c *cmd) Run(_ []string) int {
|
||||
c.UI.Output(fmt.Sprintf("Consul %s", c.version))
|
||||
func (c *cmd) init() {
|
||||
c.flags = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
c.flags.StringVar(
|
||||
&c.format,
|
||||
"format",
|
||||
PrettyFormat,
|
||||
fmt.Sprintf("Output format {%s}", strings.Join(GetSupportedFormats(), "|")))
|
||||
c.help = flags.Usage(help, c.flags)
|
||||
|
||||
const rpcProtocol = consul.DefaultRPCProtocol
|
||||
}
|
||||
|
||||
var supplement string
|
||||
if rpcProtocol < consul.ProtocolVersionMax {
|
||||
supplement = fmt.Sprintf(" (agent will automatically use protocol >%d when speaking to compatible agents)",
|
||||
rpcProtocol)
|
||||
type RPCVersionInfo struct {
|
||||
Default int
|
||||
Min int
|
||||
Max int
|
||||
}
|
||||
|
||||
type VersionInfo struct {
|
||||
HumanVersion string `json:"-"`
|
||||
Version string
|
||||
Revision string
|
||||
Prerelease string
|
||||
RPC RPCVersionInfo
|
||||
}
|
||||
|
||||
func (c *cmd) Run(args []string) int {
|
||||
if err := c.flags.Parse(args); err != nil {
|
||||
return 1
|
||||
}
|
||||
c.UI.Output(fmt.Sprintf("Protocol %d spoken by default, understands %d to %d%s",
|
||||
rpcProtocol, consul.ProtocolVersionMin, consul.ProtocolVersionMax, supplement))
|
||||
|
||||
formatter, err := NewFormatter(c.format)
|
||||
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,
|
||||
RPC: RPCVersionInfo{
|
||||
Default: consul.DefaultRPCProtocol,
|
||||
Min: int(consul.ProtocolVersionMin),
|
||||
Max: consul.ProtocolVersionMax,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
c.UI.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
c.UI.Output(out)
|
||||
return 0
|
||||
}
|
||||
|
||||
|
@ -37,5 +83,10 @@ func (c *cmd) Synopsis() string {
|
|||
}
|
||||
|
||||
func (c *cmd) Help() string {
|
||||
return ""
|
||||
return flags.Usage(c.help, nil)
|
||||
}
|
||||
|
||||
const synopsis = "Output Consul version information"
|
||||
const help = `
|
||||
Usage: consul version [options]
|
||||
`
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
func TestVersionCommand_noTabs(t *testing.T) {
|
||||
t.Parallel()
|
||||
if strings.ContainsRune(New(cli.NewMockUi(), "").Help(), '\t') {
|
||||
if strings.ContainsRune(New(cli.NewMockUi()).Help(), '\t') {
|
||||
t.Fatal("help has tabs")
|
||||
}
|
||||
}
|
||||
|
|
3
main.go
3
main.go
|
@ -10,7 +10,6 @@ import (
|
|||
"github.com/hashicorp/consul/command/version"
|
||||
"github.com/hashicorp/consul/lib"
|
||||
_ "github.com/hashicorp/consul/service_os"
|
||||
consulversion "github.com/hashicorp/consul/version"
|
||||
"github.com/mitchellh/cli"
|
||||
)
|
||||
|
||||
|
@ -43,7 +42,7 @@ func realMain() int {
|
|||
}
|
||||
|
||||
if cli.IsVersion() {
|
||||
cmd := version.New(ui, consulversion.GetHumanVersion())
|
||||
cmd := version.New(ui)
|
||||
return cmd.Run(nil)
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,33 @@ Command: `consul version`
|
|||
|
||||
The `version` command prints the version of Consul and the protocol versions it understands for speaking to other agents.
|
||||
|
||||
## Usage
|
||||
|
||||
Usage: `consul version [options]`
|
||||
|
||||
### Command Options
|
||||
|
||||
- `-format={pretty|json}` - Command output format. The default value is `pretty`.
|
||||
|
||||
## Plain Output
|
||||
```shell-session
|
||||
$ consul version
|
||||
Consul v0.7.4
|
||||
Consul v1.7.0
|
||||
Revision d1fc59061
|
||||
Protocol 2 spoken by default, understands 2 to 3 (agent will automatically use protocol >2 when speaking to compatible agents)
|
||||
```
|
||||
|
||||
## JSON Output
|
||||
```shell-session
|
||||
$ consul version -format=json
|
||||
{
|
||||
"Version": "1.8.0",
|
||||
"Revision": "d1fc59061",
|
||||
"Prerelease": "dev",
|
||||
"RPC": {
|
||||
"Default": 2,
|
||||
"Min": 2,
|
||||
"Max": 3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
Loading…
Reference in New Issue