Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

api: lease list #8358

Merged
merged 9 commits into from
Aug 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions Documentation/dev-guide/api_reference_v3.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ This is a generated documentation. Please read the proto files for more.
| LeaseRevoke | LeaseRevokeRequest | LeaseRevokeResponse | LeaseRevoke revokes a lease. All keys attached to the lease will expire and be deleted. |
| LeaseKeepAlive | LeaseKeepAliveRequest | LeaseKeepAliveResponse | LeaseKeepAlive keeps the lease alive by streaming keep alive requests from the client to the server and streaming keep alive responses from the server to the client. |
| LeaseTimeToLive | LeaseTimeToLiveRequest | LeaseTimeToLiveResponse | LeaseTimeToLive retrieves lease information. |
| LeaseLeases | LeaseLeasesRequest | LeaseLeasesResponse | LeaseLeases lists all existing leases. |



Expand Down Expand Up @@ -513,6 +514,21 @@ Empty field.



##### message `LeaseLeasesRequest` (etcdserver/etcdserverpb/rpc.proto)

Empty field.



##### message `LeaseLeasesResponse` (etcdserver/etcdserverpb/rpc.proto)

| Field | Description | Type |
| ----- | ----------- | ---- |
| header | | ResponseHeader |
| leases | | (slice of) LeaseStatus |



##### message `LeaseRevokeRequest` (etcdserver/etcdserverpb/rpc.proto)

| Field | Description | Type |
Expand All @@ -529,6 +545,14 @@ Empty field.



##### message `LeaseStatus` (etcdserver/etcdserverpb/rpc.proto)

| Field | Description | Type |
| ----- | ----------- | ---- |
| ID | | int64 |



##### message `LeaseTimeToLiveRequest` (etcdserver/etcdserverpb/rpc.proto)

| Field | Description | Type |
Expand Down
53 changes: 53 additions & 0 deletions Documentation/dev-guide/apispec/swagger/rpc.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,33 @@
}
}
},
"/v3alpha/kv/lease/leases": {
"post": {
"tags": [
"Lease"
],
"summary": "LeaseLeases lists all existing leases.",
"operationId": "LeaseLeases",
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/etcdserverpbLeaseLeasesRequest"
}
}
],
"responses": {
"200": {
"description": "(empty)",
"schema": {
"$ref": "#/definitions/etcdserverpbLeaseLeasesResponse"
}
}
}
}
},
"/v3alpha/kv/lease/revoke": {
"post": {
"tags": [
Expand Down Expand Up @@ -1666,6 +1693,23 @@
}
}
},
"etcdserverpbLeaseLeasesRequest": {
"type": "object"
},
"etcdserverpbLeaseLeasesResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/etcdserverpbResponseHeader"
},
"leases": {
"type": "array",
"items": {
"$ref": "#/definitions/etcdserverpbLeaseStatus"
}
}
}
},
"etcdserverpbLeaseRevokeRequest": {
"type": "object",
"properties": {
Expand All @@ -1684,6 +1728,15 @@
}
}
},
"etcdserverpbLeaseStatus": {
"type": "object",
"properties": {
"ID": {
"type": "string",
"format": "int64"
}
}
},
"etcdserverpbLeaseTimeToLiveRequest": {
"type": "object",
"properties": {
Expand Down
31 changes: 31 additions & 0 deletions clientv3/integration/lease_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,37 @@ func TestLeaseTimeToLiveLeaseNotFound(t *testing.T) {
}
}

func TestLeaseLeases(t *testing.T) {
defer testutil.AfterTest(t)

clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)

cli := clus.RandClient()

ids := []clientv3.LeaseID{}
for i := 0; i < 5; i++ {
resp, err := cli.Grant(context.Background(), 10)
if err != nil {
t.Errorf("failed to create lease %v", err)
}
ids = append(ids, resp.ID)
}

resp, err := cli.Leases(context.Background())
if err != nil {
t.Fatal(err)
}
if len(resp.Leases) != 5 {
t.Fatalf("len(resp.Leases) expected 5, got %d", len(resp.Leases))
}
for i := range resp.Leases {
if ids[i] != resp.Leases[i].ID {
t.Fatalf("#%d: lease ID expected %d, got %d", i, ids[i], resp.Leases[i].ID)
}
}
}

// TestLeaseRenewLostQuorum ensures keepalives work after losing quorum
// for a while.
func TestLeaseRenewLostQuorum(t *testing.T) {
Expand Down
31 changes: 31 additions & 0 deletions clientv3/lease.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,18 @@ type LeaseTimeToLiveResponse struct {
Keys [][]byte `json:"keys"`
}

// LeaseStatus represents a lease status.
type LeaseStatus struct {
ID LeaseID `json:"id"`
// TODO: TTL int64
}

// LeaseLeasesResponse is used to convert the protobuf lease list response.
type LeaseLeasesResponse struct {
*pb.ResponseHeader
Leases []LeaseStatus `json:"leases"`
}

const (
// defaultTTL is the assumed lease TTL used for the first keepalive
// deadline before the actual TTL is known to the client.
Expand Down Expand Up @@ -98,6 +110,9 @@ type Lease interface {
// TimeToLive retrieves the lease information of the given lease ID.
TimeToLive(ctx context.Context, id LeaseID, opts ...LeaseOption) (*LeaseTimeToLiveResponse, error)

// Leases retrieves all leases.
Leases(ctx context.Context) (*LeaseLeasesResponse, error)

// KeepAlive keeps the given lease alive forever.
KeepAlive(ctx context.Context, id LeaseID) (<-chan *LeaseKeepAliveResponse, error)

Expand Down Expand Up @@ -219,6 +234,22 @@ func (l *lessor) TimeToLive(ctx context.Context, id LeaseID, opts ...LeaseOption
}
}

func (l *lessor) Leases(ctx context.Context) (*LeaseLeasesResponse, error) {
for {
resp, err := l.remote.LeaseLeases(ctx, &pb.LeaseLeasesRequest{}, grpc.FailFast(false))
if err == nil {
leases := make([]LeaseStatus, len(resp.Leases))
for i := range resp.Leases {
leases[i] = LeaseStatus{ID: LeaseID(resp.Leases[i].ID)}
}
return &LeaseLeasesResponse{ResponseHeader: resp.GetHeader(), Leases: leases}, nil
}
if isHaltErr(ctx, err) {
return nil, toErr(ctx, err)
}
}
}

func (l *lessor) KeepAlive(ctx context.Context, id LeaseID) (<-chan *LeaseKeepAliveResponse, error) {
ch := make(chan *LeaseKeepAliveResponse, leaseResponseChSize)

Expand Down
21 changes: 21 additions & 0 deletions e2e/ctl_v3_lease_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
)

func TestCtlV3LeaseGrantTimeToLive(t *testing.T) { testCtl(t, leaseTestGrantTimeToLive) }
func TestCtlV3LeaseGrantLeases(t *testing.T) { testCtl(t, leaseTestGrantLeasesList) }
func TestCtlV3LeaseKeepAlive(t *testing.T) { testCtl(t, leaseTestKeepAlive) }
func TestCtlV3LeaseRevoke(t *testing.T) { testCtl(t, leaseTestRevoke) }

Expand Down Expand Up @@ -51,6 +52,26 @@ func leaseTestGrantTimeToLive(cx ctlCtx) {
}
}

func leaseTestGrantLeasesList(cx ctlCtx) {
id, err := ctlV3LeaseGrant(cx, 10)
if err != nil {
cx.t.Fatal(err)
}

cmdArgs := append(cx.PrefixArgs(), "lease", "list")
proc, err := spawnCmd(cmdArgs)
if err != nil {
cx.t.Fatal(err)
}
_, err = proc.Expect(id)
if err != nil {
cx.t.Fatal(err)
}
if err = proc.Close(); err != nil {
cx.t.Fatal(err)
}
}

func leaseTestKeepAlive(cx ctlCtx) {
// put with TTL 10 seconds and keep-alive
leaseID, err := ctlV3LeaseGrant(cx, 10)
Expand Down
26 changes: 23 additions & 3 deletions etcdctl/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,26 @@ Prints lease information.
# {"cluster_id":17186838941855831277,"member_id":4845372305070271874,"revision":3,"raft_term":2,"id":3279279168933706764,"ttl":459,"granted-ttl":500,"keys":["Zm9vMQ==","Zm9vMg=="]}
```

### LEASE LIST

LEASE LIST lists all active leases.

RPC: LeaseLeases

#### Output

Prints a message with a list of active leases.

#### Example

```bash
./etcdctl lease grant 10
# lease 32695410dcc0ca06 granted with TTL(10s)

./etcdctl lease list
32695410dcc0ca06
```

### LEASE KEEP-ALIVE \<leaseID\>

LEASE KEEP-ALIVE periodically refreshes a lease so it does not expire.
Expand Down Expand Up @@ -736,9 +756,9 @@ If NOSPACE alarm is present:

### DEFRAG [options]

DEFRAG defragments the backend database file for a set of given endpoints while etcd is running, or directly defragments an
etcd data directory while etcd is not running. When an etcd member reclaims storage space from deleted and compacted keys, the
space is kept in a free list and the database file remains the same size. By defragmenting the database, the etcd member
DEFRAG defragments the backend database file for a set of given endpoints while etcd is running, or directly defragments an
etcd data directory while etcd is not running. When an etcd member reclaims storage space from deleted and compacted keys, the
space is kept in a free list and the database file remains the same size. By defragmenting the database, the etcd member
releases this free space back to the file system.

#### Options
Expand Down
20 changes: 20 additions & 0 deletions etcdctl/ctlv3/command/lease_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func NewLeaseCommand() *cobra.Command {
lc.AddCommand(NewLeaseGrantCommand())
lc.AddCommand(NewLeaseRevokeCommand())
lc.AddCommand(NewLeaseTimeToLiveCommand())
lc.AddCommand(NewLeaseListCommand())
lc.AddCommand(NewLeaseKeepAliveCommand())

return lc
Expand Down Expand Up @@ -129,6 +130,25 @@ func leaseTimeToLiveCommandFunc(cmd *cobra.Command, args []string) {
display.TimeToLive(*resp, timeToLiveKeys)
}

// NewLeaseListCommand returns the cobra command for "lease list".
func NewLeaseListCommand() *cobra.Command {
lc := &cobra.Command{
Use: "list",
Short: "List all active leases",
Run: leaseListCommandFunc,
}
return lc
}

// leaseListCommandFunc executes the "lease list" command.
func leaseListCommandFunc(cmd *cobra.Command, args []string) {
resp, rerr := mustClientFromCmd(cmd).Leases(context.TODO())
if rerr != nil {
ExitWithError(ExitBadConnection, rerr)
}
display.Leases(*resp)
}

// NewLeaseKeepAliveCommand returns the cobra command for "lease keep-alive".
func NewLeaseKeepAliveCommand() *cobra.Command {
lc := &cobra.Command{
Expand Down
2 changes: 2 additions & 0 deletions etcdctl/ctlv3/command/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type printer interface {
Revoke(id v3.LeaseID, r v3.LeaseRevokeResponse)
KeepAlive(r v3.LeaseKeepAliveResponse)
TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool)
Leases(r v3.LeaseLeasesResponse)

MemberAdd(v3.MemberAddResponse)
MemberRemove(id uint64, r v3.MemberRemoveResponse)
Expand Down Expand Up @@ -96,6 +97,7 @@ func (p *printerRPC) Grant(r v3.LeaseGrantResponse) { p.p(r
func (p *printerRPC) Revoke(id v3.LeaseID, r v3.LeaseRevokeResponse) { p.p(r) }
func (p *printerRPC) KeepAlive(r v3.LeaseKeepAliveResponse) { p.p(r) }
func (p *printerRPC) TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool) { p.p(&r) }
func (p *printerRPC) Leases(r v3.LeaseLeasesResponse) { p.p(&r) }

func (p *printerRPC) MemberAdd(r v3.MemberAddResponse) { p.p((*pb.MemberAddResponse)(&r)) }
func (p *printerRPC) MemberRemove(id uint64, r v3.MemberRemoveResponse) {
Expand Down
7 changes: 7 additions & 0 deletions etcdctl/ctlv3/command/printer_fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@ func (p *fieldsPrinter) TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool) {
}
}

func (p *fieldsPrinter) Leases(r v3.LeaseLeasesResponse) {
p.hdr(r.ResponseHeader)
for _, item := range r.Leases {
fmt.Println(`"ID" :`, item.ID)
}
}

func (p *fieldsPrinter) MemberList(r v3.MemberListResponse) {
p.hdr(r.Header)
for _, m := range r.Members {
Expand Down
7 changes: 7 additions & 0 deletions etcdctl/ctlv3/command/printer_simple.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ func (s *simplePrinter) TimeToLive(resp v3.LeaseTimeToLiveResponse, keys bool) {
fmt.Println(txt)
}

func (s *simplePrinter) Leases(resp v3.LeaseLeasesResponse) {
fmt.Printf("found %d leases\n", len(resp.Leases))
for _, item := range resp.Leases {
fmt.Printf("%016x\n", item.ID)
}
}

func (s *simplePrinter) Alarm(resp v3.AlarmResponse) {
for _, e := range resp.Alarms {
fmt.Printf("%+v\n", e)
Expand Down
15 changes: 15 additions & 0 deletions etcdserver/api/v3rpc/lease.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,21 @@ func (ls *LeaseServer) LeaseTimeToLive(ctx context.Context, rr *pb.LeaseTimeToLi
return resp, nil
}

func (ls *LeaseServer) LeaseLeases(ctx context.Context, rr *pb.LeaseLeasesRequest) (*pb.LeaseLeasesResponse, error) {
resp, err := ls.le.LeaseLeases(ctx, rr)
if err != nil && err != lease.ErrLeaseNotFound {
return nil, togRPCError(err)
}
if err == lease.ErrLeaseNotFound {
resp = &pb.LeaseLeasesResponse{
Header: &pb.ResponseHeader{},
Leases: []*pb.LeaseStatus{},
}
}
ls.hdr.fill(resp.Header)
return resp, nil
}

func (ls *LeaseServer) LeaseKeepAlive(stream pb.Lease_LeaseKeepAliveServer) (err error) {
errc := make(chan error, 1)
go func() {
Expand Down
3 changes: 3 additions & 0 deletions etcdserver/etcdserverpb/etcdserver.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading