1// Copyright 2016 The etcd Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package command
16
17import (
18	"context"
19	"fmt"
20	"strconv"
21
22	v3 "github.com/coreos/etcd/clientv3"
23
24	"github.com/spf13/cobra"
25)
26
27// NewLeaseCommand returns the cobra command for "lease".
28func NewLeaseCommand() *cobra.Command {
29	lc := &cobra.Command{
30		Use:   "lease <subcommand>",
31		Short: "Lease related commands",
32	}
33
34	lc.AddCommand(NewLeaseGrantCommand())
35	lc.AddCommand(NewLeaseRevokeCommand())
36	lc.AddCommand(NewLeaseTimeToLiveCommand())
37	lc.AddCommand(NewLeaseListCommand())
38	lc.AddCommand(NewLeaseKeepAliveCommand())
39
40	return lc
41}
42
43// NewLeaseGrantCommand returns the cobra command for "lease grant".
44func NewLeaseGrantCommand() *cobra.Command {
45	lc := &cobra.Command{
46		Use:   "grant <ttl>",
47		Short: "Creates leases",
48
49		Run: leaseGrantCommandFunc,
50	}
51
52	return lc
53}
54
55// leaseGrantCommandFunc executes the "lease grant" command.
56func leaseGrantCommandFunc(cmd *cobra.Command, args []string) {
57	if len(args) != 1 {
58		ExitWithError(ExitBadArgs, fmt.Errorf("lease grant command needs TTL argument."))
59	}
60
61	ttl, err := strconv.ParseInt(args[0], 10, 64)
62	if err != nil {
63		ExitWithError(ExitBadArgs, fmt.Errorf("bad TTL (%v)", err))
64	}
65
66	ctx, cancel := commandCtx(cmd)
67	resp, err := mustClientFromCmd(cmd).Grant(ctx, ttl)
68	cancel()
69	if err != nil {
70		ExitWithError(ExitError, fmt.Errorf("failed to grant lease (%v)\n", err))
71	}
72	display.Grant(*resp)
73}
74
75// NewLeaseRevokeCommand returns the cobra command for "lease revoke".
76func NewLeaseRevokeCommand() *cobra.Command {
77	lc := &cobra.Command{
78		Use:   "revoke <leaseID>",
79		Short: "Revokes leases",
80
81		Run: leaseRevokeCommandFunc,
82	}
83
84	return lc
85}
86
87// leaseRevokeCommandFunc executes the "lease grant" command.
88func leaseRevokeCommandFunc(cmd *cobra.Command, args []string) {
89	if len(args) != 1 {
90		ExitWithError(ExitBadArgs, fmt.Errorf("lease revoke command needs 1 argument"))
91	}
92
93	id := leaseFromArgs(args[0])
94	ctx, cancel := commandCtx(cmd)
95	resp, err := mustClientFromCmd(cmd).Revoke(ctx, id)
96	cancel()
97	if err != nil {
98		ExitWithError(ExitError, fmt.Errorf("failed to revoke lease (%v)\n", err))
99	}
100	display.Revoke(id, *resp)
101}
102
103var timeToLiveKeys bool
104
105// NewLeaseTimeToLiveCommand returns the cobra command for "lease timetolive".
106func NewLeaseTimeToLiveCommand() *cobra.Command {
107	lc := &cobra.Command{
108		Use:   "timetolive <leaseID> [options]",
109		Short: "Get lease information",
110
111		Run: leaseTimeToLiveCommandFunc,
112	}
113	lc.Flags().BoolVar(&timeToLiveKeys, "keys", false, "Get keys attached to this lease")
114
115	return lc
116}
117
118// leaseTimeToLiveCommandFunc executes the "lease timetolive" command.
119func leaseTimeToLiveCommandFunc(cmd *cobra.Command, args []string) {
120	if len(args) != 1 {
121		ExitWithError(ExitBadArgs, fmt.Errorf("lease timetolive command needs lease ID as argument"))
122	}
123	var opts []v3.LeaseOption
124	if timeToLiveKeys {
125		opts = append(opts, v3.WithAttachedKeys())
126	}
127	resp, rerr := mustClientFromCmd(cmd).TimeToLive(context.TODO(), leaseFromArgs(args[0]), opts...)
128	if rerr != nil {
129		ExitWithError(ExitBadConnection, rerr)
130	}
131	display.TimeToLive(*resp, timeToLiveKeys)
132}
133
134// NewLeaseListCommand returns the cobra command for "lease list".
135func NewLeaseListCommand() *cobra.Command {
136	lc := &cobra.Command{
137		Use:   "list",
138		Short: "List all active leases",
139		Run:   leaseListCommandFunc,
140	}
141	return lc
142}
143
144// leaseListCommandFunc executes the "lease list" command.
145func leaseListCommandFunc(cmd *cobra.Command, args []string) {
146	resp, rerr := mustClientFromCmd(cmd).Leases(context.TODO())
147	if rerr != nil {
148		ExitWithError(ExitBadConnection, rerr)
149	}
150	display.Leases(*resp)
151}
152
153var (
154	leaseKeepAliveOnce bool
155)
156
157// NewLeaseKeepAliveCommand returns the cobra command for "lease keep-alive".
158func NewLeaseKeepAliveCommand() *cobra.Command {
159	lc := &cobra.Command{
160		Use:   "keep-alive [options] <leaseID>",
161		Short: "Keeps leases alive (renew)",
162
163		Run: leaseKeepAliveCommandFunc,
164	}
165
166	lc.Flags().BoolVar(&leaseKeepAliveOnce, "once", false, "Resets the keep-alive time to its original value and exits immediately")
167
168	return lc
169}
170
171// leaseKeepAliveCommandFunc executes the "lease keep-alive" command.
172func leaseKeepAliveCommandFunc(cmd *cobra.Command, args []string) {
173	if len(args) != 1 {
174		ExitWithError(ExitBadArgs, fmt.Errorf("lease keep-alive command needs lease ID as argument"))
175	}
176
177	id := leaseFromArgs(args[0])
178
179	if leaseKeepAliveOnce {
180		respc, kerr := mustClientFromCmd(cmd).KeepAliveOnce(context.TODO(), id)
181		if kerr != nil {
182			ExitWithError(ExitBadConnection, kerr)
183		}
184		display.KeepAlive(*respc)
185		return
186	}
187
188	respc, kerr := mustClientFromCmd(cmd).KeepAlive(context.TODO(), id)
189	if kerr != nil {
190		ExitWithError(ExitBadConnection, kerr)
191	}
192	for resp := range respc {
193		display.KeepAlive(*resp)
194	}
195
196	if _, ok := (display).(*simplePrinter); ok {
197		fmt.Printf("lease %016x expired or revoked.\n", id)
198	}
199}
200
201func leaseFromArgs(arg string) v3.LeaseID {
202	id, err := strconv.ParseInt(arg, 16, 64)
203	if err != nil {
204		ExitWithError(ExitBadArgs, fmt.Errorf("bad lease ID arg (%v), expecting ID in Hex", err))
205	}
206	return v3.LeaseID(id)
207}
208