etcdctl: lock return exit code of exec-command

Sometimes we expect to get the exit code of the command being
executed.
release-3.5
garenchan 2021-04-05 11:55:55 +08:00
parent dfb03aba0b
commit c047ed593c
3 changed files with 46 additions and 1 deletions

View File

@ -205,6 +205,7 @@ Note that any `etcd_debugging_*` metrics are experimental and subject to change.
- Add [`etcdctl auth status`](https://github.com/etcd-io/etcd/pull/11536) command to check if authentication is enabled
- Add [`etcdctl get --count-only`](https://github.com/etcd-io/etcd/pull/11743) flag for output type `fields`.
- Add [`etcdctl member list -w=json --hex`](https://github.com/etcd-io/etcd/pull/11812) flag to print memberListResponse in hex format json.
- Changed [`etcdctl lock <lockname> exec-command`](https://github.com/etcd-io/etcd/pull/12829) to return exit code of exec-command.
### gRPC gateway

View File

@ -48,10 +48,25 @@ func lockCommandFunc(cmd *cobra.Command, args []string) {
}
c := mustClientFromCmd(cmd)
if err := lockUntilSignal(c, args[0], args[1:]); err != nil {
ExitWithError(ExitError, err)
code := getExitCodeFromError(err)
ExitWithError(code, err)
}
}
func getExitCodeFromError(err error) int {
if err == nil {
return ExitSuccess
}
if exitErr, ok := err.(*exec.ExitError); ok {
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
return status.ExitStatus()
}
}
return ExitError
}
func lockUntilSignal(c *clientv3.Client, lockname string, cmdArgs []string) error {
s, err := concurrency.NewSession(c, concurrency.WithTTL(lockTTL))
if err != nil {

View File

@ -15,6 +15,7 @@
package e2e
import (
"fmt"
"os"
"strings"
"testing"
@ -27,6 +28,10 @@ func TestCtlV3Lock(t *testing.T) {
testCtl(t, testLock)
}
func TestCtlV3LockWithCmd(t *testing.T) {
testCtl(t, testLockWithCmd)
}
func testLock(cx ctlCtx) {
name := "a"
@ -95,6 +100,22 @@ func testLock(cx ctlCtx) {
}
}
func testLockWithCmd(cx ctlCtx) {
// exec command with zero exit code
echoCmd := []string{"echo"}
if err := ctlV3LockWithCmd(cx, echoCmd, ""); err != nil {
cx.t.Fatal(err)
}
// exec command with non-zero exit code
code := 3
awkCmd := []string{"awk", fmt.Sprintf("BEGIN{exit %d}", code)}
expect := fmt.Sprintf("Error: exit status %d", code)
if err := ctlV3LockWithCmd(cx, awkCmd, expect); err != nil {
cx.t.Fatal(err)
}
}
// ctlV3Lock creates a lock process with a channel listening for when it acquires the lock.
func ctlV3Lock(cx ctlCtx, name string) (*expect.ExpectProcess, <-chan string, error) {
cmdArgs := append(cx.PrefixArgs(), "lock", name)
@ -113,3 +134,11 @@ func ctlV3Lock(cx ctlCtx, name string) (*expect.ExpectProcess, <-chan string, er
}()
return proc, outc, err
}
// ctlV3LockWithCmd creates a lock process to exec command.
func ctlV3LockWithCmd(cx ctlCtx, execCmd []string, as ...string) error {
// use command as lock name
cmdArgs := append(cx.PrefixArgs(), "lock", execCmd[0])
cmdArgs = append(cmdArgs, execCmd...)
return spawnWithExpects(cmdArgs, as...)
}