Merge pull request #25 from alexsong93/redis-sync

Redis sync fix
diff --git a/Dockerfile b/Dockerfile
index 69f9723..81fb111 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -8,10 +8,9 @@
 
 # install dependencies
 RUN apk update \
-    && apk add gcc tar libtool zlib jemalloc jemalloc-dev perl \ 
+    && apk add gcc tar libtool zlib jemalloc jemalloc-dev perl \
     make musl-dev openssl-dev pcre-dev g++ zlib-dev curl python \
-    perl-test-longstring perl-list-moreutils perl-http-message \
-    geoip-dev nodejs
+    perl-test-longstring perl-list-moreutils perl-http-message geoip-dev
 
 # openresty build
 ENV OPENRESTY_VERSION=1.9.7.3 \
diff --git a/api-gateway-config/conf.d/management_apis.conf b/api-gateway-config/conf.d/management_apis.conf
index eccdcb1..067eb95 100644
--- a/api-gateway-config/conf.d/management_apis.conf
+++ b/api-gateway-config/conf.d/management_apis.conf
@@ -83,21 +83,31 @@
                  ngx.status = 400
                  ngx.say("Invalid verb")
              end
-
          }
-     }
+    }
 
-    location /subscribe {
+    location /v1/sync {
         access_by_lua_block {
             local mgmt = require("management")
-            mgmt.subscribe()
+            local requestMethod = ngx.req.get_method()
+            if requestMethod == "GET" then
+                mgmt.sync()
+            else
+                ngx.say("Invalid verb")
+            end
         }
     }
 
-    location /unsubscribe {
+    location /v1/subscribe {
         access_by_lua_block {
             local mgmt = require("management")
-            mgmt.unsubscribe()
+            local requestMethod = ngx.req.get_method()
+            if requestMethod == "GET" then
+                mgmt.subscribe()
+            else
+                ngx.say("Invalid verb")
+            end
         }
     }
+
 }
diff --git a/api-gateway-config/scripts/lua/lib/filemgmt.lua b/api-gateway-config/scripts/lua/lib/filemgmt.lua
index ca1d793..f1a6e1c 100644
--- a/api-gateway-config/scripts/lua/lib/filemgmt.lua
+++ b/api-gateway-config/scripts/lua/lib/filemgmt.lua
@@ -73,8 +73,6 @@
   })
   file:write(location)
   file:close()
-  -- reload nginx to refresh conf files
-  os.execute("/usr/local/sbin/nginx -s reload")
   return fileLocation
 end
 
@@ -86,8 +84,6 @@
 function _M.deleteResourceConf(baseConfDir, tenant, gatewayPath)
   local fileLocation = utils.concatStrings({baseConfDir, tenant, "/", gatewayPath, ".conf"})
   os.execute(utils.concatStrings({"rm -f ", fileLocation}))
-  -- reload nginx to refresh conf files
-  os.execute("/usr/local/sbin/nginx -s reload")
   return fileLocation
 end
 
diff --git a/api-gateway-config/scripts/lua/lib/logger.lua b/api-gateway-config/scripts/lua/lib/logger.lua
index c1e5680..a5fd418 100644
--- a/api-gateway-config/scripts/lua/lib/logger.lua
+++ b/api-gateway-config/scripts/lua/lib/logger.lua
@@ -21,6 +21,7 @@
 --- @module logger
 -- Module to handle logging in a single place
 -- @author Cody Walker (cmwalker), Alex Song (songs)
+local utils = require "lib/utils"
 
 local _M = {}
 
@@ -33,6 +34,13 @@
 --- Handle debug stream to stdout
 -- @param s String to write to debug stream
 function _M.debug(s)
+  if s == nil then
+    s = "nil"
+  elseif type(s) == "table" then
+    s = utils.serializeTable(s)
+  elseif type(s) == "boolean" then
+    s = (s == true) and "true" or "false"
+  end
   os.execute("echo \"" .. s .. "\"")
 end
 
diff --git a/api-gateway-config/scripts/lua/lib/redis.lua b/api-gateway-config/scripts/lua/lib/redis.lua
index 62b2322..25b5a8e 100644
--- a/api-gateway-config/scripts/lua/lib/redis.lua
+++ b/api-gateway-config/scripts/lua/lib/redis.lua
@@ -60,13 +60,13 @@
     connect, err = red:connect(host, port)
   end
   if not connect then
-    request.err(500, utils.concatStrings({"Failed to connect to redis: ", err}))  
+    request.err(500, utils.concatStrings({"Failed to connect to redis: ", err}))
   end
   -- Authenticate with Redis
   if password ~= nil and password ~= "" then
     local res, err = red:auth(password)
     if not res then
-      request.err(500, utils.concatStrings({"Failed to authenticate: ", err}))  
+      request.err(500, utils.concatStrings({"Failed to authenticate: ", err}))
     end
   end
   return red
@@ -90,11 +90,35 @@
 -- @param red Redis client instance
 -- @param id id of API
 -- @param apiObj the api to add
-function _M.addAPI(red, id, apiObj)
+-- @param existingAPI existing api to update
+function _M.addAPI(red, id, apiObj, existingAPI)
+  if existingAPI == nil then
+    local apis = _M.getAllAPIs(red)
+    -- Return error if api with basepath already exists
+    for apiId, obj in pairs(apis) do
+      if apiId%2 == 0 then
+        obj = cjson.decode(obj)
+        if obj.tenantId == apiObj.tenantId and obj.basePath == apiObj.basePath then
+          request.err(500, "basePath not unique for given tenant.")
+        end
+      end
+    end
+  else
+    -- Delete all resources for the existingAPI
+    local basePath = existingAPI.basePath:sub(2)
+    for path, v in pairs(existingAPI.resources) do
+      local gatewayPath = utils.concatStrings({basePath, ngx.escape_uri(path)})
+      local redisKey = utils.concatStrings({"resources:", existingAPI.tenantId, ":", ngx.unescape_uri(gatewayPath)})
+      _M.deleteResource(red, redisKey, REDIS_FIELD)
+    end
+  end
+  -- Add new API
+  apiObj = cjson.encode(apiObj):gsub("\\", "")
   local ok, err = red:hset("apis", id, apiObj)
   if not ok then
     request.err(500, utils.concatStrings({"Failed to save the API: ", err}))
   end
+  return apiObj
 end
 
 --- Get all APIs from redis
@@ -168,7 +192,7 @@
 -- @param resourceObj redis object containing operations for resource
 function _M.createResource(red, key, field, resourceObj)
   -- Add/update resource to redis
-  local ok, err = red:hset(key, field, resourceObj)
+  ok, err = red:hset(key, field, resourceObj)
   if not ok then
     request.err(500, utils.concatStrings({"Failed to save the resource: ", err}))
   end
@@ -188,10 +212,31 @@
   if resourceObj == ngx.null then
     return nil
   end
-
   return resourceObj
 end
 
+--- Get all resource keys in redis
+-- @param red redis client instance
+function getAllResourceKeys(red)
+  -- Find all resourceKeys in redis
+  local resources, err = red:scan(0, "match", "resources:*:*")
+  if not resources then
+    request.err(500, util.concatStrings({"Failed to retrieve resource keys: ", err}))
+  end
+  local cursor = resources[1]
+  local resourceKeys = resources[2]
+  while cursor ~= "0" do
+    resources, err = red:scan(cursor, "match", "resources:*:*")
+    if not resources then
+      request.err(500, util.concatStrings({"Failed to retrieve resource keys: ", err}))
+    end
+    cursor = resources[1]
+    for k, v in pairs(resources[2]) do
+      resourceKeys[#resourceKeys + 1] = v
+    end
+  end
+  return resourceKeys
+end
 --- Delete resource in redis
 -- @param red redis client instance
 -- @param key redis resource key
@@ -204,6 +249,7 @@
   if resourceObj == ngx.null then
     request.err(404, "Resource doesn't exist.")
   end
+  -- Delete redis resource
   local ok, err = red:del(key)
   if not ok then
     request.err(500, utils.concatStrings({"Failed to delete the resource: ", err}))
@@ -221,10 +267,23 @@
 -- @param id id of tenant
 -- @param tenantObj the tenant to add
 function _M.addTenant(red, id, tenantObj)
+  local tenants = _M.getAllTenants(red)
+  -- Return tenant from redis if it already exists
+  for tenantId, obj in pairs(tenants) do
+    if tenantId%2 == 0 then
+      obj = cjson.decode(obj)
+      if obj.namespace == tenantObj.namespace and obj.instance == tenantObj.instance then
+        return cjson.encode(obj)
+      end
+    end
+  end
+  -- Add new tenant
+  tenantObj = cjson.encode(tenantObj)
   local ok, err = red:hset("tenants", id, tenantObj)
   if not ok then
     request.err(500, utils.concatStrings({"Failed to add the tenant: ", err}))
   end
+  return tenantObj
 end
 
 --- Get all tenants from redis
@@ -297,79 +356,38 @@
 ------- Pub/Sub with Redis --------
 -----------------------------------
 
+--- Sync with redis on startup and create conf files for resources that are already in redis
+-- @param red redis client instance
+function _M.syncWithRedis(red)
+  logger.debug("Sync with redis in progress...")
+  local resourceKeys = getAllResourceKeys(red)
+  for k, resourceKey in pairs(resourceKeys) do
+    local prefix, tenant, gatewayPath = resourceKey:match("([^,]+):([^,]+):([^,]+)")
+    local resourceObj = _M.getResource(red, resourceKey, REDIS_FIELD)
+    filemgmt.createResourceConf(BASE_CONF_DIR, tenant, ngx.escape_uri(gatewayPath), resourceObj)
+  end
+  os.execute("/usr/local/sbin/nginx -s reload")
+  logger.debug("All resources synced.")
+end
+
 --- Subscribe to redis
 -- @param redisSubClient the redis client that is listening for the redis key changes
 -- @param redisGetClient the redis client that gets the changed resource to update the conf file
 function _M.subscribe(redisSubClient, redisGetClient)
-  -- create conf files for existing resources in redis
-  syncWithRedis(redisGetClient, ngx)
-  -- enable keyspace notifications
-  local ok, err = redisGetClient:config("set", "notify-keyspace-events", "KEA")
+  logger.debug("Subscribed to redis and listening for key changes...")
+  local ok, err = redisSubClient:config("set", "notify-keyspace-events", "KEA")
   if not ok then
-    request.err(500, utils.concatStrings({"Failed setting notify-keyspace-events: ", err}))
+    request.err(500, utils.concatStrings({"Failed to subscribe to redis: ", err}))
   end
   ok, err = redisSubClient:psubscribe("__keyspace@0__:resources:*:*")
   if not ok then
     request.err(500, utils.concatStrings({"Failed to subscribe to redis: ", err}))
   end
-  ngx.say("\nSubscribed to redis and listening for key changes...")
-  ngx.flush(true)
-  subscribe(redisSubClient, redisGetClient, ngx)
-  ngx.exit(ngx.status)
-end
-
---- Sync with redis on startup and create conf files for resources that are already in redis
--- @param red redis client instance
-function syncWithRedis(red)
-  logger.debug("\nCreating nginx conf files for existing resources...")
-  local redisKeys, err = red:keys("*")
-  if not redisKeys then
-    request.err(500, util.concatStrings({"Failed to sync with Redis: ", err}))
-  end
-  -- Find all redis keys with "resources:*:*"
-  local resourcesExist = false
-  for k, redisKey in pairs(redisKeys) do
-    local index = 1
-    local tenant = ""
-    local gatewayPath = ""
-    for word in string.gmatch(redisKey, '([^:]+)') do
-      if index == 1 then
-        if word ~= "resources" then
-          break
-        else
-          resourcesExist = true
-          index = index + 1
-        end
-      else
-        if index == 2 then
-          tenant = word
-        elseif index == 3 then
-          gatewayPath = word
-          -- Create new conf file
-          local resourceObj = _M.getResource(red, redisKey, REDIS_FIELD)
-          local fileLocation = filemgmt.createResourceConf(BASE_CONF_DIR, tenant, ngx.escape_uri(gatewayPath), resourceObj)
-          logger.debug(utils.concatStrings({"Updated file: ", fileLocation}))
-        end
-        index = index + 1
-      end
-    end
-  end
-  if resourcesExist == false then
-    logger.debug("No existing resources.")
-  end
-end
-
---- Subscribe helper method
--- Starts a while loop that listens for key changes in redis
--- @param redisSubClient the redis client that is listening for the redis key changes
--- @param redisGetClient the redis client that gets the changed resource to update the conf file
-function subscribe(redisSubClient, redisGetClient)
   while true do
     local res, err = redisSubClient:read_reply()
     if not res then
       if err ~= "timeout" then
-        ngx.say("Read reply error: ", err)
-        ngx.exit(ngx.status)
+        request.err(500, utils.concatStrings({"Failed to read from redis: ", err}))
       end
     else
       local index = 1
@@ -391,29 +409,17 @@
       local resourceObj = _M.getResource(redisGetClient, redisKey, REDIS_FIELD)
       if resourceObj == nil then
         local fileLocation = filemgmt.deleteResourceConf(BASE_CONF_DIR, tenant, ngx.escape_uri(gatewayPath))
+        os.execute("/usr/local/sbin/nginx -s reload")
         logger.debug(utils.concatStrings({"Redis key deleted: ", redisKey}))
         logger.debug(utils.concatStrings({"Deleted file: ", fileLocation}))
       else
         local fileLocation = filemgmt.createResourceConf(BASE_CONF_DIR, tenant, ngx.escape_uri(gatewayPath), resourceObj)
+        os.execute("/usr/local/sbin/nginx -s reload")
         logger.debug(utils.concatStrings({"Redis key updated: ", redisKey}))
         logger.debug(utils.concatStrings({"Updated file: ", fileLocation}))
       end
     end
   end
-  ngx.exit(ngx.status)
-end
-
-
---- Unsubscribe from redis
--- @param red redis client instance
-function _M.unsubscribe(red)
-  local ok, err = red:unsubscribe("__keyspace@0__:resources:*:*")
-  if not ok then
-    request.err(500, utils.concatStrings({"Failed to unsubscribe to redis: ", err}))
-  end
-  _M.close(red, ngx)
-  ngx.say("Unsubscribed from redis")
-  ngx.exit(ngx.status)
 end
 
 return _M
\ No newline at end of file
diff --git a/api-gateway-config/scripts/lua/lib/request.lua b/api-gateway-config/scripts/lua/lib/request.lua
index 0ef5999..9a61a3e 100644
--- a/api-gateway-config/scripts/lua/lib/request.lua
+++ b/api-gateway-config/scripts/lua/lib/request.lua
@@ -31,7 +31,7 @@
 -- @param msg error message
 function err(code, msg)
   ngx.status = code
-  ngx.print(utils.concatStrings({"Error: ", msg}))
+  ngx.say(utils.concatStrings({"Error: ", msg}))
   ngx.exit(ngx.status)
 end
 
@@ -40,7 +40,7 @@
 -- @param obj object to return
 function success(code, obj)
   ngx.status = code
-  ngx.print(obj)
+  ngx.say(obj)
   ngx.exit(ngx.status)
 end
 
diff --git a/api-gateway-config/scripts/lua/management.lua b/api-gateway-config/scripts/lua/management.lua
index a09ff51..9f9ea48 100644
--- a/api-gateway-config/scripts/lua/management.lua
+++ b/api-gateway-config/scripts/lua/management.lua
@@ -24,7 +24,6 @@
 
 local cjson = require "cjson"
 local redis = require "lib/redis"
-local filemgmt = require "lib/filemgmt"
 local utils = require "lib/utils"
 local logger = require "lib/logger"
 local request = require "lib/request"
@@ -56,22 +55,9 @@
 function _M.addAPI()
   -- Open connection to redis or use one from connection pool
   local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
-  -- Check for api id and use existingAPI if it already exists in redis
+  -- Check for api id from uri and use existingAPI if it already exists in redis
   local uri = string.gsub(ngx.var.request_uri, "?.*", "")
-  local apiId, existingAPI
-  local index = 1
-  for word in string.gmatch(uri, '([^/]+)') do
-    if index == 3 then
-      apiId = word
-    end
-    index = index + 1
-  end
-  if apiId ~= nil then
-    existingAPI = redis.getAPI(red, apiId)
-    if existingAPI == nil then
-      request.err(404, utils.concatStrings({"Unknown API id ", apiId}))
-    end
-  end
+  local existingAPI = checkURIForExisting(red, uri, "api")
   -- Read in the PUT JSON Body
   ngx.req.read_body()
   local args = ngx.req.get_body_data()
@@ -80,6 +66,13 @@
   end
   -- Convert json into Lua table
   local decoded = cjson.decode(args)
+  -- Check for api id in JSON body
+  if existingAPI == nil and decoded.id ~= nil then
+    existingAPI = redis.getAPI(red, decoded.id)
+    if existingAPI == nil then
+      request.err(404, utils.concatStrings({"Unknown API id ", decoded.id}))
+    end
+  end
   -- Error checking
   local fields = {"name", "basePath", "tenantId", "resources"}
   for k, v in pairs(fields) do
@@ -90,12 +83,7 @@
   end
   -- Format basePath
   local basePath = decoded.basePath:sub(1,1) == '/' and decoded.basePath:sub(2) or decoded.basePath
-  -- Add resources to redis and create nginx conf files
-  for path, resource in pairs(decoded.resources) do
-    local gatewayPath = utils.concatStrings({basePath, ngx.escape_uri(path)})
-    addResource(red, resource, gatewayPath, decoded.tenantId)
-  end
-  -- Return managedUrl object
+  -- Create managedUrl object
   local uuid = existingAPI ~= nil and existingAPI.id or utils.uuid()
   local managedUrl = utils.concatStrings({"http://", MANAGEDURL_HOST, ":", MANAGEDURL_PORT, "/api/", decoded.tenantId})
   if basePath:sub(1,1) ~= '' then
@@ -109,9 +97,13 @@
     resources = decoded.resources,
     managedUrl = managedUrl
   }
-  managedUrlObj = cjson.encode(managedUrlObj):gsub("\\", "")
   -- Add API object to redis
-  redis.addAPI(red, uuid, managedUrlObj)
+  managedUrlObj = redis.addAPI(red, uuid, managedUrlObj, existingAPI)
+  -- Add resources to redis
+  for path, resource in pairs(decoded.resources) do
+    local gatewayPath = utils.concatStrings({basePath, ngx.escape_uri(path)})
+    addResource(red, resource, gatewayPath, decoded.tenantId)
+  end
   redis.close(red)
   -- Return managed url object
   ngx.header.content_type = "application/json; charset=utf-8"
@@ -206,7 +198,6 @@
   end
   local resourceObj = redis.generateResourceObj(operations, apiId)
   redis.createResource(red, redisKey, REDIS_FIELD, resourceObj)
-  filemgmt.createResourceConf(BASE_CONF_DIR, tenantId, gatewayPath, resourceObj)
 end
 
 --- Get one or all APIs from the gateway
@@ -306,13 +297,14 @@
   if api == nil then
     request.err(404, utils.concatStrings({"Unknown API id ", id}))
   end
+  -- Delete API
+  redis.deleteAPI(red, id)
   -- Delete all resources for the API
   local basePath = api.basePath:sub(2)
   for path, v in pairs(api.resources) do
     local gatewayPath = utils.concatStrings({basePath, ngx.escape_uri(path)})
     deleteResource(red, gatewayPath, api.tenantId)
   end
-  redis.deleteAPI(red, id)
   redis.close(red)
   request.success(200, {})
 end
@@ -324,7 +316,6 @@
 function deleteResource(red, gatewayPath, tenantId)
   local redisKey = utils.concatStrings({"resources:", tenantId, ":", ngx.unescape_uri(gatewayPath)})
   redis.deleteResource(red, redisKey, REDIS_FIELD)
-  filemgmt.deleteResourceConf(BASE_CONF_DIR, tenantId, gatewayPath)
 end
 
 -----------------------------
@@ -343,20 +334,7 @@
   local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
   -- Check for tenant id and use existingTenant if it already exists in redis
   local uri = string.gsub(ngx.var.request_uri, "?.*", "")
-  local tenantId, existingTenant
-  local index = 1
-  for word in string.gmatch(uri, '([^/]+)') do
-    if index == 3 then
-      tenantId = word
-    end
-    index = index + 1
-  end
-  if tenantId ~= nil then
-    existingTenant = redis.getTenant(red, tenantId)
-    if existingTenant == nil then
-      request.err(400, utils.concatStrings({"Unknown tenant id ", tenantId}))
-    end
-  end
+  local existingTenant = checkURIForExisting(red, uri, "tenant")
   -- Read in the PUT JSON Body
   ngx.req.read_body()
   local args = ngx.req.get_body_data()
@@ -365,6 +343,13 @@
   end
   -- Convert json into Lua table
   local decoded = cjson.decode(args)
+  -- Check for tenant id in JSON body
+  if existingTenant == nil and decoded.id ~= nil then
+    existingTenant = redis.getTenant(red, decoded.id)
+    if existingTenant == nil then
+      request.err(404, utils.concatStrings({"Unknown Tenant id ", decoded.id}))
+    end
+  end
   -- Error checking
   local fields = {"namespace", "instance"}
   for k, v in pairs(fields) do
@@ -379,8 +364,7 @@
     namespace = decoded.namespace,
     instance = decoded.instance
   }
-  tenantObj = cjson.encode(tenantObj)
-  redis.addTenant(red, uuid, tenantObj)
+  tenantObj = redis.addTenant(red, uuid, tenantObj)
   redis.close(red)
   ngx.header.content_type = "application/json; charset=utf-8"
   request.success(200, tenantObj)
@@ -490,24 +474,22 @@
 ----- Pub/Sub with Redis -----
 ------------------------------
 
---- Subscribe to redis
--- GET /subscribe
-function _M.subscribe()
-  -- Initialize and connect to redis
-  local redisGetClient = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
-  local redisSubClient = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 60000) -- read_reply will timeout every minute
-  logger.debug(utils.concatStrings({"\nConnected to redis at ", REDIS_HOST, ":", REDIS_PORT}))
-  redis.subscribe(redisSubClient, redisGetClient)
+--- Sync with redis
+-- GET /v1/sync
+function _M.sync()
+  local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 10000)
+  logger.debug(utils.concatStrings({"Connected to redis at ", REDIS_HOST, ":", REDIS_PORT}))
+  redis.syncWithRedis(red)
   ngx.exit(200)
 end
 
---- Unsusbscribe to redis
--- GET /unsubscribe
-function _M.unsubscribe()
-  -- Initialize and connect to redis
-  local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
-  redis.unsubscribe(red)
-  request.success(200, "Unsubscribed to redis")
+--- Subscribe to redis
+-- GET /v1/subscribe
+function _M.subscribe()
+  local redisGetClient = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
+  local redisSubClient = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
+  redis.subscribe(redisSubClient, redisGetClient)
+  ngx.exit(200)
 end
 
 ---------------------------
@@ -606,6 +588,38 @@
   return redisKey
 end
 
+
+--- Check for api id from uri and use existingAPI if it already exists in redis
+-- @param red Redis client instance
+-- @param uri Uri of request. Eg. /v1/apis/{id}
+-- @param type type to look for in redis. "api" or "tenant"
+function checkURIForExisting(red, uri, type)
+  local id, existing
+  local index = 1
+  -- Check if id is in the uri
+  for word in string.gmatch(uri, '([^/]+)') do
+    if index == 3 then
+      id = word
+    end
+    index = index + 1
+  end
+  -- Get object from redis
+  if id ~= nil then
+    if type == "api" then
+      existing = redis.getAPI(red, id)
+      if existing == nil then
+        request.err(404, utils.concatStrings({"Unknown API id ", id}))
+      end
+    elseif type == "tenant" then
+      existing = redis.getTenant(red, id)
+      if existing == nil then
+        request.err(404, utils.concatStrings({"Unknown Tenant id ", id}))
+      end
+    end
+  end
+  return existing
+end
+
 --- Parse the request uri to get the redisKey, tenant, and gatewayPath
 -- @param requestURI String containing the uri in the form of "/resources/<tenant>/<path>"
 -- @return list containing redisKey, tenant, gatewayPath
diff --git a/api-gateway-config/tests/run-tests.sh b/api-gateway-config/tests/run-tests.sh
index 6e137da..1eaa926 100755
--- a/api-gateway-config/tests/run-tests.sh
+++ b/api-gateway-config/tests/run-tests.sh
@@ -1,4 +1,4 @@
 #!/bin/sh
 
 # Run unit tests
-busted -c --output=TAP --helper=set_paths spec/test.lua
\ No newline at end of file
+busted --output=TAP --helper=set_paths spec/test.lua
\ No newline at end of file
diff --git a/api-gateway-config/tests/spec/test.lua b/api-gateway-config/tests/spec/test.lua
index 89e7052..5547e61 100644
--- a/api-gateway-config/tests/spec/test.lua
+++ b/api-gateway-config/tests/spec/test.lua
@@ -44,16 +44,16 @@
     local code = 500
     local msg = 'Internal server error\n'
     request.err(code, msg)
-    assert.are.equal(ngx._body, 'Error: ' .. msg)
-    assert.are.equal(ngx._exit, code)
+    assert.are.equal('Error: ' .. msg .. '\n', ngx._body)
+    assert.are.equal(code, ngx._exit)
   end)
 
   it('should return correct success response', function()
     local code = 200
     local msg ='Success!\n'
     request.success(code, msg)
-    assert.are.equal(ngx._body, msg)
-    assert.are.equal(ngx._exit, code)
+    assert.are.equal(msg .. '\n', ngx._body)
+    assert.are.equal(code, ngx._exit)
   end)
 end)
 
diff --git a/init.sh b/init.sh
index b81d4d1..a553b75 100755
--- a/init.sh
+++ b/init.sh
@@ -53,7 +53,8 @@
 
 if [[ -n "${redis_host}" && -n "${redis_port}" ]]; then
     sleep 1  # sleep until api-gateway is set up
-    curl -s http://0.0.0.0:9000/subscribe # subscribe to redis key changes for routes
+    curl -s http://0.0.0.0:9000/v1/sync & # sync with redis
+    curl -s http://0.0.0.0:9000/v1/subscribe # subscribe to redis key changes for routes
 else
     echo "REDIS_HOST and/or REDIS_PORT not defined"
 fi