Bläddra i källkod

etcdctl: support exec on lock

The lock command is clumsy to use from the command line, needing mkfifo,
wait, etc. Instead, make like consul and support launching a command if
one is given.
Anthony Romano 8 år sedan
förälder
incheckning
643c2a310d
2 ändrade filer med 38 tillägg och 7 borttagningar
  1. 12 1
      etcdctl/README.md
  2. 26 6
      etcdctl/ctlv3/command/lock_command.go

+ 12 - 1
etcdctl/README.md

@@ -790,7 +790,7 @@ Prints a line of JSON encoding the database hash, revision, total keys, and size
 
 
 ## Concurrency commands
 ## Concurrency commands
 
 
-### LOCK \<lockname\>
+### LOCK \<lockname\> [command arg1 arg2 ...]
 
 
 LOCK acquires a distributed named mutex with a given name. Once the lock is acquired, it will be held until etcdctl is terminated.
 LOCK acquires a distributed named mutex with a given name. Once the lock is acquired, it will be held until etcdctl is terminated.
 
 
@@ -798,13 +798,24 @@ LOCK acquires a distributed named mutex with a given name. Once the lock is acqu
 
 
 Once the lock is acquired, the result for the GET on the unique lock holder key is displayed.
 Once the lock is acquired, the result for the GET on the unique lock holder key is displayed.
 
 
+If a command is given, it will be launched with environment variables `ETCD_LOCK_KEY` and `ETCD_LOCK_REV` set to the lock's holder key and revision.
+
 #### Example
 #### Example
 
 
+Acquire lock with standard output display:
+
 ```bash
 ```bash
 ./etcdctl lock mylock
 ./etcdctl lock mylock
 # mylock/1234534535445
 # mylock/1234534535445
 ```
 ```
 
 
+Acquire lock and execute `echo lock acquired`:
+
+```bash
+./etcdctl lock mylock echo lock acquired
+# lock acquired
+```
+
 #### Remarks
 #### Remarks
 
 
 LOCK returns a zero exit code only if it is terminated by a signal and releases the lock.
 LOCK returns a zero exit code only if it is terminated by a signal and releases the lock.

+ 26 - 6
etcdctl/ctlv3/command/lock_command.go

@@ -16,7 +16,9 @@ package command
 
 
 import (
 import (
 	"errors"
 	"errors"
+	"fmt"
 	"os"
 	"os"
+	"os/exec"
 	"os/signal"
 	"os/signal"
 	"syscall"
 	"syscall"
 
 
@@ -29,7 +31,7 @@ import (
 // NewLockCommand returns the cobra command for "lock".
 // NewLockCommand returns the cobra command for "lock".
 func NewLockCommand() *cobra.Command {
 func NewLockCommand() *cobra.Command {
 	c := &cobra.Command{
 	c := &cobra.Command{
-		Use:   "lock <lockname>",
+		Use:   "lock <lockname> [exec-command arg1 arg2 ...]",
 		Short: "Acquires a named lock",
 		Short: "Acquires a named lock",
 		Run:   lockCommandFunc,
 		Run:   lockCommandFunc,
 	}
 	}
@@ -37,16 +39,16 @@ func NewLockCommand() *cobra.Command {
 }
 }
 
 
 func lockCommandFunc(cmd *cobra.Command, args []string) {
 func lockCommandFunc(cmd *cobra.Command, args []string) {
-	if len(args) != 1 {
-		ExitWithError(ExitBadArgs, errors.New("lock takes one lock name argument."))
+	if len(args) == 0 {
+		ExitWithError(ExitBadArgs, errors.New("lock takes a lock name argument and an optional command to execute."))
 	}
 	}
 	c := mustClientFromCmd(cmd)
 	c := mustClientFromCmd(cmd)
-	if err := lockUntilSignal(c, args[0]); err != nil {
+	if err := lockUntilSignal(c, args[0], args[1:]); err != nil {
 		ExitWithError(ExitError, err)
 		ExitWithError(ExitError, err)
 	}
 	}
 }
 }
 
 
-func lockUntilSignal(c *clientv3.Client, lockname string) error {
+func lockUntilSignal(c *clientv3.Client, lockname string, cmdArgs []string) error {
 	s, err := concurrency.NewSession(c)
 	s, err := concurrency.NewSession(c)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -69,6 +71,18 @@ func lockUntilSignal(c *clientv3.Client, lockname string) error {
 		return err
 		return err
 	}
 	}
 
 
+	if len(cmdArgs) > 0 {
+		cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
+		cmd.Env = append(environLockResponse(m), os.Environ()...)
+		cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
+		err := cmd.Run()
+		unlockErr := m.Unlock(context.TODO())
+		if err != nil {
+			return err
+		}
+		return unlockErr
+	}
+
 	k, kerr := c.Get(ctx, m.Key())
 	k, kerr := c.Get(ctx, m.Key())
 	if kerr != nil {
 	if kerr != nil {
 		return kerr
 		return kerr
@@ -76,7 +90,6 @@ func lockUntilSignal(c *clientv3.Client, lockname string) error {
 	if len(k.Kvs) == 0 {
 	if len(k.Kvs) == 0 {
 		return errors.New("lock lost on init")
 		return errors.New("lock lost on init")
 	}
 	}
-
 	display.Get(*k)
 	display.Get(*k)
 
 
 	select {
 	select {
@@ -87,3 +100,10 @@ func lockUntilSignal(c *clientv3.Client, lockname string) error {
 
 
 	return errors.New("session expired")
 	return errors.New("session expired")
 }
 }
+
+func environLockResponse(m *concurrency.Mutex) []string {
+	return []string{
+		"ETCD_LOCK_KEY=" + m.Key(),
+		fmt.Sprintf("ETCD_LOCK_REV=%d", m.Header().Revision),
+	}
+}