Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configure DataDog host and port from env variables #7463

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 15 additions & 4 deletions kong/plugins/datadog/schema.lua
Expand Up @@ -73,8 +73,9 @@ return {
type = "record",
default = { metrics = DEFAULT_METRICS },
fields = {
{ host = typedefs.host({ required = true, default = "localhost" }), },
{ port = typedefs.port({ required = true, default = 8125 }), },
{ use_env = { type = "boolean", default = false } },
{ host = typedefs.host({ default = "localhost" }), },
{ port = typedefs.port({ default = 8125 }), },
{ prefix = { type = "string", default = "kong" }, },
{ metrics = {
type = "array",
Expand All @@ -95,7 +96,17 @@ return {
if_match = { one_of = { "counter", "gauge" }, },
then_field = "sample_rate",
then_match = { required = true },
}, },
}, }, }, }, }, }, }, },
}, }, }, }, }, },
},
entity_checks = {
{ conditional = {
if_field = "use_env", if_match = {eq = false},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@javierguerragiraldez I don't like the name use_env but also don't know of a better alternative, thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use case here is defining the host and port when DataDog is run as a daemonset under Kubernetes. In theory, this configuration could be applied more broadly though

then_field = "host", then_match = { required = true },
}, },
{ conditional = {
if_field = "use_env", if_match = {eq = false},
then_field = "port", then_match = { required = true },
}, },
}, }, }, },
}

25 changes: 20 additions & 5 deletions kong/plugins/datadog/statsd_logger.lua
Expand Up @@ -19,18 +19,33 @@ local stat_types = {
local statsd_mt = {}
statsd_mt.__index = statsd_mt

local env_datadog_agent_host = os.getenv 'KONG_DATADOG_AGENT_HOST'
local env_datadog_agent_port = tonumber(os.getenv 'KONG_DATADOG_AGENT_PORT' or "")

function statsd_mt:new(conf)
local sock = udp()
local _, err = sock:setpeername(conf.host, conf.port)

local host, port
if conf.use_env then
if not (env_datadog_agent_host and env_datadog_agent_port) then
return nil, "both KONG_DATADOG_AGENT_HOST and KONG_DATADOG_AGENT_PORT must be set"
end
host = env_datadog_agent_host
port = env_datadog_agent_port
else
host = conf.host
port = conf.port
end

local _, err = sock:setpeername(host, port)
if err then
return nil, fmt("failed to connect to %s:%s: %s", conf.host,
tostring(conf.port), err)
return nil, fmt("failed to connect to %s:%s: %s", host,
tostring(port), err)
end

local statsd = {
host = conf.host,
port = conf.port,
host = host,
port = port,
prefix = conf.prefix,
socket = sock,
stat_types = stat_types,
Expand Down
46 changes: 46 additions & 0 deletions spec/03-plugins/08-datadog/01-log_spec.lua
Expand Up @@ -45,6 +45,11 @@ for _, strategy in helpers.each_strategy() do
service = bp.services:insert { name = "dd4" }
}

local route5 = bp.routes:insert {
hosts = { "datadog5.com" },
service = bp.services:insert { name = "dd5" }
}

local route_grpc = assert(bp.routes:insert {
protocols = { "grpc" },
paths = { "/hello.HelloService/" },
Expand Down Expand Up @@ -122,6 +127,21 @@ for _, strategy in helpers.each_strategy() do
},
}

bp.plugins:insert {
name = "key-auth",
route = { id = route5.id },
}

helpers.setenv('KONG_DATADOG_AGENT_HOST', 'localhost')
helpers.setenv('KONG_DATADOG_AGENT_PORT', '9999')
rallyben marked this conversation as resolved.
Show resolved Hide resolved
bp.plugins:insert {
name = "datadog",
route = { id = route5.id },
config = {
use_env = true,
},
}

bp.plugins:insert {
name = "key-auth",
route = { id = route_grpc.id },
Expand All @@ -148,6 +168,9 @@ for _, strategy in helpers.each_strategy() do
proxy_client:close()
end

helpers.unsetenv('KONG_DATADOG_AGENT_HOST')
helpers.unsetenv('KONG_DATADOG_AGENT_PORT')

helpers.stop_kong()
end)

Expand Down Expand Up @@ -257,6 +280,29 @@ for _, strategy in helpers.each_strategy() do
assert.contains("kong.latency:%d+|g|#name:dd3,status:200,T2:V2:V3,T4", gauges, true)
end)

it("logs metrics to host/port defined via environment variables", function()
local thread = helpers.udp_server(9999, 6)

local res = assert(proxy_client:send {
method = "GET",
path = "/status/200?apikey=kong",
headers = {
["Host"] = "datadog5.com"
}
})
assert.res_status(200, res)

local ok, gauges = thread:join()
assert.True(ok)
assert.equal(6, #gauges)
assert.contains("kong.request.count:1|c|#name:dd5,status:200,consumer:bar,app:kong" , gauges)
assert.contains("kong.latency:%d+|ms|#name:dd5,status:200,consumer:bar,app:kong", gauges, true)
assert.contains("kong.request.size:%d+|ms|#name:dd5,status:200,consumer:bar,app:kong", gauges, true)
assert.contains("kong.response.size:%d+|ms|#name:dd5,status:200,consumer:bar,app:kong", gauges, true)
assert.contains("kong.upstream_latency:%d+|ms|#name:dd5,status:200,consumer:bar,app:kong", gauges, true)
assert.contains("kong.kong_latency:%d*|ms|#name:dd5,status:200,consumer:bar,app:kong", gauges, true)
end)

it("should not return a runtime error (regression)", function()
local thread = helpers.udp_server(9999, 1, 1)

Expand Down
13 changes: 13 additions & 0 deletions spec/03-plugins/08-datadog/02-schema_spec.lua
Expand Up @@ -8,6 +8,19 @@ describe("Plugin: datadog (schema)", function()
assert.is_nil(err)
assert.is_truthy(ok)
end)
it("rejects null host without use_env", function()
local _, err = v({ host = ngx.null, use_env = false }, schema_def)
assert.equal("required field missing", err.config.host)
end)
it("rejects null port without use_env", function()
local _, err = v({ port = ngx.null, use_env = false }, schema_def)
assert.equal("required field missing", err.config.port)
end)
it("accepts null host and port with use_env", function()
local ok, err = v({ host = ngx.null, port = ngx.null, use_env = true }, schema_def)
assert.is_nil(err)
assert.is_truthy(ok)
end)
it("accepts empty metrics", function()
local metrics_input = {}
local ok, err = v({ metrics = metrics_input }, schema_def)
Expand Down