diff --git a/etcdutl/ctl.go b/etcdutl/ctl.go index cf85e11d9..a044547c6 100644 --- a/etcdutl/ctl.go +++ b/etcdutl/ctl.go @@ -40,6 +40,7 @@ func init() { etcdutl.NewBackupCommand(), etcdutl.NewDefragCommand(), etcdutl.NewSnapshotCommand(), + etcdutl.NewVersionCommand(), ) } diff --git a/etcdutl/etcdutl/common.go b/etcdutl/etcdutl/common.go index 351a23761..f3ebe3523 100644 --- a/etcdutl/etcdutl/common.go +++ b/etcdutl/etcdutl/common.go @@ -17,11 +17,13 @@ package etcdutl import ( "go.etcd.io/etcd/pkg/v3/cobrautl" "go.uber.org/zap" + "go.uber.org/zap/zapcore" ) func GetLogger() *zap.Logger { config := zap.NewProductionConfig() config.Encoding = "console" + config.EncoderConfig.EncodeTime=zapcore.RFC3339TimeEncoder lg, err := config.Build() if err != nil { cobrautl.ExitWithError(cobrautl.ExitBadArgs, err) diff --git a/etcdutl/etcdutl/version_command.go b/etcdutl/etcdutl/version_command.go new file mode 100644 index 000000000..1cb1a146b --- /dev/null +++ b/etcdutl/etcdutl/version_command.go @@ -0,0 +1,37 @@ +// Copyright 2021 The etcd Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package etcdutl + +import ( + "fmt" + + "go.etcd.io/etcd/api/v3/version" + + "github.com/spf13/cobra" +) + +// NewVersionCommand prints out the version of etcd. +func NewVersionCommand() *cobra.Command { + return &cobra.Command{ + Use: "version", + Short: "Prints the version of etcdutl", + Run: versionCommandFunc, + } +} + +func versionCommandFunc(cmd *cobra.Command, args []string) { + fmt.Println("etcdutl version:", version.Version) + fmt.Println("API version:", version.APIVersion) +} diff --git a/etcdutl/main_test.go b/etcdutl/main_test.go new file mode 100644 index 000000000..b54b2ba23 --- /dev/null +++ b/etcdutl/main_test.go @@ -0,0 +1,66 @@ +// Copyright 2017 The etcd Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "log" + "os" + "strings" + "testing" +) + +func SplitTestArgs(args []string) (testArgs, appArgs []string) { + for i, arg := range os.Args { + switch { + case strings.HasPrefix(arg, "-test."): + testArgs = append(testArgs, arg) + case i == 0: + appArgs = append(appArgs, arg) + testArgs = append(testArgs, arg) + default: + appArgs = append(appArgs, arg) + } + } + return +} + +// Empty test to avoid no-tests warning. +func TestEmpty(t *testing.T) {} + +/** + * The purpose of this "test" is to run etcdctl with code-coverage + * collection turned on. The technique is documented here: + * + * https://www.cyphar.com/blog/post/20170412-golang-integration-coverage + */ +func TestMain(m *testing.M) { + // don't launch etcdutl when invoked via go test + if strings.HasSuffix(os.Args[0], "etcdutl.test") { + return + } + + testArgs, appArgs := SplitTestArgs(os.Args) + + os.Args = appArgs + + err := Start() + if err != nil { + log.Fatalf("etcdctl failed with: %v", err) + } + + // This will generate coverage files: + os.Args = testArgs + m.Run() +}