├── .gitignore ├── .luacheckrc ├── README.md ├── kong-plugin-sync-eureka-1.1.0-2.rockspec └── kong └── plugins └── sync-eureka ├── api.lua ├── handler.lua └── schema.lua /.gitignore: -------------------------------------------------------------------------------- 1 | servroot 2 | *.rock -------------------------------------------------------------------------------- /.luacheckrc: -------------------------------------------------------------------------------- 1 | -- Configuration file for LuaCheck 2 | -- see: https://luacheck.readthedocs.io/en/stable/ 3 | -- 4 | -- To run do: `luacheck .` from the repo 5 | 6 | std = "ngx_lua" 7 | unused_args = false 8 | redefined = false 9 | max_line_length = false 10 | 11 | 12 | globals = { 13 | "_KONG", 14 | "kong", 15 | "ngx.IS_CLI", 16 | } 17 | 18 | 19 | not_globals = { 20 | "string.len", 21 | "table.getn", 22 | } 23 | 24 | 25 | ignore = { 26 | "6.", -- ignore whitespace warnings 27 | } 28 | 29 | 30 | exclude_files = { 31 | --"spec/fixtures/invalid-module.lua", 32 | --"spec-old-api/fixtures/invalid-module.lua", 33 | } 34 | 35 | 36 | files["spec/**/*.lua"] = { 37 | std = "ngx_lua+busted", 38 | } 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | kong-plugin-sync-eureka 2 | --- 3 | 4 | Move to 5 | --- 6 | 7 | https://github.com/anjia0532/discovery-syncer 8 | 9 | Prerequisites 10 | --- 11 | - [Kong >=1.4.0](https://github.com/Kong/kong/releases/tag/1.4.0) 12 | - Eureka v1 endpoint( `curl -H "Accept:application/json" http://eureka:8761/eureka/apps` ) 13 | 14 | Quickstart 15 | --- 16 | 17 | ```bash 18 | $ luarocks install kong-plugin-sync-eureka 19 | 20 | $ export plugins=bundled,sync-eureka 21 | $ kong restart 22 | $ curl -H "Content-Type: application/json" -X POST --data '{"config":{"sync_interval":10,"eureka_url":"http://eureka:8761/eureka","clean_target_interval":86400},"name":"sync-eureka"}' http://127.0.0.1:8001/plugins 23 | $ curl -H "Content-Type: application/json" -X GET http://127.0.0.1:8001/plugins/ 24 | 25 | # wait and check 26 | 27 | $ curl -H "Content-Type: application/json" -X GET http://127.0.0.1:8001/services/ 28 | $ curl -H "Content-Type: application/json" -X GET http://127.0.0.1:8001/routes/ 29 | $ curl -H "Content-Type: application/json" -X GET http://127.0.0.1:8001/upstreams/ 30 | $ curl -H "Content-Type: application/json" -X GET http://127.0.0.1:8001/upstreams/{upstream host:port or id}/targets/ 31 | 32 | 33 | # since v1.1.0 34 | # /eureka/sync/ sync all apps 35 | # /eureka/sync/app_name sync one 36 | $ curl -H "Content-Type: application/json" -X POST http://127.0.0.1:8001/eureka/sync/[{app}] 37 | # clean invalid targets 38 | $ curl -H "Content-Type: application/json" -X POST http://127.0.0.1:8001/eureka/clean-targets 39 | 40 | ``` 41 | 42 | Note 43 | --- 44 | plugin config 45 | - sync_interval : Interval between sync applications from eureka server (in seconds) default 10 sec 46 | - eureka_url : eureka server url, e.g. http://127.0.0.1:8761/eureka 47 | - clean_target_interval : Interval between cleanup invalid upstream's target default 86400 sec (1 day) 48 | 49 | References 50 | --- 51 | 52 | [Plugin Development - (un)Installing your plugin](https://docs.konghq.com/1.4.x/plugin-development/distribution/) 53 | 54 | [Kong/kong-plugin](https://github.com/Kong/kong-plugin) 55 | 56 | [Kong/kong-vagrant](https://github.com/Kong/kong-vagrant) 57 | 58 | [微服务 API 网关 Kong 插件开发 - 安装/卸载插件](https://git.102no.com/2019/05/05/kong-plugin-distribution/) 59 | 60 | [quancheng-ec/eureka-kong-register](https://github.com/quancheng-ec/eureka-kong-register) 61 | 62 | [048-使用Kong替换Zuul(从Eureka同步列表)](https://juejin.im/post/5dd25fcff265da0bbe51093f) 63 | 64 | [049-Kong1.4 vs SC Gateway2.2 vs Zuul1.3 性能测试](https://juejin.im/post/5dd26053f265da0bbe510940) 65 | 66 | Copyright and License 67 | --- 68 | 69 | This module is licensed under the BSD license. 70 | 71 | Copyright (C) 2017-, by AnJia . 72 | 73 | All rights reserved. 74 | 75 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 76 | 77 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 78 | 79 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 80 | 81 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 82 | -------------------------------------------------------------------------------- /kong-plugin-sync-eureka-1.1.0-2.rockspec: -------------------------------------------------------------------------------- 1 | rockspec_format = "3.0" 2 | package = "kong-plugin-sync-eureka" 3 | 4 | version = "1.1.0-2" 5 | 6 | local pluginName = "sync-eureka" 7 | 8 | supported_platforms = {"linux", "macosx"} 9 | source = { 10 | url = "git://github.com/anjia0532/kong-plugin-sync-eureka", 11 | tag = "1.1.0" 12 | } 13 | 14 | description = { 15 | summary = "a plugin of kong to sync from eureka application instances register to kong server", 16 | detailed = [[ 17 | sync eureka application instances register to kong server 18 | ]], 19 | homepage = "https://github.com/anjia0532/kong-plugin-sync-eureka", 20 | license = "BSD", 21 | labels = { "kong", "kong-plugin", "openresty" , "eureka"} 22 | } 23 | 24 | dependencies = { 25 | } 26 | 27 | build = { 28 | type = "builtin", 29 | modules = { 30 | ["kong.plugins."..pluginName..".handler"] = "kong/plugins/"..pluginName.."/handler.lua", 31 | ["kong.plugins."..pluginName..".schema"] = "kong/plugins/"..pluginName.."/schema.lua", 32 | ["kong.plugins."..pluginName..".api"] = "kong/plugins/"..pluginName.."/api.lua", 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /kong/plugins/sync-eureka/api.lua: -------------------------------------------------------------------------------- 1 | local eureka = require "kong.plugins.sync-eureka.handler" 2 | local write = kong.response.exit 3 | return { 4 | ["/eureka/sync(/:app)"] = { 5 | POST = function(self) 6 | eureka.sync_job(self.params.app) 7 | return write(200, { 8 | message = "sync eureka " .. (self.params.app or "all") .. 9 | " now ..." 10 | }) 11 | end 12 | }, 13 | ["/eureka/clean-targets"] = { 14 | POST = function() 15 | eureka.cleanup_targets() 16 | return write(200, {message = "cleanup invalid targets ..."}) 17 | end 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /kong/plugins/sync-eureka/handler.lua: -------------------------------------------------------------------------------- 1 | local cjson = require "cjson" 2 | local http = require "resty.http" 3 | local singletons = require "kong.singletons" 4 | local ngx_re = require "ngx.re" 5 | 6 | local admin_host = nil 7 | local plugins = kong.db.plugins 8 | local kong_cache = ngx.shared.kong 9 | 10 | local METHOD_POST = "POST" 11 | local METHOD_DELETE = "DELETE" 12 | local METHOD_GET = "GET" 13 | local cache_exptime = 120 14 | local eureka_suffix = ".eureka.internal" 15 | local sync_eureka_plugin = {} 16 | local LOG_INFO = kong.log.info 17 | local LOG_DEBUG = kong.log.debug 18 | local LOG_ERROR = kong.log.err 19 | local LOG_WARN = kong.log.warn 20 | 21 | local SyncEurekaHandler = {} 22 | -- https://github.com/Netflix/eureka/wiki/Eureka-REST-operations 23 | local status_weitht = { 24 | ["UP"] = 100, 25 | ["DOWN"] = 1, 26 | ["STARTING"] = 0, 27 | ["OUT_OF_SERVICE"] = 0, 28 | ["UNKNOWN"] = 1 29 | } 30 | 31 | SyncEurekaHandler.PRIORITY = 1000 32 | SyncEurekaHandler.VERSION = "1.0.0" 33 | 34 | --- fetch eureka applications info 35 | local function eureka_apps(app_name) 36 | LOG_INFO("start fetch eureka apps [ ", app_name or "all", " ]") 37 | if not sync_eureka_plugin then 38 | return nil, 'failed to query plugin config' 39 | end 40 | local config = sync_eureka_plugin["enabled"] and 41 | sync_eureka_plugin["config"] or nil 42 | 43 | local httpc = http.new() 44 | local res, err = httpc:request_uri(config["eureka_url"] .. "/apps/" .. 45 | (app_name or ''), { 46 | method = METHOD_GET, 47 | headers = {["Accept"] = "application/json"}, 48 | keepalive_timeout = 60, 49 | keepalive_pool = 10 50 | }) 51 | 52 | if not res then 53 | LOG_ERROR("failed to fetch eureka apps request: ", err) 54 | return nil 55 | end 56 | local apps = cjson.decode(res.body) 57 | 58 | --[[ 59 | convert to app_list 60 | -- https://github.com/Netflix/eureka/wiki/Eureka-REST-operations 61 | { 62 | "demo":{ 63 | "192.168.0.10:8080"="UP", 64 | "health_path"="/health" 65 | } 66 | } 67 | ]] 68 | 69 | if app_name then 70 | apps = {["applications"] = {["application"] = {apps["application"]}}} 71 | end 72 | 73 | local app_list = {} 74 | for _, item in pairs(apps["applications"]["application"]) do 75 | local name = string.lower(item["name"]) 76 | app_list[name] = {} 77 | for _, it in pairs(item["instance"]) do 78 | local host, _ = ngx_re.split(it["homePageUrl"], "/") 79 | app_list[name][host[3]] = it['status'] 80 | app_list[name]["health_path"] = 81 | string.sub(it["healthCheckUrl"], string.len(it["homePageUrl"])) 82 | end 83 | end 84 | 85 | LOG_DEBUG("end to fetch eureka apps,total of ", #app_list, " apps") 86 | return app_list 87 | end 88 | 89 | --- get kong admin api listeners default is 127.0.0.1:8001 90 | local function get_admin_listen() 91 | for _, item in pairs(singletons.configuration.admin_listeners) do 92 | if not item['ssl'] then 93 | if "0.0.0.0" == item["ip"] then item["ip"] = "127.0.0.1" end 94 | return "http://" .. item["ip"] .. ":" .. item['port'] 95 | end 96 | end 97 | return nil 98 | end 99 | 100 | --- http client of kong admin api 101 | ---@param method string http method like GET,POST,PUT,DELETE ... 102 | ---@param path string uri of path like /test 103 | ---@param body table request body 104 | local function admin_client(method, path, body) 105 | if not admin_host then admin_host = get_admin_listen() end 106 | local httpc = http.new() 107 | 108 | local req = { 109 | method = method, 110 | headers = {["Content-Type"] = "application/json"}, 111 | keepalive_timeout = 60, 112 | keepalive_pool = 10 113 | } 114 | LOG_DEBUG("send admin request,uri:", path, ",method:", method, "body:", body) 115 | if METHOD_POST == method and body then req["body"] = cjson.encode(body) end 116 | return httpc:request_uri(admin_host .. path, req) 117 | end 118 | 119 | --- parse response 120 | ---@param type string type of operation 121 | ---@param resp table response of request 122 | ---@param err string error message 123 | ---@param cache_key string cache key 124 | local function parse_resp(type, resp, err, cache_key) 125 | -- created success status is 201 126 | -- query success status is 200 127 | -- 409 is conflict 128 | -- query routes and targets ,status is 200 ,may be data is empty list 129 | if resp and (resp.status <= 201 or resp.status == 409) then 130 | if resp.status == 200 then 131 | local data = cjson.decode(resp.body)["data"] 132 | if data and #data == 0 then return "ok", nil end 133 | end 134 | kong_cache:safe_set(cache_key, true, cache_exptime) 135 | return "ok", nil 136 | end 137 | if not resp or resp.status >= 400 then 138 | LOG_ERROR(resp.body) 139 | LOG_ERROR( 140 | "[" .. type .. "]failed to create kong request: http err msg:", err, 141 | ", http code:", resp.status, ", cache_key:", cache_key, 142 | ",response body:", resp.body) 143 | return nil, "failed to create kong " .. type 144 | end 145 | return "ok", nil 146 | end 147 | 148 | --- create service by app name 149 | ---@param name string app name 150 | local function create_service(name) 151 | local cache_key = "sync_eureka_apps:service:" .. name 152 | if kong_cache:get(cache_key) then return "ok", nil end 153 | 154 | LOG_DEBUG("[create service]:miss cache,we need to query this service:", name) 155 | local res, err = admin_client(METHOD_GET, "/services/" .. name, nil) 156 | LOG_ERROR("res", res, err) 157 | parse_resp("service", res, err, cache_key) 158 | if kong_cache:get(cache_key) then return "ok", nil end 159 | 160 | LOG_DEBUG("[create service]:new service,we need to create this service:", 161 | name) 162 | res, err = admin_client(METHOD_POST, "/services", 163 | {name = name, host = name .. eureka_suffix}) 164 | parse_resp("service", res, err, cache_key) 165 | end 166 | 167 | --- create route by app name 168 | ---@param name string app name 169 | local function create_route(name) 170 | local cache_key = "sync_eureka_apps:route:" .. name 171 | if kong_cache:get(cache_key) then return "ok", nil end 172 | 173 | LOG_DEBUG("[create route]:miss cache,we need to query this route:", name) 174 | local res, err = admin_client(METHOD_GET, "/routes/" .. name, nil) 175 | parse_resp("route", res, err, cache_key) 176 | if kong_cache:get(cache_key) then return "ok", nil end 177 | 178 | LOG_DEBUG("[create route]:new route,we need to create this route:", name) 179 | res, err = admin_client(METHOD_POST, "/services/" .. name .. "/routes", { 180 | name = name, 181 | protocols = {"http"}, 182 | paths = {"/" .. name} 183 | }) 184 | parse_resp("route", res, err, cache_key) 185 | end 186 | 187 | --- create upstream by appname 188 | ---@param name string app name 189 | ---@param item table object of eureka's instance 190 | local function create_upstream(name, item) 191 | 192 | local cache_key = "sync_eureka_apps:upstream:" .. name 193 | if kong_cache:get(cache_key) then return "ok", nil end 194 | 195 | LOG_DEBUG("[create upstream]:miss cache,we need to query this upstream:", 196 | name) 197 | local res, err = admin_client(METHOD_GET, 198 | "/upstreams/" .. name .. eureka_suffix, nil) 199 | parse_resp("upstream", res, err, cache_key) 200 | if kong_cache:get(cache_key) then return "ok", nil end 201 | 202 | LOG_DEBUG("[create upstream]:new route,we need to create this upstream:", 203 | name) 204 | res, err = admin_client(METHOD_POST, "/upstreams", { 205 | name = name .. eureka_suffix, 206 | healthchecks = { 207 | -- passive check 208 | passive = { 209 | unhealthy = { 210 | http_failures = 10, 211 | timeouts = 40, 212 | http_statuses = {503, 504} 213 | }, 214 | healthy = {successes = 3} 215 | } 216 | 217 | } 218 | }) 219 | parse_resp("upstream", res, err, cache_key) 220 | end 221 | 222 | --- get targets by upstream name 223 | ---@param name string upstream name 224 | ---@param target_next string url of next targets page 225 | local function get_targets(name, target_next) 226 | 227 | local res, err = admin_client(METHOD_GET, target_next, nil) 228 | local targets = {} 229 | if res and res.status == 200 then 230 | local target_resp = cjson.decode(res.body) 231 | LOG_DEBUG("[get targets]:path:", target_next, " ,length:", 232 | #target_resp["data"]) 233 | for _, item in pairs(target_resp["data"]) do 234 | local kong_cache_key = "sync_eureka_apps:target:" .. name .. ":" .. 235 | item["target"] 236 | targets[item["target"]] = item["weight"] 237 | kong_cache:safe_set(kong_cache_key, true, cache_exptime * 30) 238 | end 239 | if ngx.null ~= target_resp["next"] then 240 | LOG_DEBUG("[get targets]: next page, path:", target_resp["next"], 241 | " ,this page size:", #target_resp["data"]) 242 | local next_targets = get_targets(name, target_resp["next"]) 243 | for key, value in pairs(next_targets) do 244 | targets[key] = value 245 | end 246 | end 247 | else 248 | return nil, err 249 | end 250 | return targets, nil 251 | end 252 | 253 | ---delete unhealthy instance target 254 | ---@param name string app name 255 | ---@param target string app host:port 256 | local function delete_target(name, target) 257 | 258 | local cache_key = "sync_eureka_apps:target:" .. name .. ":" .. target 259 | 260 | LOG_WARN("[delete target]: upstream name :", name .. eureka_suffix, 261 | " ,target:", target) 262 | kong_cache:safe_set(cache_key, nil) 263 | 264 | admin_client(METHOD_DELETE, "/upstreams/" .. name .. eureka_suffix .. 265 | "/targets/" .. target, nil) 266 | 267 | end 268 | 269 | --- add target to upstream 270 | ---@param name string app name 271 | ---@param target string app host:port 272 | ---@param weight integer 0-1000 default 100 273 | ---@param tags table tags 274 | local function put_target(name, target, weight, tags) 275 | 276 | -- get_targets use(fetch targets) 277 | local cache_key = "sync_eureka_apps:target:" .. name .. ":" .. target 278 | 279 | local targets = get_targets(name, "/upstreams/" .. name .. eureka_suffix .. 280 | "/targets") 281 | if not targets then return nil, "targets is nil" end 282 | weight = weight or 0 283 | local kong_weight = targets[target] or 0 284 | 285 | if weight ~= kong_weight then 286 | -- kong not have this target 287 | if kong_weight ~= 0 then delete_target(name, target) end 288 | -- weight == 0 means starting and out_of_service 289 | -- kong_weight == 0 means this target not in kong upstream 290 | -- weight == 100 means eureka status is up 291 | if weight == 0 or (kong_weight == 0 and weight ~= 100) then 292 | return "ok", nil 293 | end 294 | else 295 | return "ok", nil 296 | end 297 | 298 | LOG_DEBUG("[add target]: name:", name, " ,target:", target) 299 | local res, err = admin_client(METHOD_POST, "/upstreams/" .. name .. 300 | eureka_suffix .. "/targets", { 301 | target = target, 302 | weight = weight or 1, 303 | tags = tags or {} 304 | }) 305 | parse_resp("target", res, err, cache_key) 306 | end 307 | 308 | --- fetch kong's upstream 309 | ---@param upstream_next string url of next upstream page 310 | local function kong_upstreams(upstream_next) 311 | 312 | LOG_DEBUG("[fetch kong's upstream]: path:", upstream_next or "/upstreams") 313 | local res, err = 314 | admin_client(METHOD_GET, upstream_next or "/upstreams", nil) 315 | if not res or res.status ~= 200 then 316 | return nil, "failed to fetch kong's upstreams" .. err 317 | end 318 | local ups = cjson.decode(res.body) 319 | local upstreams = {} 320 | for _, item in pairs(ups["data"]) do 321 | if string.sub(item["name"], -string.len(eureka_suffix)) == eureka_suffix then 322 | upstreams[item["name"]] = string.sub(item["name"], 1, 323 | #item["name"] - #eureka_suffix) 324 | end 325 | end 326 | if ngx.null ~= ups["next"] then 327 | local next_ups = kong_upstreams(ups["next"]) 328 | for key, value in pairs(next_ups) do upstreams[key] = value end 329 | end 330 | 331 | LOG_DEBUG("[get kong's upstreams]: next page, path:", ups["next"], 332 | " ,this page size:", #ups["data"]) 333 | return upstreams 334 | end 335 | 336 | --- cron job to cleanup invalid targets 337 | SyncEurekaHandler.cleanup_targets = function() 338 | LOG_DEBUG("cron job to cleanup invalid targets") 339 | sync_eureka_plugin = plugins:select_by_cache_key("plugins:sync-eureka::::") 340 | if not sync_eureka_plugin then return end 341 | local app_list = eureka_apps() 342 | local upstreams = kong_upstreams() or {} 343 | for up_name, name in pairs(upstreams) do 344 | local targets = 345 | get_targets(name, "/upstreams/" .. up_name .. "/targets") or {} 346 | -- delete all targets by this upstream name 347 | if not app_list[name] then 348 | for target, _ in pairs(targets) do 349 | delete_target(name, target) 350 | end 351 | else 352 | for target, _ in pairs(targets) do 353 | -- delete this target 354 | if app_list[name][target] ~= "UP" then 355 | delete_target(name, target) 356 | end 357 | end 358 | end 359 | end 360 | end 361 | 362 | --- cron job to fetch apps from eureka server 363 | SyncEurekaHandler.sync_job = function(app_name) 364 | LOG_INFO("cron job to fetch apps from eureka server [ ", app_name or "all", 365 | " ]") 366 | sync_eureka_plugin = plugins:select_by_cache_key("plugins:sync-eureka::::") 367 | if not sync_eureka_plugin then return end 368 | 369 | local cache_app_list = kong_cache:get("sync_eureka_apps") or "{}" 370 | cache_app_list = cjson.decode(cache_app_list) 371 | local app_list = eureka_apps(app_name) 372 | for name, item in pairs(app_list) do 373 | if not cache_app_list[name] then 374 | create_service(name) 375 | create_route(name) 376 | create_upstream(name) 377 | end 378 | 379 | cache_app_list[name] = true 380 | for target, status in pairs(item) do 381 | if target ~= "health_path" then 382 | put_target(name, target, status_weitht[status], {status}) 383 | end 384 | end 385 | end 386 | kong_cache:safe_set("sync_eureka_apps", cjson.encode(cache_app_list), 387 | cache_exptime) 388 | end 389 | 390 | --- init worker 391 | function SyncEurekaHandler:init_worker() 392 | if 0 ~= ngx.worker.id() then return end 393 | 394 | sync_eureka_plugin = plugins:select_by_cache_key("plugins:sync-eureka::::") 395 | LOG_INFO("init worker,load sync_eureka_plugin:", 396 | cjson.encode(sync_eureka_plugin)) 397 | 398 | if sync_eureka_plugin and sync_eureka_plugin["enabled"] then 399 | local ok, err = ngx.timer.every( 400 | sync_eureka_plugin["config"]["sync_interval"], 401 | SyncEurekaHandler.sync_job) 402 | if not ok then 403 | LOG_ERROR("failed to create the timer: ", err) 404 | return 405 | end 406 | local ok, err = ngx.timer.every( 407 | sync_eureka_plugin["config"]["clean_target_interval"], 408 | SyncEurekaHandler.cleanup_targets) 409 | if not ok then 410 | LOG_ERROR("failed to create the timer: ", err) 411 | return 412 | end 413 | end 414 | end 415 | 416 | return SyncEurekaHandler 417 | -------------------------------------------------------------------------------- /kong/plugins/sync-eureka/schema.lua: -------------------------------------------------------------------------------- 1 | return { 2 | name = "sync-eureka", 3 | fields = { 4 | { 5 | config = { 6 | type = "record", 7 | fields = { 8 | {eureka_url = {type = "string"}}, 9 | {sync_interval = {type = "number", default = 30}}, 10 | {clean_target_interval = {type = "number", default = 86400}} 11 | } 12 | } 13 | } 14 | } 15 | } 16 | --------------------------------------------------------------------------------