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
13 changes: 8 additions & 5 deletions apisix/cli/file.lua
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,10 @@ local function var_sub(val)
end


local function resolve_conf_var(conf)
local function resolve_conf_var(conf, enable_type_conversion)
if enable_type_conversion == nil then
enable_type_conversion = true
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

resolve_conf_var now defaults enable_type_conversion to true, but apisix/core/config_yaml.lua calls file.resolve_conf_var(table) when reloading apisix.yaml in standalone mode. That means type conversion is still enabled at runtime for apisix.yaml, so the large-number/scientific-notation issue will persist unless that call site is updated to pass false (or otherwise disable conversion for apisix.yaml).

Suggested change
enable_type_conversion = true
enable_type_conversion = false

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

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

I'd prefer keeping the default enable_type_conversion = true to ensure backward compatibility. Since _M.resolve_conf_var is a public function, changing the global default would silently break existing callers (like config.yaml processing) that rely on type conversion.
Because this fix is specific to apisix.yaml in standalone mode, it's safer for those specific call sites to explicitly opt out by passing false. I'll also add file.resolve_conf_var(table, false) to update_config() in apisix/core/config_yaml.lua to handle runtime reloads.

end
local new_keys = {}
for key, val in pairs(conf) do
-- avoid re-iterating the table for already iterated key
Expand All @@ -111,7 +114,7 @@ local function resolve_conf_var(conf)
end
end
if type(val) == "table" then
local ok, err = resolve_conf_var(val)
local ok, err = resolve_conf_var(val, enable_type_conversion)
if not ok then
return nil, err
end
Expand All @@ -123,7 +126,7 @@ local function resolve_conf_var(conf)
return nil, err
end

if var_used then
if var_used and enable_type_conversion then
if tonumber(new_val) ~= nil then
new_val = tonumber(new_val)
elseif new_val == "true" then
Expand Down Expand Up @@ -255,7 +258,7 @@ function _M.read_yaml_conf(apisix_home)
return nil, "invalid config.yaml file"
end

local ok, err = resolve_conf_var(user_conf)
local ok, err = resolve_conf_var(user_conf, true)
if not ok then
return nil, err
end
Expand Down Expand Up @@ -304,7 +307,7 @@ function _M.read_yaml_conf(apisix_home)
if apisix_conf_yaml then
local apisix_conf = yaml.load(apisix_conf_yaml)
if apisix_conf then
local ok, err = resolve_conf_var(apisix_conf)
local ok, err = resolve_conf_var(apisix_conf, false)
if not ok then
return nil, err
end
Expand Down
2 changes: 1 addition & 1 deletion apisix/core/config_yaml.lua
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ local function update_config(table, conf_version)
return
end

local ok, err = file.resolve_conf_var(table)
local ok, err = file.resolve_conf_var(table, false)
if not ok then
log.error("failed to resolve variables:" .. err)
return
Expand Down
139 changes: 139 additions & 0 deletions t/cli/test_standalone.sh
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,142 @@ if [ $expected_config_reloads -ne $actual_config_reloads ]; then
exit 1
fi
echo "passed: apisix.yaml was not reloaded"

make stop
sleep 0.5

# test: environment variable with large number should be preserved as string
echo '
apisix:
enable_admin: false
deployment:
role: data_plane
role_data_plane:
config_provider: yaml
' > conf/config.yaml

echo '
routes:
-
uri: /test-large-number
plugins:
response-rewrite:
body: "${{APISIX_CLIENT_ID}}"
status_code: 200
upstream:
nodes:
"127.0.0.1:9091": 1
type: roundrobin
#END
' > conf/apisix.yaml

# Test with large number that exceeds Lua double precision
APISIX_CLIENT_ID="356002209726529540" make init

if ! APISIX_CLIENT_ID="356002209726529540" make run > output.log 2>&1; then
cat output.log
echo "failed: large number in env var should not cause type conversion error"
exit 1
fi

sleep 0.1

# Verify the response body matches the exact large numeric string
code=$(curl -o /tmp/response_body -s -m 5 -w %{http_code} http://127.0.0.1:9080/test-large-number)
body=$(cat /tmp/response_body)
if [ "$code" -ne 200 ]; then
echo "failed: expected 200 for /test-large-number, but got: $code, body: $body"
exit 1
fi
if [ "$body" != "356002209726529540" ]; then
echo "failed: large number env var was not preserved as string, got: $body"
exit 1
fi
Comment on lines +198 to +208
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

For the new routes, the assertions only compare the response body. If the request fails or returns an unexpected status (eg 404/502), the body check may be misleading. Capture and assert the HTTP status code is 200 alongside the body for /test-large-number and /test-quoted so failures are diagnosed correctly.

Copilot uses AI. Check for mistakes.

make stop
sleep 0.5

echo "passed: large number in env var preserved as string in apisix.yaml"

# test: quoted numeric env vars in apisix.yaml should remain strings
echo '
apisix:
enable_admin: false
deployment:
role: data_plane
role_data_plane:
config_provider: yaml
' > conf/config.yaml

echo '
routes:
-
uri: /test-quoted
plugins:
response-rewrite:
body: "${{NUMERIC_ID}}"
status_code: 200
upstream:
nodes:
"127.0.0.1:9091": 1
type: roundrobin
#END
' > conf/apisix.yaml

NUMERIC_ID="12345" make init
NUMERIC_ID="12345" make run
sleep 0.1

code=$(curl -o /tmp/response_body -s -m 5 -w %{http_code} http://127.0.0.1:9080/test-quoted)
body=$(cat /tmp/response_body)
if [ "$code" -ne 200 ]; then
echo "failed: expected 200 for /test-quoted, but got: $code, body: $body"
exit 1
fi
if [ "$body" != "12345" ]; then
echo "failed: quoted numeric env var in apisix.yaml was not preserved as string, got: $body"
exit 1
fi

make stop
sleep 0.5

echo "passed: quoted numeric env var preserved as string in apisix.yaml"

# test: config.yaml should still support type conversion (boolean)
echo '
routes: []
#END
' > conf/apisix.yaml

echo '
apisix:
enable_admin: ${{ENABLE_ADMIN}}
deployment:
role: traditional
role_traditional:
config_provider: yaml
etcd:
host:
- "http://127.0.0.1:2379"
' > conf/config.yaml

ENABLE_ADMIN=false make init
ENABLE_ADMIN=false make run
sleep 0.1

# If type conversion works, enable_admin is boolean false and admin API is disabled (404)
# If type conversion fails, enable_admin stays string "false" which is truthy, admin API is enabled
code=$(curl -o /dev/null -s -m 5 -w %{http_code} http://127.0.0.1:9080/apisix/admin/routes)
if [ "$code" -ne 404 ]; then
echo "failed: expected 404 when admin API is disabled, but got: $code"
exit 1
fi

make stop
sleep 0.5

echo "passed: config.yaml still converts boolean env vars correctly"

git checkout conf/config.yaml
git checkout conf/apisix.yaml
Loading