Skip to content
Permalink
Browse files

feat(storage) introduce add/setnx api

  • Loading branch information
fffonion committed Nov 13, 2019
1 parent ef3e110 commit 895b041750ef4e920c3ed8ec432353f8e7e8eced
@@ -273,6 +273,8 @@ storage_config = {
}
```

Redis >= 2.6.0 is required as this storage requires [PEXPIRE](https://redis.io/commands/pexpire).

### vault

Hashicorp [Vault](https://www.vaultproject.io/) based storage. The default config is:
@@ -313,7 +315,6 @@ storage_config = {
TODO
====
- autossl: ocsp staping
- storage: implement ttl?
- openssl: add check for pkey has privkey

[Back to TOC](#table-of-contents)
@@ -11,7 +11,13 @@ function _M.new(conf)
return self, err
end
function _M:set(k, v)
-- set the key regardless of it's existence
function _M:set(k, v, ttl)
return err
end
-- set the key only if the key doesn't exist
function _M:add(k, v, ttl)
return err
end
@@ -61,27 +61,70 @@ local function api(self, method, uri, payload)
return decoded, err
end

function _M:set(k, v)
local res, err = api(self, "PUT", k, v)
local function set_cas(self, k, v, cas, ttl)
local params = {}
if ttl then
table.insert(params, string.format("flags=%d", (ngx.now() + ttl) * 1000))
end
if cas then
table.insert(params, string.format("cas=%d", cas))
end
local uri = k
if #params > 0 then
uri = uri .. "?" .. table.concat(params, "&")
end
local res, err = api(self, "PUT", uri, v)
if not res or err then
return err or "set key failed"
return err or "consul returned false"
end
end

function _M:add(k, v, ttl)
-- update_time is called in get()
-- we don't delete key automatically
local vget, err = self:get(k)
if err then
return "error reading key " .. err
end
if vget then
return "exists"
end
return nil
-- do cas for prevent race condition
return set_cas(self, k, v, 0, ttl)
end

function _M:delete(k)
local res, err = api(self, "DELETE", k)
function _M:set(k, v, ttl)
ngx.update_time()
return set_cas(self, k, v, nil, ttl)
end

function _M:delete(k, cas)
local uri = k
if cas then
uri = uri .. string.format("?cas=%d", cas)
end
local res, err = api(self, "DELETE", uri)
if not res or err then
return err or "delete key failed"
end
end

function _M:get(k)
local res, err = api(self, 'GET', k)
ngx.update_time()
if err then
return nil, err
elseif not res or not res[1] or not res[1]["Value"] then
return nil, nil
elseif res[1]["Flags"] and res[1]["Flags"] > 0 and res[1]["Flags"] < ngx.now() * 1000 then
err = self:delete(k, res[1]["ModifyIndex"])
if err then
return nil, "error cleanup expired key ".. err
end
return nil, nil
end
if res[1]["Value"] == ngx.null then
return nil, err
end
return ngx.decode_base64(res[1]["Value"]), err
end
@@ -20,7 +20,27 @@ local function regulate_filename(dir, s)
return dir .. "/" .. ngx.encode_base64(s)
end

function _M:set(k, v)
local function exists(f)
-- TODO: check for existence, not just able to open or not
local f, err = io.open(f, "rb")
if f then
f:close()
end
return err == nil
end

function _M:add(k, v, ttl)
local f = regulate_filename(self.dir, k)
if exists(f) then
return "exists"
end
return self:set(k, v, ttl)
end

function _M:set(k, v, ttl)
if ttl then
return "nyi"
end
local f = regulate_filename(self.dir, k)
local file, err = io.open(f, "wb")
if err then
@@ -33,15 +53,6 @@ function _M:set(k, v)
file:close()
end

function exists(f)
-- TODO: check for existence, not just able to open or not
local f, err = io.open(f, "rb")
if f then
f:close()
end
return err == nil
end

function _M:delete(k)
local f = regulate_filename(self.dir, k)
if not exists(f) then
@@ -57,7 +68,7 @@ function _M:get(k)
local f = regulate_filename(self.dir, k)
local file, err = io.open(f, "rb")
if err then
ngx.log(ngx.ERR, "can't read file: ", err)
ngx.log(ngx.INFO, "can't read file: ", err)
-- TODO: return nil, nil if not found
return nil, nil
end
@@ -50,11 +50,33 @@ local function op(self, op, ...)
return ok, err
end

function _M:set(k, v)
-- TODO: use EX/NX flag if we can determine redis version (>=2.6.12)
function _M:add(k, v, ttl)
local ok, err = op(self, 'setnx', k, v)
if err then
return err
elseif ok == 0 then
return "exists"
end
if ttl then
ok, err = op(self, 'pexpire', k, math.floor(ttl * 1000))
if err then
return err
end
end
end

function _M:set(k, v, ttl)
local ok, err = op(self, 'set', k, v)
if err then
return err
end
if ttl then
ok, err = op(self, 'pexpire', k, math.floor(ttl * 1000))
if err then
return err
end
end
end

function _M:delete(k)
@@ -22,12 +22,19 @@ function _M.new(conf)
return self
end

function _M:set(k, v)
self.shm:set(k, v)
function _M:add(k, v, ttl)
local _, err = self.shm:add(k, v, ttl)
return err
end

function _M:set(k, v, ttl)
local _, err = self.shm:set(k, v, ttl)
return err
end

function _M:delete(k)
self.shm:delete(k)
local _, err = self.shm:delete(k)
return err
end

function _M:get(k)
@@ -65,35 +65,89 @@ local function api(self, method, uri, payload)
return decoded, err
end

function _M:set(k, v)
local res, err = api(self, "POST", self.data_url .. k, {
local function set_cas(self, k, v, cas, ttl)
if ttl then
if ttl > 0 and ttl < 1 then
ngx.log(ngx.WARN, "vault doesn't support ttl less than 1s, will use 1s")
end
ttl = 1
-- first update the metadata
local _, err = api(self, "POST", self.metadata_url .. k, {
delete_version_after = string.format("%dms", ttl * 1000)
})
-- vault doesn't seem return any useful info in this api ?
if err then
return err
end
end

local payload = {
data = {
value = v,
note = "managed by lua-resty-acme",
}
})
}
if cas then
payload.options = {
cas = cas,
}
end
local res, err = api(self, "POST", self.data_url .. k, payload)
if not res or err then
return err or "set key failed"
end
return nil
end

function _M:delete(k)
local res, err = api(self, "DELETE", self.metadata_url .. k)
local function get(self, k)
local res, err = api(self, 'GET', self.data_url .. k)
if err then
return nil, err
elseif not res or not res["data"] or not res["data"]["data"]
or not res["data"]["data"]["value"] then
return nil, nil
end
return res['data'], nil
end

function _M:add(k, v, ttl)
-- we don't delete key automatically
local vget, err = get(self, k)
if err then
return "error reading key " .. err
end
local revision
-- if there's no 'data' meaning all versions are gone, then we are good
if vget then
if vget['data'] then
return "exists"
end
revision = vget['metadata'] and vget['metadata']['version'] or 0
end
ngx.update_time()
-- do cas for prevent race condition
return set_cas(self, k, v, revision, ttl)
end

function _M:set(k, v, ttl)
ngx.update_time()
return set_cas(self, k, v, nil, ttl)
end

function _M:delete(k, cas)
-- delete metadata will delete all versions of secret as well
local _, err = api(self, "DELETE", self.metadata_url .. k)
if err then
return "delete key failed"
end
end

function _M:get(k)
local res, err = api(self, 'GET', self.data_url .. k)
local v, err = get(self, k)
if err then
return nil, err
elseif not res or not res["data"] or not res["data"]["data"]
or not res["data"]["data"]["value"] then
return nil, nil
end
return res["data"]["data"]["value"], err
return v and v["data"]["value"], err
end

local empty_table = {}

0 comments on commit 895b041

Please sign in to comment.
You can’t perform that action at this time.