Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apisix/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ function _M.ssl_client_hello_phase()
core.log.error("failed to match any SSL certificate by SNI: ", sni)
span:set_status(tracer.status.ERROR, "no matched SSL")
span:finish(ngx_ctx)
tracer.release(ngx_ctx)
ngx_exit(-1)
end

Expand All @@ -230,13 +231,15 @@ function _M.ssl_client_hello_phase()
core.log.error("failed to set ssl protocols: ", err)
span:set_status(tracer.status.ERROR, "failed set protocols")
span:finish(ngx_ctx)
tracer.release(ngx_ctx)
ngx_exit(-1)
end

-- in stream subsystem, ngx.ssl.server_name() return hostname of ssl session in preread phase,
-- so that we can't get real SNI without recording it in ngx.ctx during client_hello phase
ngx.ctx.client_hello_sni = sni
span:finish(ngx_ctx)
tracer.release(ngx_ctx)
end


Expand Down
7 changes: 4 additions & 3 deletions apisix/tracer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ function _M.start(ctx, name, kind)
return noop_span
end

local tracing = ctx.tracing
local tracing = rawget(ctx, "tracing")
if not tracing then
tracing = tablepool.fetch("tracing", 0, 8)
tracing.spans = tablepool.fetch("tracing_spans", 20, 0)
ctx.tracing = tracing
rawset(ctx, "tracing", tracing)
-- create a dummy root span as the invisible parent of all top-level spans
span.new(ctx, "root", nil)
end
Expand Down Expand Up @@ -72,7 +72,7 @@ end


function _M.release(ctx)
local tracing = ctx.tracing
local tracing = rawget(ctx, "tracing")
if not tracing then
return
end
Expand All @@ -82,6 +82,7 @@ function _M.release(ctx)
end
tablepool.release("tracing_spans", tracing.spans)
tablepool.release("tracing", tracing)
Comment thread
janiussyafiq marked this conversation as resolved.
rawset(ctx, "tracing", nil)
end


Expand Down
46 changes: 46 additions & 0 deletions t/lib/test_otel.lua
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,50 @@ function _M.verify_tree(filepath, expected_tree)
end


function _M.verify_isolated_traces(filepath, root_name, count)
local spans_by_id, err = parse_spans(filepath)
if not spans_by_id then
return false, err
end

local traces = {}
for _, span in pairs(spans_by_id) do
if not traces[span.traceId] then
traces[span.traceId] = {}
end
table.insert(traces[span.traceId], span.name)
end

local matching = {}
for trace_id, names in pairs(traces) do
for _, name in ipairs(names) do
if name == root_name then
table.insert(matching, { id = trace_id, names = names })
break
end
end
end

if #matching ~= count then
return false, string.format(
"expected %d traces with span '%s', got %d",
count, root_name, #matching)
end

for _, trace in ipairs(matching) do
local seen = {}
for _, name in ipairs(trace.names) do
if seen[name] then
return false, string.format(
"trace %s has duplicate span '%s': cross-stream contamination detected",
trace.id, name)
end
seen[name] = true
end
end

return true
end


return _M
105 changes: 105 additions & 0 deletions t/node/tracer.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';

repeat_each(1);
log_level('debug');
no_root_location();
no_shuffle();

add_block_preprocessor(sub {
my ($block) = @_;

if (!$block->extra_yaml_config) {
my $extra_yaml_config = <<_EOC_;
apisix:
tracing: true
_EOC_
$block->set_value("extra_yaml_config", $extra_yaml_config);
}

if (!$block->request) {
$block->set_value("request", "GET /t");
}

if (!defined $block->response_body) {
$block->set_value("response_body", "passed\n");
}
});

run_tests;

__DATA__

=== TEST 1: set SSL cert for test.com
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local core = require("apisix.core")
local data = {cert = ssl_cert, key = ssl_key, sni = "test.com"}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"value": {
"sni": "test.com"
},
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}



=== TEST 2: set route
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/opentracing"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}



=== TEST 3: consecutive HTTPS keepalive requests do not crash when tracing is enabled
--- exec
curl -s -k https://test.com:1994/opentracing https://test.com:1994/opentracing
--- response_body
opentracing
opentracing
60 changes: 53 additions & 7 deletions t/plugin/opentelemetry6.t
Original file line number Diff line number Diff line change
Expand Up @@ -218,13 +218,6 @@ opentracing
["http.status_code"] = "200",
},
children = {
{
name = "ssl_client_hello_phase",
kind = 2,
children = {
{ name = "sni_radixtree_match", kind = 1 },
}
},
{
name = "apisix.phase.access",
kind = 2,
Expand All @@ -248,3 +241,56 @@ opentracing
end
}
}



=== TEST 7: clear file
--- exec
echo '' > ci/pod/otelcol-contrib/data-otlp.json
--- response_body eval
qr//



=== TEST 8: trigger two concurrent HTTP/2 requests on the same TLS connection
--- init_by_lua_block
require "resty.core"
apisix = require("apisix")
core = require("apisix.core")
apisix.http_init()

local utils = require("apisix.core.utils")
utils.dns_parse = function (domain)
if domain == "test1.com" then
return {address = "127.0.0.2"}
end
error("unknown domain: " .. domain)
end
--- exec
curl -sk --http2 --parallel --resolve "test.com:1994:127.0.0.1" https://test.com:1994/opentracing https://test.com:1994/opentracing
--- wait: 5
--- response_body
opentracing
opentracing



=== TEST 9: verify each HTTP/2 stream has its own isolated span set
--- config
location /t {
content_by_lua_block {
local otel = require("lib.test_otel")

local ok, err = otel.verify_isolated_traces(
"ci/pod/otelcol-contrib/data-otlp.json",
"GET /opentracing",
2
)

if not ok then
ngx.say("FAIL:\n" .. err)
else
ngx.say("passed")
end
}
}
Loading