GitHub tagHex.pm Version

eetcd

etcd v3 client for erlang.

Quick Start

1. Setup

## rebar.config
{deps, [eetcd]}.

Prepare configuration.

## sys.config
[{eetcd,
    {etcd_cluster, ["127.0.0.1:2379", "127.0.0.1:2479", "127.0.0.1:2579"]},

    {http2_transport, tcp},  %% tcp | ssl
    %% ssl:connect_option() see all options in ssl_api.hrl such as [{certfile, Certfile}, {keyfile, Keyfile}] or [{cert, Cert}, {key, Key}]
    {http2_transport_opts, []}
 }
]

2. Usage

All etcd3 API's are defined in gRPC services, which categorize remote procedure calls (RPCs) understood by the etcd server. A full listing of all etcd RPCs are documented in markdown in the gRPC API listing.

KV - Creates, updates, fetches, and deletes key-value pairs.
%% -include_lib("eetcd/include/eetcd.hrl").

%% creates
{ok, #'Etcd.PutResponse'{ header = #'Etcd.ResponseHeader'{}, prev_kv = undefined}}
    = eetcd_kv:put(#&#39;Etcd.PutRequest&#39;{key = <<"key">>, value = <<"value">>}).

%% updates
{ok, #&#39;Etcd.PutResponse&#39;{}}
    = eetcd_kv:put(#&#39;Etcd.PutRequest&#39;{key = <<"key_exist">>, value = <<"new_value">>, ignore_value = true}).
{error, {grpc_error, 3, <<"etcdserver: value is provided">>}}
    = eetcd_kv:put(#&#39;Etcd.PutRequest&#39;{key = <<"key_no_exist">>, value = <<"new_value">>, ignore_value = true}).

%% fetches
{ok, #&#39;Etcd.RangeResponse&#39;{
        header = #&#39;Etcd.ResponseHeader&#39;{},
        more = false,
        count = 1,
        kvs = [#&#39;mvccpb.KeyValue&#39;{key = "key_exist", value = "value"}]
    }}
    = eetcd_kv:range(#&#39;Etcd.RangeRequest&#39;{key = "key_exist"}).
%% fetches all keys
{ok, #&#39;Etcd.RangeResponse&#39;{
        more = false,
        count = Count,
        kvs = Kvs
    }}
    = eetcd_kv:range(#&#39;Etcd.RangeRequest&#39;{key = "\0", range_end = "\0",
          sort_target = &#39;KEY&#39;, sort_order = &#39;ASCEND&#39;, keys_only = true}).

%% deletes
{ok, #&#39;Etcd.DeleteRangeResponse&#39;{
        deleted = 1,
        prev_kvs = [#&#39;mvccpb.KeyValue&#39;{key = "key", value = "value"}]
    }} = eetcd_kv:delete_range(#&#39;Etcd.DeleteRangeRequest&#39;{key = "key", prev_kv = true}).
%% batch deletes
{ok, #&#39;Etcd.DeleteRangeResponse&#39;{deleted = 2, prev_kvs = Kvs}}
    = eetcd_kv:delete_range(#&#39;Etcd.DeleteRangeRequest&#39;{key = "key", range_end = "\0", prev_kv = true}).
Txn - Transaction
%% implement etcd v2 CompareAndSwap by Txn
    {ok, #&#39;Etcd.RangeResponse&#39;{kvs = [#&#39;mvccpb.KeyValue&#39;{key = Kv1, value = Vv1, mod_revision = ModRevision}]}}
        = eetcd_kv:range(#&#39;Etcd.RangeRequest&#39;{key = Kv1}),

    {ok,#&#39;Etcd.TxnResponse&#39;{
        succeeded = false,
        responses = []}
    } = eetcd_kv:txn(#&#39;Etcd.TxnRequest&#39;{
        compare = [#&#39;Etcd.Compare&#39;{result = &#39;EQUAL&#39;, target = &#39;MOD&#39;, key = Kv1, target_union = {mod_revision, ModRevision - 1}}],
        success = [#&#39;Etcd.RequestOp&#39;{request = {request_put, #&#39;Etcd.PutRequest&#39;{key = Kv1, value = Vv4, prev_kv = true}}}],
        failure = []
    }),
    {ok,#&#39;Etcd.TxnResponse&#39;{
        succeeded = true,
        responses = [#&#39;Etcd.ResponseOp&#39;{
            response = {response_put, #&#39;Etcd.PutResponse&#39;{prev_kv =  #&#39;mvccpb.KeyValue&#39;{key = Kv1, value = Vv1}}}
        }]}}
        = eetcd_kv:txn(#&#39;Etcd.TxnRequest&#39;{
        compare = [#&#39;Etcd.Compare&#39;{result = &#39;EQUAL&#39;, target = &#39;MOD&#39;, key = Kv1, target_union = {mod_revision, ModRevision}}],
        success = [#&#39;Etcd.RequestOp&#39;{request = {request_put, #&#39;Etcd.PutRequest&#39;{key = Kv1, value = Vv4, prev_kv = true}}}],
        failure = []
    }).
Watch - Monitors changes to keys.
    Pid = self(),
    Key = <<"etcd_key">>,
    Value = <<"etcd_value">>,
    Value1 = <<"etcd_value1">>,
    Value2 = <<"etcd_value2">>,
    Callback = fun(Res) -> erlang:send(Pid, Res) end,
    {ok, WatchPid} = eetcd:watch(#&#39;Etcd.WatchCreateRequest&#39;{key = Key}, Callback),
    eetcd_kv:put(#&#39;Etcd.PutRequest&#39;{key = Key, value = Value}),
    #&#39;Etcd.WatchResponse&#39;{created = false,
        events = [#&#39;mvccpb.Event&#39;{type = &#39;PUT&#39;,
            kv = #&#39;mvccpb.KeyValue&#39;{key = Key, value = Value}}]} = flush(),

    eetcd_kv:put(#&#39;Etcd.PutRequest&#39;{key = Key, value = Value1}),
    #&#39;Etcd.WatchResponse&#39;{created = false,
        events = [#&#39;mvccpb.Event&#39;{type = &#39;PUT&#39;,
            kv = #&#39;mvccpb.KeyValue&#39;{key = Key, value = Value1}}]} = flush(),

    eetcd_kv:delete_range(#&#39;Etcd.DeleteRangeRequest&#39;{key = Key}),
    #&#39;Etcd.WatchResponse&#39;{created = false,
        events = [#&#39;mvccpb.Event&#39;{type = &#39;DELETE&#39;,
            kv = #&#39;mvccpb.KeyValue&#39;{key = Key, value = <<>>}}]} = flush(),

    ok = eetcd:unwatch(WatchPid),
    eetcd_kv:put(#&#39;Etcd.PutRequest&#39;{key = Key, value = Value2}),
    {error, timeout} = flush().

flush() -> flush(1000).

flush(Time) ->
    receive Msg  -> Msg
    after Time -> {error, timeout}
    end.
Lease - Primitives for consuming client keep-alive messages.
TTL = 3,
{ok, #&#39;Etcd.LeaseGrantResponse&#39;{&#39;ID&#39; =ID, &#39;TTL&#39; = TTL}}
    = eetcd_lease:lease_grant(#&#39;Etcd.LeaseGrantRequest&#39;{&#39;TTL&#39; = TTL}),
ok = eetcd:lease_keep_alive(#&#39;Etcd.LeaseKeepAliveRequest&#39;{&#39;ID&#39; = ID}),

{ok, #&#39;Etcd.LeaseLeasesResponse&#39;{leases = [
    #&#39;Etcd.LeaseStatus&#39;{&#39;ID&#39; = ID}
 ]}}
   = eetcd_lease:lease_leases(#&#39;Etcd.LeaseLeasesRequest&#39;{}),
timer:sleep(10000),
{ok, #&#39;Etcd.LeaseLeasesResponse&#39;{leases = [
    #&#39;Etcd.LeaseStatus&#39;{&#39;ID&#39; = ID}
  ]}}
    = eetcd_lease:lease_leases(#&#39;Etcd.LeaseLeasesRequest&#39;{}),

{ok, #&#39;Etcd.LeaseRevokeResponse&#39;{}} =
   eetcd_lease:lease_revoke(#&#39;Etcd.LeaseRevokeRequest&#39;{&#39;ID&#39; = ID}).

More detailed examples see eetcd_kv_SUITE.erleetcd_watch_SUITE.erleetcd_lease_SUITE.erl.

Test

rebar3 ct

Architecture

Home