etcdctl/ctlv3: add 'lease timetolive' command

This commit is contained in:
Gyu-Ho Lee 2016-09-09 08:21:58 +09:00
parent 4c08f6767c
commit 04a4cea630
3 changed files with 109 additions and 21 deletions

View File

@ -326,6 +326,40 @@ LEASE REVOKE destroys a given lease, deleting all attached keys.
lease 32695410dcc0ca06 revoked
```
### LEASE TIMETOLIVE \<leaseID\>
LEASE TIMETOLIVE retrieves the lease information with the given lease ID.
#### Return value
- On success, prints lease information.
- On failure, prints an error message and returns with a non-zero exit code.
#### Example
```bash
./etcdctl lease grant 500
lease 2d8257079fa1bc0c granted with TTL(500s)
./etcdctl put foo1 bar --lease=2d8257079fa1bc0c
./etcdctl put foo2 bar --lease=2d8257079fa1bc0c
./etcdctl lease timetolive 2d8257079fa1bc0c
lease 2d8257079fa1bc0c granted with TTL(500s), remaining(481s)
./etcdctl lease timetolive 2d8257079fa1bc0c --keys
lease 2d8257079fa1bc0c granted with TTL(500s), remaining(472s), attached keys([foo2 foo1])
./etcdctl lease timetolive 2d8257079fa1bc0c --write-out=json
{"cluster_id":17186838941855831277,"member_id":4845372305070271874,"revision":3,"raft_term":2,"id":3279279168933706764,"ttl":465,"granted-ttl":500,"keys":null}
./etcdctl lease timetolive 2d8257079fa1bc0c --write-out=json --keys
{"cluster_id":17186838941855831277,"member_id":4845372305070271874,"revision":3,"raft_term":2,"id":3279279168933706764,"ttl":459,"granted-ttl":500,"keys":["Zm9vMQ==","Zm9vMg=="]}
```
### LEASE KEEP-ALIVE \<leaseID\>
LEASE KEEP-ALIVE periodically refreshes a lease so it does not expire.

View File

@ -32,6 +32,7 @@ func NewLeaseCommand() *cobra.Command {
lc.AddCommand(NewLeaseGrantCommand())
lc.AddCommand(NewLeaseRevokeCommand())
lc.AddCommand(NewLeaseTimeToLiveCommand())
lc.AddCommand(NewLeaseKeepAliveCommand())
return lc
@ -87,13 +88,9 @@ func leaseRevokeCommandFunc(cmd *cobra.Command, args []string) {
ExitWithError(ExitBadArgs, fmt.Errorf("lease revoke command needs 1 argument"))
}
id, err := strconv.ParseInt(args[0], 16, 64)
if err != nil {
ExitWithError(ExitBadArgs, fmt.Errorf("bad lease ID arg (%v), expecting ID in Hex", err))
}
id := leaseFromArgs(args[0])
ctx, cancel := commandCtx(cmd)
_, err = mustClientFromCmd(cmd).Revoke(ctx, v3.LeaseID(id))
_, err := mustClientFromCmd(cmd).Revoke(ctx, id)
cancel()
if err != nil {
ExitWithError(ExitError, fmt.Errorf("failed to revoke lease (%v)\n", err))
@ -101,6 +98,37 @@ func leaseRevokeCommandFunc(cmd *cobra.Command, args []string) {
fmt.Printf("lease %016x revoked\n", id)
}
var timeToLiveKeys bool
// NewLeaseTimeToLiveCommand returns the cobra command for "lease timetolive".
func NewLeaseTimeToLiveCommand() *cobra.Command {
lc := &cobra.Command{
Use: "timetolive <leaseID>",
Short: "Get lease information",
Run: leaseTimeToLiveCommandFunc,
}
lc.Flags().BoolVar(&timeToLiveKeys, "keys", false, "Get keys attached to this lease")
return lc
}
// leaseTimeToLiveCommandFunc executes the "lease timetolive" command.
func leaseTimeToLiveCommandFunc(cmd *cobra.Command, args []string) {
if len(args) != 1 {
ExitWithError(ExitBadArgs, fmt.Errorf("lease timetolive command needs lease ID as argument"))
}
var opts []v3.LeaseOption
if timeToLiveKeys {
opts = append(opts, v3.WithAttachedKeys())
}
resp, rerr := mustClientFromCmd(cmd).TimeToLive(context.TODO(), leaseFromArgs(args[0]), opts...)
if rerr != nil {
ExitWithError(ExitBadConnection, rerr)
}
display.TimeToLive(*resp, timeToLiveKeys)
}
// NewLeaseKeepAliveCommand returns the cobra command for "lease keep-alive".
func NewLeaseKeepAliveCommand() *cobra.Command {
lc := &cobra.Command{
@ -119,12 +147,8 @@ func leaseKeepAliveCommandFunc(cmd *cobra.Command, args []string) {
ExitWithError(ExitBadArgs, fmt.Errorf("lease keep-alive command needs lease ID as argument"))
}
id, err := strconv.ParseInt(args[0], 16, 64)
if err != nil {
ExitWithError(ExitBadArgs, fmt.Errorf("bad lease ID arg (%v), expecting ID in Hex", err))
}
respc, kerr := mustClientFromCmd(cmd).KeepAlive(context.TODO(), v3.LeaseID(id))
id := leaseFromArgs(args[0])
respc, kerr := mustClientFromCmd(cmd).KeepAlive(context.TODO(), id)
if kerr != nil {
ExitWithError(ExitBadConnection, kerr)
}
@ -134,3 +158,11 @@ func leaseKeepAliveCommandFunc(cmd *cobra.Command, args []string) {
}
fmt.Printf("lease %016x expired or revoked.\n", id)
}
func leaseFromArgs(arg string) v3.LeaseID {
id, err := strconv.ParseInt(arg, 16, 64)
if err != nil {
ExitWithError(ExitBadArgs, fmt.Errorf("bad lease ID arg (%v), expecting ID in Hex", err))
}
return v3.LeaseID(id)
}

View File

@ -35,6 +35,8 @@ type printer interface {
Txn(v3.TxnResponse)
Watch(v3.WatchResponse)
TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool)
MemberList(v3.MemberListResponse)
EndpointStatus([]epStatus)
@ -159,6 +161,18 @@ func (s *simplePrinter) Watch(resp v3.WatchResponse) {
}
}
func (s *simplePrinter) TimeToLive(resp v3.LeaseTimeToLiveResponse, keys bool) {
txt := fmt.Sprintf("lease %016x granted with TTL(%ds), remaining(%ds)", resp.ID, resp.GrantedTTL, resp.TTL)
if keys {
ks := make([]string, len(resp.Keys))
for i := range resp.Keys {
ks[i] = string(resp.Keys[i])
}
txt += fmt.Sprintf(", attached keys(%v)", ks)
}
fmt.Println(txt)
}
func (s *simplePrinter) Alarm(resp v3.AlarmResponse) {
for _, e := range resp.Alarms {
fmt.Printf("%+v\n", e)
@ -203,6 +217,9 @@ func (tp *tablePrinter) Txn(r v3.TxnResponse) {
func (tp *tablePrinter) Watch(r v3.WatchResponse) {
ExitWithError(ExitBadFeature, errors.New("table is not supported as output format"))
}
func (tp *tablePrinter) TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool) {
ExitWithError(ExitBadFeature, errors.New("table is not supported as output format"))
}
func (tp *tablePrinter) Alarm(r v3.AlarmResponse) {
ExitWithError(ExitBadFeature, errors.New("table is not supported as output format"))
}
@ -236,15 +253,16 @@ func (tp *tablePrinter) DBStatus(r dbstatus) {
type jsonPrinter struct{}
func (p *jsonPrinter) Del(r v3.DeleteResponse) { printJSON(r) }
func (p *jsonPrinter) Get(r v3.GetResponse) { printJSON(r) }
func (p *jsonPrinter) Put(r v3.PutResponse) { printJSON(r) }
func (p *jsonPrinter) Txn(r v3.TxnResponse) { printJSON(r) }
func (p *jsonPrinter) Watch(r v3.WatchResponse) { printJSON(r) }
func (p *jsonPrinter) Alarm(r v3.AlarmResponse) { printJSON(r) }
func (p *jsonPrinter) MemberList(r v3.MemberListResponse) { printJSON(r) }
func (p *jsonPrinter) EndpointStatus(r []epStatus) { printJSON(r) }
func (p *jsonPrinter) DBStatus(r dbstatus) { printJSON(r) }
func (p *jsonPrinter) Del(r v3.DeleteResponse) { printJSON(r) }
func (p *jsonPrinter) Get(r v3.GetResponse) { printJSON(r) }
func (p *jsonPrinter) Put(r v3.PutResponse) { printJSON(r) }
func (p *jsonPrinter) Txn(r v3.TxnResponse) { printJSON(r) }
func (p *jsonPrinter) Watch(r v3.WatchResponse) { printJSON(r) }
func (p *jsonPrinter) TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool) { printJSON(r) }
func (p *jsonPrinter) Alarm(r v3.AlarmResponse) { printJSON(r) }
func (p *jsonPrinter) MemberList(r v3.MemberListResponse) { printJSON(r) }
func (p *jsonPrinter) EndpointStatus(r []epStatus) { printJSON(r) }
func (p *jsonPrinter) DBStatus(r dbstatus) { printJSON(r) }
func printJSON(v interface{}) {
b, err := json.Marshal(v)
@ -283,6 +301,10 @@ func (p *pbPrinter) Watch(r v3.WatchResponse) {
}
}
func (p *pbPrinter) TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool) {
ExitWithError(ExitBadFeature, errors.New("only support simple or json as output format"))
}
func (p *pbPrinter) Alarm(r v3.AlarmResponse) {
printPB((*pb.AlarmResponse)(&r))
}