You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by mh...@apache.org on 2017/06/29 19:23:48 UTC
[incubator-openwhisk-apigateway] branch master updated:
Snapshotting of tenant swagger in redis (#222)
This is an automated email from the ASF dual-hosted git repository.
mhamann pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk-apigateway.git
The following commit(s) were added to refs/heads/master by this push:
new f84a9d6 Snapshotting of tenant swagger in redis (#222)
f84a9d6 is described below
commit f84a9d6db4a563f9e8a55714060750cf6ce334c2
Author: Taylor King <ta...@gmail.com>
AuthorDate: Thu Jun 29 15:23:46 2017 -0400
Snapshotting of tenant swagger in redis (#222)
* basic tenant based snapshotting
* add some tests
* some basic management apis are working now
* subscriptions endpoint works with snapshots
* add a lock
* tests passing
---
api-gateway.conf | 2 +-
scripts/lua/lib/dataStore.lua | 68 ++++++++++++----
scripts/lua/lib/redis.lua | 103 ++++++++++++++++++++----
scripts/lua/management/lib/resources.lua | 3 +
scripts/lua/management/lib/subscriptions.lua | 42 +++-------
scripts/lua/management/routes/apis.lua | 4 +-
scripts/lua/management/routes/subscriptions.lua | 66 ++++++++-------
scripts/lua/routing.lua | 16 +++-
tests/fakeredis.lua | 10 ++-
tests/scripts/lua/lib/subscriptions.lua | 16 ++--
tests/scripts/lua/routing.lua | 16 ++++
tests/scripts/lua/security.lua | 64 ++++++++++++++-
12 files changed, 300 insertions(+), 110 deletions(-)
diff --git a/api-gateway.conf b/api-gateway.conf
index e206e48..4c18873 100644
--- a/api-gateway.conf
+++ b/api-gateway.conf
@@ -36,7 +36,7 @@ env PORT;
env REDIS_RETRY_COUNT;
-
+env SNAPSHOTTING;
env CACHING_ENABLED;
env CACHE_SIZE;
env CACHE_TTL;
diff --git a/scripts/lua/lib/dataStore.lua b/scripts/lua/lib/dataStore.lua
index 52aa773..764d870 100644
--- a/scripts/lua/lib/dataStore.lua
+++ b/scripts/lua/lib/dataStore.lua
@@ -1,8 +1,3 @@
-
-
-
-
-
local DATASTORE = os.getenv( 'DATASTORE')
local utils = require('lib/utils')
@@ -21,6 +16,13 @@ function DataStore:init()
return o
end
+function DataStore:setSnapshotId(tenant)
+ self.snapshotId = self.impl.getSnapshotId(self.ds, tenant)
+ self:lockSnapshot(snapshotId)
+ if self.snapshotId == ngx.null then
+ self.snapshotId = nil
+ end
+end
-- right now just using this for the tests
function DataStore:initWithDriver(ds)
local o = {}
@@ -31,11 +33,16 @@ local o = {}
return o
end
+function DataStore:lockSnapshot(snapshotId)
+ return self.impl.lockSnapshot(self.ds, snapshotId)
+end
+
function DataStore:close()
return self.impl.close(self.ds)
end
function DataStore:addAPI(id, apiObj, existingAPI)
+ self:setSnapshotId(apiObj.tenantId)
return self.impl.addAPI(self.ds, id, apiObj, existingAPI)
end
@@ -52,7 +59,7 @@ function DataStore:deleteAPI(id)
end
function DataStore:resourceToApi(resource)
- return self.impl.resourceToApi(self.ds, resource)
+ return self.impl.resourceToApi(self.ds, resource, self.snapshotId)
end
function DataStore:generateResourceObj(ops, apiId, tenantObj, cors)
@@ -60,49 +67,62 @@ function DataStore:generateResourceObj(ops, apiId, tenantObj, cors)
end
function DataStore:createResource(key, field, resourceObj)
- return self.impl.createResource(self.ds, key, field, resourceObj)
+ return self.impl.createResource(self.ds, key, field, resourceObj, self.snapshotId)
end
function DataStore:addResourceToIndex(index, resourceKey)
- return self.impl.addResourceToIndex(self.ds, index, resourceKey)
+ return self.impl.addResourceToIndex(self.ds, index, resourceKey, self.snapshotId)
end
function DataStore:deleteResourceFromIndex(index, resourceKey)
- return self.impl.deleteResourceFromIndex(self.ds, index, resourceKey)
+ return self.impl.deleteResourceFromIndex(self.ds, index, resourceKey, self.snapshotId)
end
function DataStore:getResource(key, field)
- return self.impl.getResource(self.ds, key, field)
+ return self.impl.getResource(self.ds, key, field, self.snapshotId)
end
function DataStore:getAllResources(tenantId)
- return self.impl.getAllResources(self.ds, tenantId)
+ return self.impl.getAllResources(self.ds, tenantId, self.snapshotId)
end
function DataStore:deleteResource(key, field)
- return self.impl.deleteResource(self.ds, key, field)
+ return self.impl.deleteResource(self.ds, key, field, self.snapshotId)
end
+
function DataStore:addTenant(id, tenantObj)
return self.impl.addTenant(self.ds, id, tenantObj)
end
+
function DataStore:getAllTenants()
return self.impl.getAllTenants(self.ds)
end
+
function DataStore:getTenant(id)
return self.impl.getTenant(self.ds, id)
end
+
function DataStore:deleteTenant(id)
return self.impl.deleteTenant(self.ds, id)
end
+
function DataStore:createSubscription(key)
- return self.impl.createSubscription(self.ds, key)
+ return self.impl.createSubscription(self.ds, key, self.snapshotId)
end
+
function DataStore:deleteSubscription(key)
- return self.impl.deleteSubscription(self.ds, key)
+ return self.impl.deleteSubscription(self.ds, key, self.snapshotId)
+end
+
+function DataStore:getSubscriptions(artifactId, tenantId)
+ return self.impl.getSubscriptions(artifactId, tenantId)
end
+
function DataStore:healthCheck()
return self.impl.healthCheck(self.ds)
end
+
function DataStore:addSwagger(id, swagger)
return self.impl.addSwagger(self.ds, id, swagger)
end
+
function DataStore:getSwagger(id)
return self.impl.getSwagger(self.ds, id)
end
@@ -120,7 +140,7 @@ function DataStore:saveOAuthToken(provider, token, body, ttl)
end
function DataStore:exists(key)
- return self.impl.exists(self.ds, key)
+ return self.impl.exists(self.ds, key, self.snapshotId)
end
function DataStore:setRateLimit(key, value, interval, expires)
@@ -129,8 +149,22 @@ end
function DataStore:getRateLimit(key)
return self.impl.getRateLimit(self.ds, key)
end
--- to be removed in the future
-
+-- o be removed in the future
+
+function DataStore:deleteSubscriptionAdv(artifactId, tenantId, clientId)
+ local subscriptionKey = utils.concatStrings({"subscriptions:tenant:", tenantId, ":api:", artifactId})
+ local key = utils.concatStrings({subscriptionKey, ":key:", clientId})
+ if self:exists(key) == 1 then
+ self:deleteSubscription(key)
+ else
+ local pattern = utils.concatStrings({subscriptionKey, ":clientsecret:" , clientId, ":*"})
+ local res = self.impl.cleanSubscriptions(self.ds, pattern)
+ if res == false then
+ return false
+ end
+ end
+ return true
+end
function _M.init()
return DataStore:init()
diff --git a/scripts/lua/lib/redis.lua b/scripts/lua/lib/redis.lua
index 78caab4..bfc24fc 100644
--- a/scripts/lua/lib/redis.lua
+++ b/scripts/lua/lib/redis.lua
@@ -126,15 +126,16 @@ function _M.addAPI(red, id, apiObj, existingAPI)
end
end
else
+ local snapshotId = _M.getSnapshotId(red, apiObj.tenantId)
-- Delete all resources for the existingAPI
local basePath = existingAPI.basePath:sub(2)
for path, v in pairs(existingAPI.resources) do
local gatewayPath = ngx.unescape_uri(utils.concatStrings({basePath, ngx.escape_uri(path)}))
gatewayPath = gatewayPath:sub(1,1) == "/" and gatewayPath:sub(2) or gatewayPath
local redisKey = utils.concatStrings({"resources:", existingAPI.tenantId, ":", gatewayPath})
- _M.deleteResource(red, redisKey, REDIS_FIELD)
+ _M.deleteResource(red, redisKey, REDIS_FIELD, snapshotId)
local indexKey = utils.concatStrings({"resources:", existingAPI.tenantId, ":__index__"})
- _M.deleteResourceFromIndex(red, indexKey, redisKey)
+ _M.deleteResourceFromIndex(red, indexKey, redisKey, snapshotId)
end
end
-- Add new API
@@ -180,7 +181,10 @@ function _M.deleteAPI(red, id)
end
end
-function _M.resourceToApi(red, resource)
+function _M.resourceToApi(red, resource, snapshotId)
+ if snapshotId ~= nil then
+ resource = utils.concatStrings({'snapshots:', snapshotId, ':', resource})
+ end
local resource = hget(red, resource, "resources")
if resource == ngx.null then
return nil
@@ -232,7 +236,10 @@ end
-- @param key redis resource key
-- @param field redis resource field
-- @param resourceObj redis object containing operations for resource
-function _M.createResource(red, key, field, resourceObj)
+function _M.createResource(red, key, field, resourceObj, snapshotId)
+ if snapshotId ~= nil then
+ key = utils.concatStrings({'snapshots:', snapshotId, ':', key})
+ end
-- Add/update resource to redis
local ok, err = hset(red, key, field, resourceObj)
if not ok then
@@ -244,7 +251,10 @@ end
-- @param red redis client instance
-- @param index index key
-- @param resourceKey resource key to add
-function _M.addResourceToIndex(red, index, resourceKey)
+function _M.addResourceToIndex(red, index, resourceKey, snapshotId)
+ if snapshotId ~= nil then
+ index = utils.concatStrings({'snapshots:', snapshotId, ':', index})
+ end
local ok, err = sadd(red, index, resourceKey)
if not ok then
request.err(500, utils.concatStrings({"Failed to update the resource index set: ", err}))
@@ -255,7 +265,10 @@ end
-- @param red redis client instance
-- @param index index key
-- @param key resourceKey key to delete
-function _M.deleteResourceFromIndex(red, index, resourceKey)
+function _M.deleteResourceFromIndex(red, index, resourceKey, snapshotId)
+ if snapshotId ~= nil then
+ index = utils.concatStrings({'snapshots:', snapshotId, ':', index})
+ end
local ok, err = srem(red, index, resourceKey)
if not ok then
request.err(500, utils.concatStrings({"Failed to update the resource index set: ", err}))
@@ -266,8 +279,12 @@ end
-- @param red redis client instance
-- @param key redis resource key
-- @param field redis resource field
+-- @param snapshotId an optional snapshotId
-- @return resourceObj redis object containing operations for resource
-function _M.getResource(red, key, field)
+function _M.getResource(red, key, field, snapshotId)
+ if snapshotId ~= nil then
+ key = utils.concatStrings({"snapshots:", snapshotId, ":", key})
+ end
local resourceObj, err = hget(red, key, field)
if not resourceObj then
request.err(500, utils.concatStrings({"Failed to retrieve the resource: ", err}))
@@ -282,19 +299,31 @@ end
--- Get all resource keys for a tenant in redis
-- @param red redis client instance
-- @param tenantId tenant id
-function _M.getAllResources(red, tenantId)
- local keys, err = smembers(red, utils.concatStrings({"resources:", tenantId, ":__index__"}))
+function _M.getAllResources(red, tenantId, snapshotId)
+ local key = utils.concatStrings({'resources:', tenantId, ':__index__'})
+ if snapshotId ~= nil then
+ key = utils.concatStrings({'snapshots:', snapshotId, ':', key})
+ end
+ local keys, err = smembers(red, key)
if not keys then
request.err(500, utils.concatStrings({"Failed to retrieve resource keys: ", err}))
end
- return keys
+ local result = {}
+ for _, v in ipairs(keys) do
+ local str = v:gsub(utils.concatStrings({'snapshots:', snapshotId, ':', ''}), '')
+ table.insert(result, str)
+ end
+ return result
end
--- Delete resource in redis
-- @param red redis client instance
-- @param key redis resource key
-- @param field redis resource field
-function _M.deleteResource(red, key, field)
+function _M.deleteResource(red, key, field, snapshotId)
+ if snapshotId ~= nil then
+ key = utils.concatStrings({'snapshots:', snapshotId, ':', key})
+ end
local resourceObj, err = hget(red, key, field)
if not resourceObj then
request.err(500, utils.concatStrings({"Failed to delete the resource: ", err}))
@@ -339,6 +368,14 @@ function _M.addTenant(red, id, tenantObj)
return tenantObj
end
+function _M.getSnapshotId(red, tenantId)
+ local result = red:get(utils.concatStrings({'snapshots:tenant:', tenantId}))
+ if result == ngx.null then
+ return nil
+ end
+ return result
+end
+
--- Get all tenants from redis
-- @param red Redis client instance
function _M.getAllTenants(red)
@@ -380,7 +417,10 @@ end
--- Create/update subscription/apikey in redis
-- @param red redis client instance
-- @param key redis subscription key to create
-function _M.createSubscription(red, key)
+function _M.createSubscription(red, key, snapshotId)
+ if snapshotId ~= nil then
+ key = utils.concatStrings({'snapshots:', snapshotId, ':', key})
+ end
-- Add/update a subscription key to redis
local ok, err = set(red, key, '')
if not ok then
@@ -391,7 +431,10 @@ end
--- Delete subscription/apikey int redis
-- @param red redis client instance
-- @param key redis subscription key to delete
-function _M.deleteSubscription(red, key)
+function _M.deleteSubscription(red, key, snapshotId)
+ if snapshotId ~= nil then
+ key = utils.concatStrings({'snapshots:', snapshotId, ':', key})
+ end
local subscription, err = get(red, key)
if not subscription then
request.err(500, utils.concatStrings({"Failed to delete the subscription key: ", err}))
@@ -405,6 +448,30 @@ function _M.deleteSubscription(red, key)
end
end
+function _M.cleanSubscriptions(red, pattern)
+ return red:eval("return redis.call('del', unpack(redis.call('keys', ARGV[1])))", 0, pattern)
+end
+
+
+function _M.getSubscriptions(red, artifactId, tenantId, snapshotId)
+ local res = red:scan(0, "match", utils.concatStrings({"subscriptions:tenant:", tenantId, ":api:", artifactId, ":*"}))
+ local cursor = res[1]
+ local subscriptions = {}
+ for _, v in pairs(res[2]) do
+ local matched = {string.match(v, "subscriptions:tenant:([^:]+):api:([^:]+):([^:]+):([^:]+):*")}
+ subscriptions[#subscriptions + 1] = matched[4]
+ end
+ while cursor ~= "0" do
+ res = red:scan(cursor, "match", utils.concatStrings({"subscriptions:tenant:", tenantId, ":api:", artifactId, ":*"}))
+ cursor = res[1]
+ for _, v in pairs(res[2]) do
+ local matched = {string.match(v, "subscriptions:tenant:([^:]+):api:([^:]+):([^:]+):([^:]+):*")}
+ subscriptions[#subscriptions + 1] = matched[4]
+ end
+ end
+ return subscriptions
+end
+
-----------------------------
--- OAuth Tokens ---
-----------------------------
@@ -470,9 +537,17 @@ end
function _M.getRateLimit(red, key)
return get(red, key)
end
+
+function _M.lockSnapshot(red, snapshotId)
+ red:set(utils.concatStrings({'lock:snapshots:', snapshotId}), 'true')
+ red:expire(utils.concatStrings({'lock:snapshots:', snapshotId}), 60)
+end
-- LRU Caching methods
-function exists(red, key)
+function exists(red, key, snapshotId)
+ if snapshotId ~= nil then
+ key = utils.concatStrings({'snapshots:', snapshotId, ':', key})
+ end
if CACHING_ENABLED then
local cached = c:get(key)
if cached ~= nil then
diff --git a/scripts/lua/management/lib/resources.lua b/scripts/lua/management/lib/resources.lua
index 041c481..5c530cb 100644
--- a/scripts/lua/management/lib/resources.lua
+++ b/scripts/lua/management/lib/resources.lua
@@ -38,6 +38,8 @@ function _M.addResource(dataStore, resource, gatewayPath, tenantObj)
local apiId = resource.apiId
local cors = resource.cors
local resourceObj = dataStore:generateResourceObj(operations, apiId, tenantObj, cors)
+ print ('setting snapshot for id: ' .. tenantObj.id)
+ dataStore:setSnapshotId(tenantObj.id)
dataStore:createResource(redisKey, REDIS_FIELD, resourceObj)
local indexKey = utils.concatStrings({"resources:", tenantObj.id, ":__index__"})
dataStore:addResourceToIndex(indexKey, redisKey)
@@ -48,6 +50,7 @@ end
-- @param gatewayPath path in gateway
-- @param tenantId tenant id
function _M.deleteResource(dataStore, gatewayPath, tenantId)
+ dataStore:setSnapshotId(tenantId)
local redisKey = utils.concatStrings({"resources:", tenantId, ":", gatewayPath})
dataStore:deleteResource(redisKey, REDIS_FIELD)
local indexKey = utils.concatStrings({"resources:", tenantId, ":__index__"})
diff --git a/scripts/lua/management/lib/subscriptions.lua b/scripts/lua/management/lib/subscriptions.lua
index 9671ef1..fdbde79 100644
--- a/scripts/lua/management/lib/subscriptions.lua
+++ b/scripts/lua/management/lib/subscriptions.lua
@@ -26,8 +26,9 @@ local utils = require "lib/utils"
local _M = {}
-function _M.addSubscription(red, artifactId, tenantId, clientId, clientSecret, hashFunction)
+function _M.addSubscription(dataStore, artifactId, tenantId, clientId, clientSecret, hashFunction)
local subscriptionKey = utils.concatStrings({"subscriptions:tenant:", tenantId, ":api:", artifactId})
+ dataStore:setSnapshotId(tenantId)
if clientSecret ~= nil then
if hashFunction == nil then
hashFunction = utils.hash
@@ -36,41 +37,16 @@ function _M.addSubscription(red, artifactId, tenantId, clientId, clientSecret, h
else
subscriptionKey = utils.concatStrings({subscriptionKey, ":key:", clientId})
end
- redis.createSubscription(red, subscriptionKey)
+ dataStore:createSubscription(subscriptionKey)
end
-function _M.getSubscriptions(red, artifactId, tenantId)
- local res = red:scan(0, "match", utils.concatStrings({"subscriptions:tenant:", tenantId, ":api:", artifactId, ":*"}))
- local cursor = res[1]
- local subscriptions = {}
- for _, v in pairs(res[2]) do
- local matched = {string.match(v, "subscriptions:tenant:([^:]+):api:([^:]+):([^:]+):([^:]+):*")}
- subscriptions[#subscriptions + 1] = matched[4]
- end
- while cursor ~= "0" do
- res = red:scan(cursor, "match", utils.concatStrings({"subscriptions:tenant:", tenantId, ":api:", artifactId, ":*"}))
- cursor = res[1]
- for _, v in pairs(res[2]) do
- local matched = {string.match(v, "subscriptions:tenant:([^:]+):api:([^:]+):([^:]+):([^:]+):*")}
- subscriptions[#subscriptions + 1] = matched[4]
- end
- end
- return subscriptions
+function _M.getSubscriptions(dataStore, artifactId, tenantId)
+ dataStore:setSnapshotId(tenantId)
+ return dataStore:getSubscriptions(artifactId, tenantId)
end
-function _M.deleteSubscription(red, artifactId, tenantId, clientId)
- local subscriptionKey = utils.concatStrings({"subscriptions:tenant:", tenantId, ":api:", artifactId})
- local key = utils.concatStrings({subscriptionKey, ":key:", clientId})
- if redis.exists(red, key) == 1 then
- redis.deleteSubscription(red, key)
- else
- local pattern = utils.concatStrings({subscriptionKey, ":clientsecret:" , clientId, ":*"})
- local res = red:eval("return redis.call('del', unpack(redis.call('keys', ARGV[1])))", 0, pattern)
- if res == false then
- return false
- end
- end
- return true
+function _M.deleteSubscription(dataStore, artifactId, tenantId, clientId)
+ return dataStore:deleteSubscriptionAdv(artifactId, tenantId, clientId)
end
-return _M
\ No newline at end of file
+return _M
diff --git a/scripts/lua/management/routes/apis.lua b/scripts/lua/management/routes/apis.lua
index 5e70e4f..6e9d3d1 100644
--- a/scripts/lua/management/routes/apis.lua
+++ b/scripts/lua/management/routes/apis.lua
@@ -99,7 +99,7 @@ function getAPIs(dataStore)
managed_url = api.managedUrl,
open_api_doc = dataStore:getSwagger(api.id)
}
- dataStore.close()
+ dataStore:close()
request.success(200, cjson.encode(returnObj))
end
end
@@ -134,7 +134,7 @@ function addAPI(dataStore)
end
-- Check for api id in JSON body
if existingAPI == nil and decoded.id ~= nil then
- existingAPI = dataStore.getAPI(decoded.id)
+ existingAPI = dataStore:getAPI(decoded.id)
if existingAPI == nil then
dataStore:close()
request.err(404, utils.concatStrings({"Unknown API id ", decoded.id}))
diff --git a/scripts/lua/management/routes/subscriptions.lua b/scripts/lua/management/routes/subscriptions.lua
index 40b5986..0d2c5e5 100644
--- a/scripts/lua/management/routes/subscriptions.lua
+++ b/scripts/lua/management/routes/subscriptions.lua
@@ -33,12 +33,12 @@ local REDIS_PASS = os.getenv("REDIS_PASS")
local _M = {}
-function _M.requestHandler()
+function _M.requestHandler(dataStore)
local version = ngx.var.version
if version == "v2" then
- v2()
+ v2(dataStore)
elseif version == "v1" then
- v1()
+ v1(dataStore)
else
request.err(404, "404 Not found")
end
@@ -47,23 +47,25 @@ end
-- v2 --
-function v2()
+function v2(dataStore)
local requestMethod = ngx.req.get_method()
if requestMethod == "POST" or requestMethod == "PUT" then
- v2AddSubscription()
+ v2AddSubscription(dataStore)
elseif requestMethod == "GET" then
- v2GetSubscriptions()
+ v2GetSubscriptions(dataStore)
elseif requestMethod == "DELETE" then
- v2DeleteSubscription()
+ v2DeleteSubscription(dataStore)
else
+ dataStore:close()
request.err(400, "Invalid verb")
end
end
-function v2AddSubscription()
+function v2AddSubscription(dataStore)
ngx.req.read_body()
local args = ngx.req.get_body_data()
if not args then
+ dataStore:close()
request.err(400, "Missing request body.")
end
local decoded = cjson.decode(args)
@@ -71,13 +73,12 @@ function v2AddSubscription()
if res == false then
request.err(err.statusCode, err.message)
end
- local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
local artifactId = decoded.artifact_id
local tenantId = ngx.var.tenant_id
local clientId = decoded.client_id
local clientSecret = decoded.client_secret
- subscriptions.addSubscription(red, artifactId, tenantId, clientId, clientSecret, utils.hash)
- redis.close(red)
+ subscriptions.addSubscription(dataStore, artifactId, tenantId, clientId, clientSecret, utils.hash)
+ dataStore:close()
local result = {
message = utils.concatStrings({"Subscription '", clientId, "' created for API '", artifactId, "'"})
}
@@ -85,20 +86,19 @@ function v2AddSubscription()
request.success(200, cjson.encode(result))
end
-function v2GetSubscriptions()
+function v2GetSubscriptions(dataStore)
local tenantId = ngx.var.tenant_id
local artifactId = ngx.req.get_uri_args()["artifact_id"]
if artifactId == nil or artifactId == "" then
request.err(400, "Missing artifact_id")
end
- local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
- local subscriptionList = subscriptions.getSubscriptions(red, artifactId, tenantId)
+ local subscriptionList = subscriptions.getSubscriptions(dataStore, artifactId, tenantId)
redis.close(red)
ngx.header.content_type = "application/json; charset=utf-8"
request.success(200, cjson.encode(subscriptionList))
end
-function v2DeleteSubscription()
+function v2DeleteSubscription(dataStore)
local clientId = ngx.var.client_id
local tenantId = ngx.var.tenant_id
local artifactId = ngx.req.get_uri_args()["artifact_id"]
@@ -108,8 +108,7 @@ function v2DeleteSubscription()
if artifactId == nil or artifactId == "" then
request.err(400, "Missing artifact_id")
end
- local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
- local res = subscriptions.deleteSubscription(red, artifactId, tenantId, clientId)
+ local res = subscriptions.deleteSubscription(dataStore, artifactId, tenantId, clientId)
if res == false then
request.err(404, "Subscription doesn't exist")
end
@@ -120,38 +119,38 @@ end
-- v1 --
-function v1()
+function v1(dataStore)
local requestMethod = ngx.req.get_method()
if requestMethod == "POST" or requestMethod == "PUT" then
- addSubscription()
+ addSubscription(dataStore)
elseif requestMethod == "DELETE" then
- deleteSubscription()
+ deleteSubscription(dataStore)
else
+ dataStore:close()
request.err(400, "Invalid verb")
end
end
-function addSubscription()
- local redisKey = validateSubscriptionBody()
- local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
- redis.createSubscription(red, redisKey)
- redis.close(red)
+function addSubscription(dataStore)
+ local redisKey = validateSubscriptionBody(dataStore)
+ dataStore:createSubscription(redisKey)
+ dataStore:close()
request.success(200, "Subscription created.")
end
-function deleteSubscription()
- local redisKey = validateSubscriptionBody()
- local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
- redis.deleteSubscription(red, redisKey)
- redis.close(red)
+function deleteSubscription(dataStore)
+ local redisKey = validateSubscriptionBody(dataStore)
+ dataStore:deleteSubscription(redisKey)
+ dataStore:close()
request.success(200, "Subscription deleted.")
end
-function validateSubscriptionBody()
+function validateSubscriptionBody(dataStore)
-- Read in the PUT JSON Body
ngx.req.read_body()
local args = ngx.req.get_body_data()
if not args then
+ dataStore:close()
request.err(400, "Missing request body.")
end
-- Convert json into Lua table
@@ -159,12 +158,14 @@ function validateSubscriptionBody()
-- Check required fields
local res, err = utils.tableContainsAll(decoded, {"key", "scope", "tenantId"})
if res == false then
+ dataStore:close()
request.err(err.statusCode, err.message)
end
-- Check if we're using tenant or resource or api
local resource = decoded.resource
local apiId = decoded.apiId
local redisKey
+ dataStore:setSnapshotId(decoded.tenantId)
local prefix = utils.concatStrings({"subscriptions:tenant:", decoded.tenantId})
if decoded.scope == "tenant" then
redisKey = prefix
@@ -172,15 +173,18 @@ function validateSubscriptionBody()
if resource ~= nil then
redisKey = utils.concatStrings({prefix, ":resource:", resource})
else
+ dataStore:close()
request.err(400, "\"resource\" missing from request body.")
end
elseif decoded.scope == "api" then
if apiId ~= nil then
redisKey = utils.concatStrings({prefix, ":api:", apiId})
else
+ dataStore:close()
request.err(400, "\"apiId\" missing from request body.")
end
else
+ dataStore:close()
request.err(400, "Invalid scope")
end
redisKey = utils.concatStrings({redisKey, ":key:", decoded.key})
diff --git a/scripts/lua/routing.lua b/scripts/lua/routing.lua
index cd1d11b..09003c0 100644
--- a/scripts/lua/routing.lua
+++ b/scripts/lua/routing.lua
@@ -33,12 +33,18 @@ local backendRouting = require "policies/backendRouting"
local cors = require "cors"
+local SNAPSHOTTING = os.getenv('SNAPSHOTTING')
+
local _M = {}
--- Main function that handles parsing of invocation details and carries out implementation
function _M.processCall(dataStore)
-- Get resource object from redis
local tenantId = ngx.var.tenant
+
+ if SNAPSHOTTING == 'true' then
+ dataStore:setSnapshotId(tenantId)
+ end
local gatewayPath = ngx.var.gatewayPath
local i, j = ngx.var.request_uri:find("/api/([^/]+)")
ngx.var.analyticsUri = ngx.var.request_uri:sub(j+1)
@@ -46,11 +52,17 @@ function _M.processCall(dataStore)
setRequestLogs()
end
local resourceKeys = dataStore:getAllResources(tenantId)
+ print(cjson.encode(resourceKeys))
local redisKey = _M.findResource(resourceKeys, tenantId, gatewayPath)
if redisKey == nil then
request.err(404, 'Not found.')
end
- local obj = cjson.decode(dataStore:getResource(redisKey, "resources"))
+ local resource = dataStore:getResource(redisKey, "resources")
+ if resource == nil then
+ request.err(404, 'Snapshot not found.')
+ end
+ local obj = cjson.decode(resource)
+
cors.processCall(obj)
ngx.var.tenantNamespace = obj.tenantNamespace
ngx.var.tenantInstance = obj.tenantInstance
@@ -82,7 +94,7 @@ function _M.processCall(dataStore)
setRequestLogs()
end
dataStore:close()
- return
+ return nil
end
end
request.err(404, 'Whoops. Verb not supported.')
diff --git a/tests/fakeredis.lua b/tests/fakeredis.lua
index 1e9fddc..afad80f 100644
--- a/tests/fakeredis.lua
+++ b/tests/fakeredis.lua
@@ -1,5 +1,5 @@
local unpack = table.unpack or unpack
-
+local cjson = require 'cjson'
--- Bit operations
local bit
@@ -308,6 +308,11 @@ local bitcount = function(self, k, i1, i2)
return r
end
+
+local expire = function(self, k, x)
+ return nil -- do nothing for now
+end
+
local bitop = function(self, op, k, ...)
assert(type(op) == "string")
op = op:lower()
@@ -1597,6 +1602,7 @@ local methods = {
randomkey = randomkey, -- () -> [k|nil]
rename = chkargs_wrap(rename, 2), -- (k,k2) -> true
renamenx = chkargs_wrap(renamenx, 2), -- (k,k2) -> ! existed? k2
+ expire = chkargs_wrap(expire, 2),
-- strings
append = chkargs_wrap(append, 2), -- (k,v) -> #new
bitcount = bitcount, -- (k,[start,end]) -> n
@@ -1613,7 +1619,7 @@ local methods = {
mget = mget, -- (k1,...) -> {v1,...}
mset = mset, -- (k1,v1,...) -> true
msetnx = msetnx, -- (k1,v1,...) -> worked? (i.e. !existed? any k)
- set = chkargs_wrap(set, 2), -- (k,v) -> true
+ set = chkargs_wrap(set, 2), -- (k,v,expiration) -> true -- toss the expiration if it's passed in
setbit = setbit, -- (k,offset,b) -> old
setnx = chkargs_wrap(setnx, 2), -- (k,v) -> worked? (i.e. !existed?)
setrange = setrange, -- (k,offset,val) -> #new
diff --git a/tests/scripts/lua/lib/subscriptions.lua b/tests/scripts/lua/lib/subscriptions.lua
index 916d0ac..250733d 100644
--- a/tests/scripts/lua/lib/subscriptions.lua
+++ b/tests/scripts/lua/lib/subscriptions.lua
@@ -35,13 +35,15 @@ describe("Testing v2 subscriptions API", function()
local artifactId = "abc"
local tenantId = "testtenant"
local clientId = "xxx"
- subscriptions.addSubscription(red, artifactId, tenantId, clientId)
+ local ds = require 'lib/dataStore'
+ local dataStore = ds.initWithDriver(red)
+ subscriptions.addSubscription(dataStore, artifactId, tenantId, clientId)
local generated = red:exists("subscriptions:tenant:" .. tenantId .. ":api:" .. artifactId .. ":key:" .. clientId)
assert.are.equal(1, generated)
-- client id and secret
local newClientId = "newclientid"
local clientSecret = "secret"
- subscriptions.addSubscription(red, artifactId, tenantId, newClientId, clientSecret, generateHash)
+ subscriptions.addSubscription(dataStore, artifactId, tenantId, newClientId, clientSecret, generateHash)
generated = red:exists("subscriptions:tenant:" .. tenantId .. ":api:" .. artifactId .. ":clientsecret:" .. newClientId .. ":" .. generateHash(clientSecret))
assert.are.equal(1, generated)
-- wrong secret
@@ -49,15 +51,17 @@ describe("Testing v2 subscriptions API", function()
generated = red:exists("subscriptions:tenant:" .. tenantId .. ":api:" .. artifactId .. ":clientsecret:" .. newClientId .. ":" .. generateHash(badsecret))
assert.are.equal(0, generated)
end)
-
+
it("should delete a subscription", function()
local artifactId = "abc"
local tenantId = "testtenant"
local clientId = "12345"
- subscriptions.addSubscription(red, artifactId, tenantId, clientId)
+ local ds = require 'lib/dataStore'
+ local dataStore = ds.initWithDriver(red)
+ subscriptions.addSubscription(dataStore, artifactId, tenantId, clientId)
local generated = red:exists("subscriptions:tenant:" .. tenantId .. ":api:" .. artifactId .. ":key:" .. clientId)
assert.are.same(1, generated)
- subscriptions.deleteSubscription(red, artifactId, tenantId, clientId)
+ subscriptions.deleteSubscription(dataStore, artifactId, tenantId, clientId)
generated = red:exists("subscriptions:tenant:" .. tenantId .. ":api:" .. artifactId .. ":key:" .. clientId)
assert.are.same(0, generated)
end)
@@ -66,4 +70,4 @@ end)
-- Generate fake hash
function generateHash(str)
return string.byte(str)*13 + 2961
-end
\ No newline at end of file
+end
diff --git a/tests/scripts/lua/routing.lua b/tests/scripts/lua/routing.lua
index 8d5b68b..2447d51 100644
--- a/tests/scripts/lua/routing.lua
+++ b/tests/scripts/lua/routing.lua
@@ -58,6 +58,7 @@ describe('Testing routing module', function()
local tenant = 'guest'
local path = 'bp1/bad/path'
local actual = routing.findResource(keys, tenant, path)
+ print(actual)
assert.are.same(expected, actual)
end)
@@ -118,5 +119,20 @@ describe('Testing routing module', function()
actual = ngx.ctx.var2
assert.are.same(expected, actual)
end)
+end)
+describe('Testing routing with snapshotting', function()
+ it('Gets a redis key for a tenant with the correct snapshot', function()
+ local red = fakeredis.new()
+ red:set('snapshots:tenant:test', 'abcd1234')
+ red:sadd('snapshots:abcd1234:resources:test:__index__', 'snapshots:abcd1234:resources:test:v1/test')
+ red:hset('snapshots:abcd1234:resources:test:v1/test', 'resources', '{"operations":{}}')
+ local ds = require 'lib/dataStore'
+ local dataStore = ds.initWithDriver(red)
+ dataStore:setSnapshotId('test')
+ local result = dataStore:getAllResources('test')[1]
+ assert.are.same(result, 'resources:test:v1/test')
+ local routing = require 'routing'
+ local result = routing.findResource(dataStore:getAllResources('test'), 'test', 'v1/test')
+ end)
end)
diff --git a/tests/scripts/lua/security.lua b/tests/scripts/lua/security.lua
index 239650d..58d3f4e 100644
--- a/tests/scripts/lua/security.lua
+++ b/tests/scripts/lua/security.lua
@@ -47,8 +47,12 @@ describe('API Key module', function()
"type":"apikey"
}
]])
- red:hset('resources:abcd:v1/test', 'resources', '{"apiId":"bnez"}')
- red:set('subscriptions:tenant:abcd:api:bnez:key:a1234', 'true')
+ red:set('snapshots:tenant:abcd', 'abcdefg')
+
+ red:hset('snapshots:abcdefg:resources:abcd:v1/test', 'resources', '{"apiId":"bnez"}')
+ red:set('snapshots:abcdefg:subscriptions:tenant:abcd:api:bnez:key:a1234', 'true')
+ local dataStore = ds.initWithDriver(red)
+ dataStore:setSnapshotId('abcd')
local key = apikey.process(dataStore, securityObj, function() return "fakehash" end)
assert.same(key, 'a1234')
end)
@@ -319,6 +323,62 @@ describe('Client Secret Module', function()
local result = clientSecret.processWithHashFunction(red, cjson.decode(securityObj), function() return "fakehash" end)
assert(result)
end)
+ it('Validates a client secret pair with default names and snapshotting', function()
+ local ngx = fakengx.new()
+ local red = fakeredis.new()
+ local ngxattrs = [[
+ {
+ "http_X_Client_ID":"abcd",
+ "http_X_Client_Secret":"1234",
+ "tenant":"1234",
+ "gatewayPath":"v1/test"
+ }
+ ]]
+ ngx.req = { get_uri_args = function() return {} end }
+ ngx.var = cjson.decode(ngxattrs)
+ _G.ngx = ngx
+ local securityObj = [[
+ {
+ "type":"clientsecret",
+ "scope":"resource"
+ }
+ ]]
+ red:set("snapshots:tenant:1234", "abcdefg")
+ red:set("snapshots:abcdefg:subscriptions:tenant:1234:resource:v1/test:clientsecret:abcd:fakehash", "true")
+ local ds = require 'lib/dataStore'
+ local dataStore = ds.initWithDriver(red)
+ dataStore:setSnapshotId("1234")
+ local result = clientSecret.processWithHashFunction(dataStore, cjson.decode(securityObj), function() return "fakehash" end)
+ assert(result)
+ end)
+ it('Doesn\'t validate a client secret pair in a different snapshot', function()
+ local ngx = fakengx.new()
+ local red = fakeredis.new()
+ local ngxattrs = [[
+ {
+ "http_X_Client_ID":"abcd",
+ "http_X_Client_Secret":"1234",
+ "tenant":"1234",
+ "gatewayPath":"v1/test"
+ }
+ ]]
+ ngx.req = { get_uri_args = function() return {} end }
+ ngx.var = cjson.decode(ngxattrs)
+ _G.ngx = ngx
+ local securityObj = [[
+ {
+ "type":"clientsecret",
+ "scope":"resource"
+ }
+ ]]
+ red:set("snapshots:tenant:1234", "abcdefg")
+ red:set("snapshots:abcdefh:subscriptions:tenant:1234:resource:v1/test:clientsecret:abcd:fakehash", "true")
+ local ds = require 'lib/dataStore'
+ local dataStore = ds.initWithDriver(red)
+ dataStore:setSnapshotId("1234")
+ local result = clientSecret.processWithHashFunction(dataStore, cjson.decode(securityObj), function() return "fakehash" end)
+ assert.falsy(result)
+ end)
it('Validates a client secret pair with new names', function()
local ngx = fakengx.new()
local red = fakeredis.new()
--
To stop receiving notification emails like this one, please contact
['"commits@openwhisk.apache.org" <co...@openwhisk.apache.org>'].