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

Complete all methods #6

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
60 changes: 31 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,43 +34,44 @@ client.range("/service/hello").kvs.try(&.first?) #=> #<Etcd::Model::KV @key="/se

## TODO

- Auth.
- Specs (auth, cluster, maintenance, kv.compaction, kv.txn)
- Multi-node.
- Use enum instead of String

### Auth

- [ ] authenticate
- [ ] disable
- [ ] enable
- [ ] role/add
- [ ] role/delete
- [ ] role/get
- [ ] role/grant
- [ ] role/list
- [ ] role/revoke
- [ ] user/add
- [ ] user/changepw
- [ ] user/delete
- [ ] user/get
- [ ] user/grant
- [ ] user/list
- [ ] user/revoke
- [x] authenticate
- [x] disable
- [x] enable
- [x] role/add
- [x] role/delete
- [x] role/get
- [x] role/grant
- [x] role/list
- [x] role/revoke
- [x] user/add
- [x] user/changepw
- [x] user/delete
- [x] user/get
- [x] user/grant
- [x] user/list
- [x] user/revoke

### Cluster

- [ ] member/add
- [ ] member/list
- [ ] member/promote
- [ ] member/remove
- [ ] member/update
- [x] member/add
- [x] member/list
- [x] member/promote
- [x] member/remove
- [x] member/update

### Kv

- [x] put
- [x] range
- [x] deleterange
- [ ] compaction
- [ ] txn
- [x] compaction
- [x] txn

### Lease

Expand All @@ -82,12 +83,12 @@ client.range("/service/hello").kvs.try(&.first?) #=> #<Etcd::Model::KV @key="/se

### Maintenance

- [ ] alarm
- [ ] defragment
- [ ] hash
- [ ] snapshot
- [x] alarm
- [x] defragment
- [x] hash
- [x] snapshot
- [x] status
- [ ] transfer-leadership
- [x] transfer-leadership

### Watch

Expand All @@ -108,3 +109,4 @@ client.range("/service/hello").kvs.try(&.first?) #=> #<Etcd::Model::KV @key="/se
## Contributors

- [Caspian Baska](https://github.com/caspiano) - creator and maintainer
- [Duke Nguyen](https://github.com/dukeraphaelng) - maintainer
1 change: 1 addition & 0 deletions shard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ name: etcd
version: 1.2.5
crystal: ">= 0.35"
license: MIT

authors:
- Caspian Baska <[email protected]>
- Duke Nguyen <[email protected]>
Expand Down
6 changes: 3 additions & 3 deletions spec/kv_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ module Etcd
client = Etcd.from_env
response = client.kv.put("#{TEST_PREFIX}/hello", "world")

response.should be_a Model::PutResponse
response.should be_a Model::Put
end

it "queries a range of keys" do
Expand All @@ -64,7 +64,7 @@ module Etcd
client.kv.put(key, value)
response = client.kv.range(key)

response.should be_a Model::RangeResponse
response.should be_a Model::Range
values = response.kvs || [] of Model::Kv
value_present = values.any? { |r| r.key == key && r.value == value }
value_present.should be_true
Expand All @@ -82,7 +82,7 @@ module Etcd
client.kv.put(key1, value1, lease: lease.id)
response = client.kv.range_prefix(key0)

response.should be_a Model::RangeResponse
response.should be_a Model::Range
values = response.kvs || [] of Model::Kv
key_present = values.any? { |r| r.key == key1 && r.value == value1 }
key_present.should be_true
Expand Down
6 changes: 2 additions & 4 deletions spec/lease_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,8 @@ module Etcd

it "handles nil on keep_alive" do
# Deserialise and handle incorrect json
Etcd::Model::KeepAlive.from_json(%({"result": {"TTL": "15"}})).result.should eq(15)
expect_raises(klass: JSON::SerializableError, message: "JSON key not found: TTL") {
Etcd::Model::KeepAlive.from_json(%({"result": {"error": "error"}}))
}
Etcd::Model::KeepAlive.from_json(%({"result": {"TTL": "15"}})).result.try(&.ttl).should eq(15)
Etcd::Model::KeepAlive.from_json(%({"result": {"error": "error"}}))

client = Etcd.from_env
new_ttl = client.lease.keep_alive 5_i64
Expand Down
97 changes: 69 additions & 28 deletions src/etcd/auth.cr
Original file line number Diff line number Diff line change
Expand Up @@ -5,82 +5,123 @@ class Etcd::Auth
end

# auth/authenticate
def authenticate
raise "unimplemented"
def authenticate(name : String, password : String)
validate!(name)

response = client.api.post("/auth/auth/authenticate", {name: name, password: password}).body
Model::Token.from_json(response).token
end

# auth/disable
def disable
raise "unimplemented"
client.api.post("/auth/auth/disable").success?
end

# auth/enable
def enable
raise "unimplemented"
client.api.post("/auth/auth/enable").success?
end

# auth/role/add
def role_add
raise "unimplemented"
def role_add(name : String)
validate!(name)

client.api.post("/auth/role/add", {name: name}).success?
end

# auth/role/delete
def role_delete
raise "unimplemented"
def role_delete(role : String)
client.api.post("/auth/role/delete", {role: role}).success?
end

# auth/role/get
def role_get
raise "unimplemented"
def role_get(role : String)
response = client.api.post("/auth/role/get", {role: role}).body
Model::Permissions.from_json(response).perm
end

# auth/role/grant
def role_grant
raise "unimplemented"
def role_grant(name : String, perm_key : String, range_end : String)
validate!(name)

options = {
:name => name,
:perm => {
:key => perm_key,
:permType => "READ",
:range_end => range_end,
},
}

client.api.post("/auth/role/grant", options).success?
end

# auth/role/list
def role_list
raise "unimplemented"
response = client.api.post("/auth/role/list").body
Roles.from_json(response).roles
end

# auth/role/revoke
def role_revoke
raise "unimplemented"
def role_revoke(key : String, range_end : String, role : String)
client.api.post("/auth/role/revoke").success?
end

# auth/user/add
def user_add
raise "unimplemented"
def user_add(name : String, password : String, no_password : Bool)
validate!(name)

options = {
:name => name,
:options => {
:no_password => no_password,
},
:password => password,
}

client.api.post("/auth/user/add", options).success?
end

# auth/user/changepw
def user_changepw
raise "unimplemented"
def user_changepw(name : String, password : String)
validate!(name)

client.api.post("/auth/user/changepw", {name: name, password: password}).success?
end

# auth/user/delete
def user_delete
raise "unimplemented"
def user_delete(name : String)
validate!(name)

client.api.post("/auth/user/delete", {name: name}).success?
end

# auth/user/get
def user_get
raise "unimplemented"
def user_get(name : String)
validate!(name)

response = client.api.post("/auth/user/get", {name: name}).body
Model::Roles.from_json(response).roles
end

# auth/user/grant
def user_grant
raise "unimplemented"
def user_grant(role : String, user : String)
client.api.post("/auth/user/grant").success?
end

# auth/user/list
def user_list
raise "unimplemented"
response = client.api.post("/auth/user/list").body
Model::Users.from_json(response).users
end

# auth/user/revoke
def user_revoke
raise "unimplemented"
def user_revoke(name : String, role : String)
validate!(name)
client.api.post("/auth/user/revoke").success?
end

private def validate!(name : String)
raise ArgumentError.new("`name` is empty") if name.empty?
end
end
28 changes: 19 additions & 9 deletions src/etcd/cluster.cr
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
module Etcd::Cluster
private getter client : Etcd::Client

def initialize(@client = Etcd::Client.new)
end

# POST cluster/member/add
def member_add
raise "unimplemented"
def member_add(is_learner : Bool, peer_urls : Array(String))
caspiano marked this conversation as resolved.
Show resolved Hide resolved
response = client.api.post("/cluster/member/add", {is_learner: is_learner, peerURLs: peer_urls}).body
Model::Cluster::MemberAdd.from_json(response)
end

# POST cluster/member/list
def member_list
raise "unimplemented"
response = client.api.post("/cluster/member/list").body
Model::Cluster::Members.from_json(response).members
end

# POST cluster/member/promote
def member_promote
raise "unimplemented"
def member_promote(id : UInt64)
response = client.api.post("/cluster/member/promote", {ID: id}).body
Model::Cluster::Members.from_json(response).members
end

# POST cluster/member/remove
def member_remove
raise "unimplemented"
def member_remove(id : UInt64)
response = client.api.post("/cluster/member/remove", {ID: id}).body
Model::Cluster::Members.from_json(response).members
end

# POST cluster/member/update
def member_update
raise "unimplemented"
def member_update(id : UInt64, peer_urls : Array(String))
response = client.api.post("/cluster/member/update", {ID: id, peerURLs: peer_urls}).body
Model::Cluster::Members.from_json(response).members
end
end
24 changes: 24 additions & 0 deletions src/etcd/endpoint.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
require "./client"

abstract class Endpoint
private getter client : Etcd::Client

def initialize(@client = Etcd::Client.new)
end

private macro request(verb, path, arguments, response_type)
begin
%response = client.api.{{ verb.downcase.id }}({{ path }}, body: {{ arguments }})
%body = %response.body
%result = {{ response_type }}.from_json(%body)
rescue e : JSON::SerializableError
raise Error.new("incorrect {{ verb.id }} {{ path.id }} response: #{ %body }", cause: e)
end

unless (%error = %result.error).nil? && %response.success?
raise Error.new(%error.try(&.message) || "Unsuccessful response")
end

%result
end
end
Loading