ingress-nginx-helm/controllers/nginx-third-party/lua/ingress.lua
Prashanth Balasubramanian 3da4e74e5a git mv Ingress ingress
2016-02-26 16:55:24 -08:00

229 lines
5.6 KiB
Lua

local _M = {}
local cjson = require "cjson"
local trie = require "trie"
local http = require "resty.http"
local cache = require "resty.dns.cache"
local os = require "os"
local encode = cjson.encode
local decode = cjson.decode
local table_concat = table.concat
local trie_get = trie.get
local match = string.match
local gsub = string.gsub
local lower = string.lower
-- we "cache" the config local to each worker
local ingressConfig = nil
local cluster_domain = "cluster.local"
local def_backend = nil
local custom_error = nil
local dns_cache_options = nil
function get_ingressConfig(ngx)
local d = ngx.shared["ingress"]
local value, flags, stale = d:get_stale("ingressConfig")
if not value then
-- nothing we can do
return nil, "config not set"
end
ingressConfig = decode(value)
return ingressConfig, nil
end
function worker_cache_config(ngx)
local _, err = get_ingressConfig(ngx)
if err then
ngx.log(ngx.ERR, "unable to get ingressConfig: ", err)
return
end
end
function _M.content(ngx)
local host = ngx.var.host
-- strip off any port
local h = match(host, "^(.+):?")
if h then
host = h
end
host = lower(host)
local config, err = get_ingressConfig(ngx)
if err then
ngx.log(ngx.ERR, "unable to get config: ", err)
return ngx.exit(503)
end
-- this assumes we only allow exact host matches
local paths = config[host]
if not paths then
ngx.log(ngx.ERR, "No server for host "..host.." returning 404")
if custom_error then
openCustomErrorURL(404, custom_error)
return
else
openURL(404, def_backend)
return
end
end
local backend = trie_get(paths, ngx.var.uri)
if not backend then
ngx.log(ngx.ERR, "No server for host "..host.." and path "..ngx.var.uri.." returning 404")
if custom_error then
openCustomErrorURL(404, custom_error)
return
else
openURL(404, def_backend)
return
end
end
local address = backend.host
ngx.var.upstream_port = backend.port or 80
if dns_cache_options then
local dns = cache.new(dns_cache_options)
local answer, err, stale = dns:query(address, { qtype = 1 })
if err or (not answer) then
if stale then
answer = stale
else
answer = nil
end
end
if answer and answer[1] then
local ans = answer[1]
if ans.address then
address = ans.address
end
else
ngx.log(ngx.ERR, "dns failed for ", address, " with ", err, " => ", encode(answer or ""))
end
end
ngx.var.upstream_host = address
return
end
function _M.init_worker(ngx)
end
function _M.init(ngx, options)
-- ngx.log(ngx.OK, "options: "..encode(options))
def_backend = options.def_backend
custom_error = options.custom_error
-- try to create a dns cache
local resolvers = options.resolvers
if resolvers then
cache.init_cache(512)
local servers = trie.strsplit(" ", resolvers)
dns_cache_options =
{
dict = "dns_cache",
negative_ttl = nil,
max_stale = 900,
normalise_ttl = false,
resolver = {
nameservers = {servers[1]}
}
}
end
end
-- dump config. This is the raw config (including trie) for now
function _M.config(ngx)
ngx.header.content_type = "application/json"
local config = {
ingress = ingressConfig
}
local val = encode(config)
ngx.print(val)
end
function _M.update_ingress(ngx)
ngx.header.content_type = "application/json"
if ngx.req.get_method() ~= "POST" then
ngx.print(encode({
message = "only POST request"
}))
ngx.exit(400)
return
end
ngx.req.read_body()
local data = ngx.req.get_body_data()
local val = decode(data)
if not val then
ngx.log(ngx.ERR, "failed to decode body")
return
end
config = {}
for _, ingress in ipairs(val) do
local namespace = ingress.metadata.namespace
local spec = ingress.spec
-- we do not allow default ingress backends right now.
for _, rule in ipairs(spec.rules) do
local host = rule.host
local paths = config[host]
if not paths then
paths = trie.new()
config[host] = paths
end
rule.http = rule.http or { paths = {}}
for _, path in ipairs(rule.http.paths) do
local hostname = table_concat(
{
path.backend.serviceName,
namespace,
"svc",
cluster_domain
}, ".")
local backend = {
host = hostname,
port = path.backend.servicePort
}
paths:add(path.path, backend)
end
end
end
local d = ngx.shared["ingress"]
local ok, err, _ = d:set("ingressConfig", encode(config))
if not ok then
ngx.log(ngx.ERR, "Error: "..err)
local res = encode({
message = "Error updating Ingress rules: "..err
})
ngx.print(res)
return ngx.exit(500)
end
ingressConfig = config
local res = encode({
message = "Ingress rules updated"
})
ngx.print(res)
end
return _M