Skip to content

Commit

Permalink
etcdctl: lock return exit code of exec-command
Browse files Browse the repository at this point in the history
Sometimes we expect to get the exit code of the command being
executed.
  • Loading branch information
garenchan committed Apr 6, 2021
1 parent dfb03ab commit c047ed5
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG-3.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
17 changes: 16 additions & 1 deletion etcdctl/ctlv3/command/lock_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
29 changes: 29 additions & 0 deletions tests/e2e/ctl_v3_lock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package e2e

import (
"fmt"
"os"
"strings"
"testing"
Expand All @@ -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"

Expand Down Expand Up @@ -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)
Expand All @@ -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...)
}

0 comments on commit c047ed5

Please sign in to comment.