Browse Source

etcdctl/ctlv3: add 'lease timetolive' command

Gyu-Ho Lee 9 years ago
parent
commit
04a4cea630
3 changed files with 109 additions and 21 deletions
  1. 34 0
      etcdctl/README.md
  2. 44 12
      etcdctl/ctlv3/command/lease_command.go
  3. 31 9
      etcdctl/ctlv3/command/printer.go

+ 34 - 0
etcdctl/README.md

@@ -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.

+ 44 - 12
etcdctl/ctlv3/command/lease_command.go

@@ -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)
+}

+ 31 - 9
etcdctl/ctlv3/command/printer.go

@@ -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))
 }