mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
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.
This commit is contained in:
parent
8c1ab62bc5
commit
643c2a310d
@ -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.
|
||||||
|
@ -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 {
|
if len(args) == 0 {
|
||||||
ExitWithError(ExitBadArgs, errors.New("lock takes one lock name argument."))
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user