Skip to content

Commit

Permalink
feat(etcd) etcd storage to use v3 protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
fffonion committed Aug 4, 2024
1 parent 754ac40 commit 29c3834
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 57 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -769,13 +769,13 @@ storage_config = {

### etcd

[etcd](https://etcd.io) based storage. Right now only `v2` protocol is supported.
[etcd](https://etcd.io) based storage. Right now only `v3` protocol is supported, and etcd server
version should be >= v3.4.0.
The default config is:

```lua
storage_config = {
http_host = 'http://127.0.0.1:4001',
protocol = 'v2',
key_prefix = '',
timeout = 60,
ssl_verify = false,
Expand Down
79 changes: 65 additions & 14 deletions lib/resty/acme/storage/etcd.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ function _M.new(conf)
conf = conf or {}
local self = setmetatable({}, mt)

if conf.protocol and conf.protocol ~= "v3" then
return nil, "only v3 protocol is supported"
end

local options = {
http_host = conf.http_host or "http://127.0.0.1:4001",
protocol = conf.protocol or "v2",
key_prefix = conf.key_prefix or "",
timeout = conf.timeout or 60,
ssl_verify = conf.ssl_verify,
protocol = "v3",
}

local client, err = etcd.new(options)
Expand All @@ -21,65 +25,112 @@ function _M.new(conf)
end

self.client = client
self.protocol_is_v2 = options.protocol == "v2"
return self, nil
end

local function grant(self, ttl)
local res, err = self.client:grant(ttl)
if err then
return nil, err
end
return res.body.ID
end

-- set the key regardless of it's existence
function _M:set(k, v, ttl)
local _, err = self.client:set(k, v, ttl)
k = "/" .. k

local lease_id, err
if ttl then
lease_id, err = grant(self, ttl)
if err then
return err
end
end

local _, err = self.client:set(k, v, { lease = lease_id })
if err then
return err
end
end

-- set the key only if the key doesn't exist
-- Note: the key created by etcd:setnx can't be attached to a lease later, it seems to be a bug
function _M:add(k, v, ttl)
local res, err = self.client:setnx(k, v, ttl)
if err then
return err
k = "/" .. k

local lease_id, err
if ttl then
lease_id, err = grant(self, ttl)
if err then
return err
end
end
if res and res.body and res.body.errorCode == 105 then


local compare = {
{
key = k,
target = "CREATE",
create_revision = 0,
}
}

local success = {
{
requestPut = {
key = k,
value = v,
lease = lease_id,
}
}
}

local v, err = self.client:txn(compare, success)
if err then
return nil, err
elseif v and v.body and not v.body.succeeded then
return "exists"
end
end

function _M:delete(k)
k = "/" .. k
local _, err = self.client:delete(k)
if err then
return err
end
end

function _M:get(k)
k = "/" .. k
local res, err = self.client:get(k)
if err then
return nil, err
elseif res.status == 404 and res.body and res.body.errorCode == 100 then
elseif res and res.body.kvs == nil then
return nil, nil
elseif res.status ~= 200 then
return nil, "etcd returned status " .. res.status
end
local node = res.body.node
-- is it already expired but not evited ?
if node.expiration and not node.ttl and self.protocol_is_v2 then
local node = res.body.kvs[1]
if not node then -- would this ever happen?
return nil, nil
end
return node.value
end

local empty_table = {}
function _M:list(prefix)
local res, err = self.client:get("/")
local res, err = self.client:readdir("/" .. prefix)
if err then
return nil, err
elseif not res or not res.body or not res.body.node or not res.body.node.nodes then
elseif not res or not res.body or not res.body.kvs then
return empty_table, nil
end
local ret = {}
-- offset 1 to strip leading "/" in original key
local prefix_length = #prefix + 1
for _, node in ipairs(res.body.node.nodes) do
for _, node in ipairs(res.body.kvs) do
local key = node.key
if key then
-- start from 2 to strip leading "/"
Expand Down
109 changes: 68 additions & 41 deletions t/storage/etcd.t
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ use Cwd qw(cwd);
my $pwd = cwd();

our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;$pwd/lib/?/init.lua;$pwd/../lib/?.lua;$pwd/../lib/?/init.lua;;";
lua_package_path "/home/wow/.luarocks/share/lua/5.1/?.lua;/home/wow/.luarocks/share/lua/5.1/?/init.lua;$pwd/lib/?.lua;$pwd/lib/?/init.lua;$pwd/../lib/?.lua;$pwd/../lib/?/init.lua;;";
init_by_lua_block {
_G.test_lib = require("resty.acme.storage.etcd")
_G.test_cfg = { protocol = "v3" }
_G.test_cfg = nil
_G.test_ttl = 1
}
};
Expand All @@ -24,9 +24,10 @@ __DATA__
location =/t {
content_by_lua_block {
local st = test_lib.new(test_cfg)
local err = st:set("key1", "2")
local key = "key1_" .. ngx.now()
local err = st:set(key, "2")
ngx.say(err)
local err = st:set("key1", "new value")
local err = st:set(key, "new value")
ngx.say(err)
}
}
Expand All @@ -44,9 +45,10 @@ __DATA__
location =/t {
content_by_lua_block {
local st = test_lib.new(test_cfg)
local err = st:set("key2", "3")
local key = "key2_" .. ngx.now()
local err = st:set(key, "3")
ngx.say(err)
local v, err = st:get("key2")
local v, err = st:get(key)
ngx.say(err)
ngx.say(v)
}
Expand All @@ -67,21 +69,22 @@ nil
location =/t {
content_by_lua_block {
local st = test_lib.new(test_cfg)
local err = st:set("key3", "3")
local key = "key3_" .. ngx.now()
local err = st:set(key, "3")
ngx.say(err)
local v, err = st:get("key3")
local v, err = st:get(key)
ngx.say(err)
ngx.say(v)
local err = st:delete("key3")
local err = st:delete(key)
ngx.say(err)
-- now the key should be deleted
local v, err = st:get("key3")
local v, err = st:get(key)
ngx.say(err)
ngx.say(v)
-- delete again with no error
local err = st:delete("key3")
local err = st:delete(key)
ngx.say(err)
}
}
Expand All @@ -105,14 +108,15 @@ nil
location =/t {
content_by_lua_block {
local st = test_lib.new(test_cfg)
local err = st:set("prefix1", "bb--")
local prefix = "prefix4_" .. ngx.now()
local err = st:set(prefix .. "prefix1", "bb--")
ngx.say(err)
local err = st:set("pref-x2", "aa--")
ngx.say(err)
local err = st:set("prefix3", "aa--")
local err = st:set(prefix .. "prefix3", "aa--")
ngx.say(err)
local keys, err = st:list("prefix")
local keys, err = st:list(prefix)
ngx.say(err)
table.sort(keys)
for _, p in ipairs(keys) do ngx.say(p) end
Expand All @@ -128,8 +132,8 @@ nil
nil
nil
nil
prefix1
prefix3
prefix4.+prefix1
prefix4.+prefix3
0
"
--- no_error_log
Expand All @@ -141,15 +145,24 @@ prefix3
location =/t {
content_by_lua_block {
local st = test_lib.new(test_cfg)
local err = st:set("setttl", "bb--", test_ttl)
local key = "key5_" .. ngx.now()
local err = st:set(key, "bb--", test_ttl)
ngx.say(err)
local v, err = st:get("setttl")
ngx.say(err)
ngx.say(v)
ngx.sleep(test_ttl)
local v, err = st:get("setttl")
local v, err = st:get(key)
ngx.say(err)
ngx.say(v)
for i=1, 5 do
ngx.sleep(1)
local v, err = st:get(key)
if err then
ngx.say(err)
ngx.exit(0)
elseif not v then
ngx.say(nil)
ngx.exit(0)
end
end
ngx.say("still exists")
}
}
--- request
Expand All @@ -159,7 +172,6 @@ prefix3
nil
bb--
nil
nil
"
--- no_error_log
[error]
Expand All @@ -170,15 +182,24 @@ nil
location =/t {
content_by_lua_block {
local st = test_lib.new(test_cfg)
local err = st:add("addttl", "bb--", test_ttl)
local key = "key6_" .. ngx.now()
local err = st:add(key, "bb--", test_ttl)
ngx.say(err)
local v, err = st:get("addttl")
ngx.say(err)
ngx.say(v)
ngx.sleep(test_ttl)
local v, err = st:get("addttl")
local v, err = st:get(key)
ngx.say(err)
ngx.say(v)
for i=1, 5 do
ngx.sleep(1)
local v, err = st:get(key)
if err then
ngx.say(err)
ngx.exit(0)
elseif not v then
ngx.say(nil)
ngx.exit(0)
end
end
ngx.say("still exists")
}
}
--- request
Expand All @@ -188,7 +209,6 @@ nil
nil
bb--
nil
nil
"
--- no_error_log
[error]
Expand All @@ -199,24 +219,31 @@ nil
location =/t {
content_by_lua_block {
local st = test_lib.new(test_cfg)
local err = st:set("prefix1", "bb--", test_ttl)
local key = "key7_" .. ngx.now()
local err = st:set(key, "bb--", test_ttl)
ngx.say(err)
local err = st:add("prefix1", "aa--")
local err = st:add(key, "aa--")
ngx.say(err)
local v, err = st:get("prefix1")
local v, err = st:get(key)
ngx.say(err)
ngx.say(v)
-- note: etcd evit expired node not immediately
ngx.sleep(test_ttl+0.5)
local err = st:add("prefix1", "aa--", test_ttl)
for i=1, 5 do
ngx.sleep(1)
local v, err = st:get(key)
if err then
ngx.say(err)
break
elseif not v then
ngx.say("key evicted")
break
end
end
local err = st:add(key, "aa--", test_ttl)
ngx.say(err)
local v, err = st:get("prefix1")
local v, err = st:get(key)
ngx.say(err)
ngx.say(v)
-- note: etcd evit expired node not immediately
ngx.sleep(test_ttl+0.5)
local err = st:add("prefix1", "aa--", test_ttl)
ngx.say(err)
}
}
--- request
Expand All @@ -226,10 +253,10 @@ nil
exists
nil
bb--
key evicted
nil
nil
aa--
nil
"
--- no_error_log
[error]
Expand Down

0 comments on commit 29c3834

Please sign in to comment.