etcd client for Julia
Julia
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
src
test
.gitignore
.travis.yml
CONTRIBUTING.md
LICENSE.md
README.md
REQUIRE

README.md

Quickstart | Configure the Etcd server | Using Etcd Client

Etcd.jl

A Julia Etcd client implementation.

Quickstart

julia> Pkg.add("Etcd")
julia> using Etcd

Configure the Etcd server

The library defaults to Etcd server at 127.0.0.1:4001.

server = Etcd.EtcdServer()
EtcdServer("127.0.0.1",4001,"v2")

Or you can specify the server ip address and port number.

server = Etcd.EtcdServer("172.17.42.1",5001)
EtcdServer("172.17.42.1",5001,"v2")

Using Etcd Client

Setting Key Values

etcd = Etcd.EtcdServer()
EtcdServer("127.0.0.1",4001,"v2")

Set a value on the /foo/bar key:

julia> Etcd.set(etcd,"/foo/bar","Hello World")
["action"=>"set","node"=>["createdIndex"=>1803,"key"=>"/foo/bar","value"=>"Hello World","modifiedIndex"=>1803]]

Set a value on the /foo/bar key with a value that expires in 60 seconds:

julia> Etcd.set(etcd,"/foo/bar","Hello World",ttl=60)
["action"=>"set","node"=>["createdIndex"=>1805,"key"=>"/foo/bar","value"=>"Hello World","expiration"=>"2014-03-25T01:19:39.182867998Z","ttl"=>60,"modifiedIndex"=>1805]]

Note that the ttl value can be set with all the following commands by specifying ttl=ttl_expiry_time_in_seconds

Conditionally set a value on /foo/bar if the previous value was "Hello world". test_and_set is an alias for compare_and_swap.

julia> Etcd.compare_and_swap(etcd,"/foo/bar","Goodbye Cruel World",prev_value="Hello World")
["action"=>"compareAndSwap","prevNode"=>["createdIndex"=>1811,"key"=>"/foo/bar","value"=>"Hello World","modifiedIndex"=>1811],"node"=>["createdIndex"=>1811,"key"=>"/foo/bar","value"=>"Goodbye Cruel World","modifiedIndex"=>1812]]

You can also conditionally set a value based on the previous etcd index. Conditionally set a value on /foo/bar if the previous etcd index was 1818:

julia> Etcd.compare_and_swap(etcd,"/foo/bar","Goodbye Cruel World",prev_index=1818)
["action"=>"compareAndSwap","prevNode"=>["createdIndex"=>1818,"key"=>"/foo/bar","value"=>"Hello World","modifiedIndex"=>1818],"node"=>["createdIndex"=>1818,"key"=>"/foo/bar","value"=>"Goodbye Cruel World","modifiedIndex"=>1820]]

Create a new key /foo/boo, only if the key did not previously exist:

julia> Etcd.create(etcd,"/foo/boo","Hello World")
["action"=>"create","node"=>["createdIndex"=>1822,"key"=>"/foo/boo","value"=>"Hello World","modifiedIndex"=>1822]]

Create a new dir /fooDir, only if the directory did not previously exist:

julia> Etcd.create_dir(etcd,"/fooDir")
["action"=>"create","node"=>["createdIndex"=>1826,"key"=>"/fooDir","dir"=>true,"modifiedIndex"=>1826]]

Update an existing key /foo/bar, only if the key already existed:

julia> Etcd.update(etcd,"/foo/boo","Merhaba")
["action"=>"update","prevNode"=>["createdIndex"=>1822,"key"=>"/foo/boo","value"=>"Hello World","modifiedIndex"=>1822],"node"=>["createdIndex"=>1822,"key"=>"/foo/boo","value"=>"Merhaba","modifiedIndex"=>1828]]

You can also Create (create_dir) or update (update_dir) a directory.

Retrieving key values

Get the current value for a single key in the local etcd node:

julia> Etcd.get(etcd,"/foo/bar")
["action"=>"get","node"=>["createdIndex"=>1817,"key"=>"/foo/bar","value"=>"Hello World","modifiedIndex"=>1817]]

Add recursive=true to recursively list sub-directories.

Check for existence of a key:

julia> Etcd.exists(etcd,"/foo/bar")
true

Deleting keys

Delete a key:

julia> Etcd.create_dir(etcd,"/foo/qux")
julia> Etcd.delete(etcd,"/foo/boo")
["action"=>"delete","prevNode"=>["createdIndex"=>1822,"key"=>"/foo/boo","value"=>"Merhaba","modifiedIndex"=>1828],"node"=>["createdIndex"=>1822,"key"=>"/foo/boo","modifiedIndex"=>1837]]

Delete an empty directory:

julia> Etcd.delete_dir(etcd,"/foo/qux")
["action"=>"delete","prevNode"=>["createdIndex"=>1838,"key"=>"/foo/qux","dir"=>true,"modifiedIndex"=>1838],"node"=>["createdIndex"=>1838,"key"=>"/foo/qux","dir"=>true,"modifiedIndex"=>1839]] 

Recursively delete a key and all child keys:

julia> Etcd.get(etcd,"/foo",recursive=true)
["action"=>"get","node"=>["nodes"=>{["createdIndex"=>1817,"key"=>"/foo/bar","value"=>"Hello World","modifiedIndex"=>1817],["createdIndex"=>1818,"key"=>"/foo/baz","value"=>"Goodbye Cruel World","modifiedIndex"=>1820]},"createdIndex"=>1803,"key"=>"/foo","dir"=>true,"modifiedIndex"=>1803]]
ulia> Etcd.delete_dir(etcd,"/foo",recursive=true)
["action"=>"delete","prevNode"=>["createdIndex"=>1803,"key"=>"/foo","dir"=>true,"modifiedIndex"=>1803],"node"=>["createdIndex"=>1803,"key"=>"/foo","dir"=>true,"modifiedIndex"=>1844]]
julia> Etcd.get(etcd,"/foo",recursive=true)
["message"=>"Key not found","cause"=>"/foo","index"=>1844,"errorCode"=>100]

Conditionally delete /foo/bar if the previous value was "Hello world":

julia> Etcd.create(etcd,"/foo/bar","bar value")
["action"=>"create","node"=>["createdIndex"=>1845,"key"=>"/foo/bar","value"=>"bar value","modifiedIndex"=>1845]]
julia> Etcd.compare_and_delete(etcd,"/foo/bar",prev_value="bar value")
["action"=>"compareAndDelete","prevNode"=>["createdIndex"=>1845,"key"=>"/foo/bar","value"=>"bar value","modifiedIndex"=>1845],"node"=>["createdIndex"=>1845,"key"=>"/foo/bar","modifiedIndex"=>1846]]

Conditionally delete /foo/bar if the previous etcd index was 1849:

julia> Etcd.create(etl,"/foo/bar","Hello World")
["action"=>"create","node"=>["createdIndex"=>1849,"key"=>"/foo/bar","value"=>"Hello World","modifiedIndex"=>1849]]
julia> Etcd.compare_and_delete(etl,"/foo/bar",prev_index=1849)
["action"=>"compareAndDelete","prevNode"=>["createdIndex"=>1849,"key"=>"/foo/bar","value"=>"Hello World","modifiedIndex"=>1849],"node"=>["createdIndex"=>1849,"key"=>"/foo/bar","modifiedIndex"=>1850]]

Watching for changes

Watch for only the next change on a key:

julia> Etcd.watch(etcd,"/foo/bar",ev->println("I'm watching you:",ev))
Task (queued) @0x00000000024b65f0
...
... next make some modification to "/foo/bar" key and the callback is then called:
...
I'm watching you:["action"=>"update","prevNode"=>["createdIndex"=>1851,"key"=>"/foo/bar","value"=>"Hello World","modifiedIndex"=>1851],"node"=>["createdIndex"=>1851,"key"=>"/foo/bar","value"=>"Who's watching the watchers","modifiedIndex"=>1852]]

You can also specify the following options:

  • recursive=true to watch the key and all it's children.
  • wait_index to watch starting with the provided index.

Continuously watch a key:

julia> Etcd.keep_watching(etcd,"/foo/bar",ev->println("I'll keep on watching you:",ev))
.... The callback will keep getting called for every change to the key

Watch conditionally, while passing a function which will terminate the watch when it evaluates to true, for example:

julia> Etcd.watch_until(etcd,"/foo",ev->println("I'll be watching you (only 3 times):",ev),begin let l = 0; ev->begin l += 1; if l > 2 true else false end end end end,recursive=true)

Get all machines in the cluster

julia> Etcd.machines(etcd)
"http://172.17.42.1:5001, http://172.17.42.1:5002, http://172.17.42.1:5003"

Getting Etcd stats

You can retrieve Etcd stats by specifying one of store, self or leader.

For example to get the store stats:

julia> Etcd.stats(etcd,"store")
["getsSuccess"=>193,"updateFail"=>88,"watchers"=>1,"setsSuccess"=>710,"setsFail"=>2869,"expireCount"=>460,"compareAndSwapSuccess"=>3,"getsFail"=>10,"deleteSuccess"=>8,"createFail"=>10,"createSuccess"=>25,"compareAndDeleteSuccess"=>3,"deleteFail"=>3,"compareAndSwapFail"=>1,"updateSuccess"=>386,"compareAndDeleteFail"=>0]

Leader/Election module

Set a leader for the cluster (notice the leading slash is omitted) by specifying a name and a ttl as follows:

julia> Etcd.set_leader(etcd,"my-cluster",name="leader-1",ttl=60)
"1853"

Get the leader of the cluster:

julia> Etcd.get_leader(etcd,"my-cluster")
"leader-1"

Deleting the leader:

julia> Etcd.delete_leader(etcd,"my-cluster",name="leader-1")
""

Locking module

The lock module can be used to provide a distributed lock for resources among multiple clients. Only one client can have access to the lock at a time, once the lock holder releases the lock the next client waiting for the lock can acquire it.

lock_retrieve gets the lock index of the lock. lock_acquire is used to acquire the lock and it will return the lock index.

... another client acquires the lock
$ curl -L http://127.0.0.1:4001/mod/v2/lock/mylock -XPOST -d ttl=100
1876
julia> Etcd.lock_retrieve(etcd,"mylock")
"1876"
julia> Etcd.lock_acquire(etcd,"mylock",ttl=100)
... blocks until ttl for lock expires or until lock is released ...
"1878"

You can also renew the lock:

julia> Etcd.lock_renew(etcd,"mylock",index=1885,ttl=60)

And release the lock:

julia> Etcd.lock_release(etcd,"mylock",index=1890)