Skip to content
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
0fcff04
feat(standalone): support json format
SkyeYoung Jun 17, 2025
9cece82
feat: decode json logic
SkyeYoung Jun 17, 2025
d0b64a1
chore: correct vars name
SkyeYoung Jun 17, 2025
56663f2
chore: rm useless changes
SkyeYoung Jun 17, 2025
fa91d38
feat: optimize log format
SkyeYoung Jun 17, 2025
510ed3e
feat: skip check END mark in json format
SkyeYoung Jun 17, 2025
21a8ae5
chore: rename var
SkyeYoung Jun 17, 2025
4352026
test: consumer-group
SkyeYoung Jun 17, 2025
7e59f67
test: cp and transform config-center-yaml to json cases
SkyeYoung Jun 17, 2025
ac63edf
chore: rm useless file
SkyeYoung Jun 18, 2025
8ef41da
chore: rm not exist cases
SkyeYoung Jun 18, 2025
3b899b4
test: add default apisix json config
SkyeYoung Jun 18, 2025
40900a7
chore: rollback maybe public info
SkyeYoung Jun 18, 2025
4db06b9
chore: lint
SkyeYoung Jun 18, 2025
2c9997c
chore: lint
SkyeYoung Jun 18, 2025
33562c5
chore: lint
SkyeYoung Jun 18, 2025
4725b2f
chore: rm useless change
SkyeYoung Jun 18, 2025
491a191
docs(deployment-modes): add json related
SkyeYoung Jun 18, 2025
1d2aedf
fix: logic
SkyeYoung Jun 18, 2025
9a18acd
docs: update
SkyeYoung Jun 18, 2025
ccc8e1c
chore: simplify logic
SkyeYoung Jun 18, 2025
b77b4d5
chore
SkyeYoung Jun 18, 2025
db6491e
Update docs/en/latest/deployment-modes.md
SkyeYoung Jun 19, 2025
0ec019d
Update docs/en/latest/deployment-modes.md
SkyeYoung Jun 19, 2025
d3890e3
refactor: selection logic for config provider `json`
SkyeYoung Jun 20, 2025
d33703b
Merge remote-tracking branch 'upstream/master' into young/feat/suppor…
SkyeYoung Jun 20, 2025
195a7d5
Merge remote-tracking branch 'origin/young/feat/support-json-in-stand…
SkyeYoung Jun 20, 2025
5e81920
chore: rm too long error
SkyeYoung Jun 20, 2025
67bae17
chore: rm useless codes, comments
SkyeYoung Jun 20, 2025
4bd3819
feat: add json config_provider transform logic
SkyeYoung Jun 20, 2025
55ca345
fix
SkyeYoung Jun 20, 2025
62b3585
fix: require json config module logic
SkyeYoung Jun 20, 2025
863d552
fix: rm redundant logic
SkyeYoung Jun 20, 2025
3ce58f5
fix: test
SkyeYoung Jun 20, 2025
0cf3e3d
refactor
SkyeYoung Jun 20, 2025
3a79e7b
perf: use comma to reduce string concat
SkyeYoung Jun 20, 2025
e67ef4a
docs: add json examples
SkyeYoung Jun 20, 2025
81e2cc7
Update docs/en/latest/deployment-modes.md
SkyeYoung Jun 20, 2025
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
2 changes: 2 additions & 0 deletions apisix/cli/file.lua
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,8 @@ function _M.read_yaml_conf(apisix_home)
default_conf.etcd = default_conf.deployment.etcd
if default_conf.deployment.role_data_plane.config_provider == "yaml" then
default_conf.deployment.config_provider = "yaml"
elseif default_conf.deployment.role_data_plane.config_provider == "json" then
default_conf.deployment.config_provider = "json"
elseif default_conf.deployment.role_data_plane.config_provider == "xds" then
default_conf.deployment.config_provider = "xds"
end
Expand Down
2 changes: 1 addition & 1 deletion apisix/cli/schema.lua
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ local deployment_schema = {
role_data_plane = {
properties = {
config_provider = {
enum = {"etcd", "yaml", "xds"}
enum = {"etcd", "yaml", "json", "xds"}
},
},
required = {"config_provider"}
Expand Down
11 changes: 10 additions & 1 deletion apisix/core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,16 @@ end
local config_provider = local_conf.deployment and local_conf.deployment.config_provider
or "etcd"
log.info("use config_provider: ", config_provider)
local config = require("apisix.core.config_" .. config_provider)

local config
-- Currently, we handle JSON parsing in config_yaml, so special processing is needed here.
if config_provider == "json" then
config = require("apisix.core.config_yaml")
config.file_type = "json"
else
config = require("apisix.core.config_" .. config_provider)
end

config.type = config_provider


Expand Down
113 changes: 76 additions & 37 deletions apisix/core/config_yaml.lua
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ local ngx = ngx
local re_find = ngx.re.find
local process = require("ngx.process")
local worker_id = ngx.worker.id
local apisix_yaml_path = profile:yaml_path("apisix")
local created_obj = {}
local shared_dict
local status_report_shared_dict_name = "status-report"
Expand All @@ -56,6 +55,9 @@ local _M = {
local_conf = config_local.local_conf,
clear_local_cache = config_local.clear_cache,

-- yaml or json
file_type = "yaml",

ERR_NO_SHARED_DICT = "failed prepare standalone config shared dict, this will degrade "..
"to event broadcasting, and if a worker crashes, the configuration "..
"cannot be restored from other workers and shared dict"
Expand All @@ -69,10 +71,68 @@ local mt = {
end
}


local apisix_yaml
local apisix_yaml_mtime

local config_yaml = {
path = profile:yaml_path("apisix"),
type = "yaml",
parse = function(self)
local f, err = io.open(self.path, "r")
if not f then
return nil, "failed to open file " .. self.path .. " : " .. err
end

f:seek('end', -10)
local end_flag = f:read("*a")
local found_end_flag = re_find(end_flag, [[#END\s*$]], "jo")

if not found_end_flag then
f:close()
return nil, "missing valid end flag in file " .. self.path
end

f:seek('set')
local raw_config = f:read("*a")
f:close()

return yaml.load(raw_config), nil
end
}

local config_json = {
-- `-5` to remove the "yaml" suffix
path = config_yaml.path:sub(1, -5) .. "json",
type = "json",
parse = function(self)
local f, err = io.open(self.path, "r")
if not f then
return nil, "failed to open file " .. self.path .. " : " .. err
end
local raw_config = f:read("*a")
f:close()

local config, err = json.decode(raw_config)
if err then
return nil, "failed to decode json: " .. err
end
return config, nil
end
}

local config_file_table = {
yaml = config_yaml,
json = config_json
}


local config_file = setmetatable({}, {
__index = function(_, key)
return config_file_table[_M.file_type][key]
end
})


local function sync_status_to_shdict(status)
if process.type() ~= "worker" then
return
Expand Down Expand Up @@ -112,13 +172,13 @@ local function is_use_admin_api()
end


local function read_apisix_yaml(premature, pre_mtime)
local function read_apisix_config(premature, pre_mtime)
if premature then
return
end
local attributes, err = lfs.attributes(apisix_yaml_path)
local attributes, err = lfs.attributes(config_file.path)
if not attributes then
log.error("failed to fetch ", apisix_yaml_path, " attributes: ", err)
log.error("failed to fetch ", config_file.path, " attributes: ", err)
return
end

Expand All @@ -127,36 +187,15 @@ local function read_apisix_yaml(premature, pre_mtime)
return
end

local f, err = io.open(apisix_yaml_path, "r")
if not f then
log.error("failed to open file ", apisix_yaml_path, " : ", err)
return
end

f:seek('end', -10)
local end_flag = f:read("*a")
-- log.info("flag: ", end_flag)
local found_end_flag = re_find(end_flag, [[#END\s*$]], "jo")

if not found_end_flag then
f:close()
log.warn("missing valid end flag in file ", apisix_yaml_path)
return
end

f:seek('set')
local yaml_config = f:read("*a")
f:close()

local apisix_yaml_new = yaml.load(yaml_config)
if not apisix_yaml_new then
log.error("failed to parse the content of file " .. apisix_yaml_path)
local config_new, err = config_file:parse()
if err then
log.error("failed to parse the content of file " .. config_file.path .. ": " .. err)
return
end

update_config(apisix_yaml_new, last_modification_time)
update_config(config_new, last_modification_time)

log.warn("config file ", apisix_yaml_path, " reloaded.")
log.warn("config file ", config_file.path, " reloaded.")
end


Expand All @@ -171,7 +210,7 @@ local function sync_data(self)
else
if not apisix_yaml_mtime then
log.warn("wait for more time")
return nil, "failed to read local file " .. apisix_yaml_path
return nil, "failed to read local file " .. config_file.path
end
conf_version = apisix_yaml_mtime
end
Expand Down Expand Up @@ -395,15 +434,15 @@ local function _automatic_fetch(premature, self)
local ok, ok2, err = pcall(sync_data, self)
if not ok then
err = ok2
log.error("failed to fetch data from local file " .. apisix_yaml_path .. ": ",
log.error("failed to fetch data from local file " .. config_file.path .. ": ",
err, ", ", tostring(self))
ngx_sleep(3)
break

elseif not ok2 and err then
if err ~= "timeout" and err ~= "Key not found"
and self.last_err ~= err then
log.error("failed to fetch data from local file " .. apisix_yaml_path .. ": ",
log.error("failed to fetch data from local file " .. config_file.path .. ": ",
err, ", ", tostring(self))
end

Expand Down Expand Up @@ -477,7 +516,7 @@ function _M.new(key, opts)
end

if err then
log.error("failed to fetch data from local file ", apisix_yaml_path, ": ",
log.error("failed to fetch data from local file ", config_file.path, ": ",
err, ", ", key)
end

Expand Down Expand Up @@ -517,7 +556,7 @@ function _M.init()
return true
end

read_apisix_yaml()
read_apisix_config()
return true
end

Expand All @@ -531,7 +570,7 @@ function _M.init_worker()
end

-- sync data in each non-master process
ngx.timer.every(1, read_apisix_yaml)
ngx.timer.every(1, read_apisix_config)

return true
end
Expand Down
23 changes: 21 additions & 2 deletions docs/en/latest/deployment-modes.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ APISIX has three different deployment modes for different production use cases.
|-----------------|----------------------------|---------------------------------------------------------------------------------------------------------------------|
| traditional | traditional | Data plane and control plane are deployed together. `enable_admin` attribute should be disabled manually. |
| decoupled | data_plane / control_plane | Data plane and control plane are deployed independently. |
| standalone | data_plane / traditional | The `data_plane` mode loads configuration from a local YAML file, while the traditional mode expects configuration through Admin API. |
| standalone | data_plane / traditional | The `data_plane` mode loads configuration from a local YAML / JSON file, while the traditional mode expects configuration through Admin API. |

Each of these deployment modes are explained in detail below.

Expand Down Expand Up @@ -136,6 +136,8 @@ deployment:

This makes it possible to disable the Admin API and discover configuration changes and reloads based on the local file system.

*Note*: When using `config_provider: yaml`, you can also provide the configuration in JSON format by placing it in `conf/apisix.json`.

#### API-driven (Experimental)

> This mode is experimental, please do not rely on it in your production environment.
Expand Down Expand Up @@ -293,7 +295,7 @@ routes:
#END
```

*WARNING*: APISIX will not load the rules into memory from file `conf/apisix.yaml` if there is no `#END` at the end.
*WARNING*: APISIX will not load the rules into memory from file `conf/apisix.yaml` if there is no `#END` at the end. However, when using `conf/apisix.json`, the `#END` marker is not required, as APISIX can directly parse and validate the JSON structure.

Environment variables can also be used like so:

Expand Down Expand Up @@ -327,6 +329,23 @@ routes:
#END
```

```json5
// JSON configuration can be directly translated from YAML, this is just an example.
Copy link
Member

Choose a reason for hiding this comment

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

Presentation wise this is not the best as there are a number of examples provided for standalone configuration in parallel: https://apisix.apache.org/docs/apisix/next/deployment-modes/#standalone

Better presentation is to use tabs (YAML, JSON) and add JSON configs for all examples. Can use GPT to do so if the details are all 1:1.

Copy link
Member Author

Choose a reason for hiding this comment

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

OK, I will wait for others to finish the feedback on the code part and repair the document part.

If I remember correctly, this might involve converting the document to MDX.

{
"routes": [
{
"uri": "/hello",
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
}
}
]
}
```

Multiple Routes:

```yaml
Expand Down
24 changes: 24 additions & 0 deletions t/APISIX.pm
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,17 @@ my $profile = $ENV{"APISIX_PROFILE"};


my $apisix_file;
my $apisix_file_json;
my $debug_file;
my $config_file;
if ($profile) {
$apisix_file = "apisix-$profile.yaml";
$apisix_file_json = "apisix-$profile.json";
$debug_file = "debug-$profile.yaml";
$config_file = "config-$profile.yaml";
} else {
$apisix_file = "apisix.yaml";
$apisix_file_json = "apisix.json";
$debug_file = "debug.yaml";
$config_file = "config.yaml";
}
Expand Down Expand Up @@ -254,6 +257,18 @@ deployment:
_EOC_
}

if ($block->apisix_json && (!defined $block->yaml_config)) {
$user_yaml_config = <<_EOC_;
apisix:
node_listen: 1984
enable_admin: false
deployment:
role: data_plane
role_data_plane:
config_provider: json
_EOC_
}

my $lua_deps_path = $block->lua_deps_path // <<_EOC_;
lua_package_path "$apisix_home/?.lua;$apisix_home/?/init.lua;$apisix_home/deps/share/lua/5.1/?/init.lua;$apisix_home/deps/share/lua/5.1/?.lua;$apisix_home/apisix/?.lua;$apisix_home/t/?.lua;$apisix_home/t/xrpc/?.lua;$apisix_home/t/xrpc/?/init.lua;;";
lua_package_cpath "$apisix_home/?.so;$apisix_home/deps/lib/lua/5.1/?.so;$apisix_home/deps/lib64/lua/5.1/?.so;;";
Expand Down Expand Up @@ -915,6 +930,14 @@ $user_apisix_yaml
_EOC_
}

my $user_apisix_json = $block->apisix_json // "";
if ($user_apisix_json){
$user_apisix_json = <<_EOC_;
>>> ../conf/$apisix_file_json
$user_apisix_json
_EOC_
}

my $yaml_config = $block->yaml_config // $user_yaml_config;

my $default_deployment = <<_EOC_;
Expand Down Expand Up @@ -959,6 +982,7 @@ $etcd_pem
>>> ../conf/cert/etcd.key
$etcd_key
$user_apisix_yaml
$user_apisix_json
_EOC_

$block->set_value("user_files", $user_files);
Expand Down
Loading
Loading