Skip to content

Commit 443b84c

Browse files
authored
Merge pull request #1352 from 3scale/new-account-proxyconfigs-endpoint
THREESCALE-8508 - /admin/api/account/proxy_configs endpoint for configuration loading
2 parents d65ca28 + 35f8111 commit 443b84c

10 files changed

+560
-417
lines changed

CHANGELOG.md

+17
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1010
### Added
1111

1212
- Opentelemetry support. Opentracing is now deprecated [PR #1379](https://github.com/3scale/APIcast/pull/1379) [THREESCALE-7735](https://issues.redhat.com/browse/THREESCALE-7735)
13+
- `/admin/api/account/proxy_configs` endpoint for configuration loading [PR #1352](https://github.com/3scale/APIcast/pull/1352) [THREESCALE-8508](https://issues.redhat.com/browse/THREESCALE-8508)
14+
15+
### Removed
16+
17+
- `APICAST_LOAD_SERVICES_WHEN_NEEDED` is dropped and the configuration is fetched "when needed" by default [PR #1352](https://github.com/3scale/APIcast/pull/1352) [THREESCALE-8508](https://issues.redhat.com/browse/THREESCALE-8508)
1318

1419
## [3.13.2] 2023-02-21
1520

@@ -74,6 +79,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
7479
- Fixed issues with OIDC filters [PR #1304](https://github.com/3scale/APIcast/pull/1304) [PR #1306](https://github.com/3scale/APIcast/pull/1306) [THREESCALE-6042](https://issues.redhat.com/browse/THREESCALE-6042)
7580
- Fixed issues with OIDC filters [PR #1304](https://github.com/3scale/APIcast/pull/1304) [THREESCALE-6042](https://issues.redhat.com/browse/THREESCALE-6042)
7681
- Fixed issues with Upstream MTLS certs [PR #1307](https://github.com/3scale/APIcast/pull/1307) [THREESCALE-7508](https://issues.redhat.com/browse/THREESCALE-7508)
82+
- Fixed warning messages [PR #1318](https://github.com/3scale/APIcast/pull/1318) [THREESCALE-7906](https://issues.redhat.com/browse/THREESCALE-7906)
83+
- Fixed dirty context [PR #1328](https://github.com/3scale/APIcast/pull/1328) [THREESCALE-8000](https://issues.redhat.com/browse/THREESCALE-8000) [THREESCALE-8007](https://issues.redhat.com/browse/THREESCALE-8007)
84+
- Fixed jwk alg confusion [PR #1329](https://github.com/3scale/APIcast/pull/1329) [THREESCALE-8249](https://issues.redhat.com/browse/THREESCALE-8249)
85+
- Fixed issue with resolving target server hostnames to IP when using CONNECT method [PR #1323](https://github.com/3scale/APIcast/pull/1323) [THREESCALE-7967](https://issues.redhat.com/browse/THREESCALE-7967)
86+
- Fixed issue with resolving target server hostnames to IPs when forwarding requests through http/s proxy [PR #1323](https://github.com/3scale/APIcast/pull/1323) [THREESCALE-7967](https://issues.redhat.com/browse/THREESCALE-7967)
87+
- Fixed dirty context [PR #1328](https://github.com/3scale/APIcast/pull/1328) [THREESCALE-8000](https://issues.redhat.com/browse/THREESCALE-8000) [THREESCALE-8007](https://issues.redhat.com/browse/THREESCALE-8007) [THREESCALE-8252](https://issues.redhat.com/browse/THREESCALE-8252)
88+
- Fixed dirty context (part 2 of PR #1328) when tls termination policy is in the policy chain [PR #1333](https://github.com/3scale/APIcast/pull/1333)
89+
- Fixed NGINX filters policy error [PR #1339](https://github.com/3scale/APIcast/pull/1339) [THREESCALE-7349](https://issues.redhat.com/browse/THREESCALE-7349)
90+
- Fix to avoid uninitialized variables when request URI is too large [PR #1340](https://github.com/3scale/APIcast/pull/1340) [THREESCALE-7906](https://issues.redhat.com/browse/THREESCALE-7906)
91+
- Fixed issue where request path is stripped for proxied https requests [PR #1342](https://github.com/3scale/APIcast/pull/1342) [THREESCALE-8426](https://issues.redhat.com/browse/THREESCALE-8426)
92+
- Bumped liquid-lua to version 0.2.0-2 [PR #1369](https://github.com/3scale/APIcast/pull/1369) - includes: [THREESCALE-8483](https://issues.redhat.com/browse/THREESCALE-8483) and [THREESCALE-8484](https://issues.redhat.com/browse/THREESCALE-8484)
93+
- New /admin/api/account/proxy_configs endpoint for configuration loading [PR #1352](https://github.com/3scale/APIcast/pull/1352) [THREESCALE-8508](https://issues.redhat.com/browse/THREESCALE-8508)
7794

7895
### Added
7996

Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ clean: ## Remove all docker containers and images
244244
make -C $(PROJECT_PATH) -f $(MKFILE_PATH) clean-containers
245245
- docker rmi $(DEVEL_IMAGE) $(RUNTIME_IMAGE) apicast-runtime-image:latest apicast-development:latest --force
246246
- rm -rf luacov.stats*.out
247+
- rm -rf $(PROJECT_PATH)/t/servroot_*
247248
make -C $(PROJECT_PATH) -f $(MKFILE_PATH) clean-deps
248249

249250
doc/lua/index.html: $(shell find gateway/src -name '*.lua' 2>/dev/null) | lua_modules $(ROVER)

doc/parameters.md

-23
Original file line numberDiff line numberDiff line change
@@ -62,29 +62,6 @@ Double colon (`:`) separated list of environments (or paths) APIcast should load
6262
It can be used instead of `-e` or `---environment` parameter on the CLI and for example
6363
stored in the container image as default environment. Any value passed on the CLI overrides this variable.
6464

65-
### `APICAST_LOAD_SERVICES_WHEN_NEEDED`
66-
**Values:**
67-
- `true` or `1` for _true_
68-
- `false`, `0` or empty for _false_
69-
70-
**Default:** _false_
71-
72-
This option can be used when there are many services configured. However, its
73-
performance depends on additional factors such as the number of services, the
74-
latency between APIcast and the 3scale Admin Portal, the Time To Live (TTL) of
75-
the configuration, etc.
76-
77-
By default, APIcast loads all the services each time it downloads its
78-
configuration from the Admin Portal. With a large number of services, this could
79-
become problematic. When this option is enabled, the configurations are loaded
80-
lazily. APIcast will only load the ones configured for the host specified in the
81-
host header of the request.
82-
83-
Notes:
84-
- The caching defined by `APICAST_CONFIGURATION_CACHE` applies.
85-
- This option will be disabled when `APICAST_CONFIGURATION_LOADER` is `boot`.
86-
- Not compatible with `APICAST_PATH_ROUTING`.
87-
8865
### `APICAST_LOG_FILE`
8966

9067
**Default:** _stderr_

gateway/src/apicast/configuration_loader/remote_v2.lua

+105-97
Original file line numberDiff line numberDiff line change
@@ -100,17 +100,6 @@ local function service_config_endpoint(portal_endpoint, service_id, env, version
100100
)
101101
end
102102

103-
local function endpoint_for_services_with_host(portal_endpoint, env, host)
104-
local query_args = encode_args({ host = host, version = "latest" })
105-
106-
return format(
107-
"%s/admin/api/account/proxy_configs/%s.json?%s",
108-
portal_endpoint,
109-
env,
110-
query_args
111-
)
112-
end
113-
114103
local function parse_resp_body(self, resp_body)
115104
local ok, res = pcall(cjson.decode, resp_body)
116105
if not ok then return nil, res end
@@ -148,72 +137,62 @@ local function parse_resp_body(self, resp_body)
148137
return cjson.encode(config)
149138
end
150139

151-
local function load_just_the_services_needed()
152-
return resty_env.enabled('APICAST_LOAD_SERVICES_WHEN_NEEDED') and
153-
resty_env.value('APICAST_CONFIGURATION_LOADER') == 'lazy'
140+
local function is_service_filter_by_url_set()
141+
if resty_env.value('APICAST_SERVICES_FILTER_BY_URL') then
142+
return true
143+
else
144+
return false
145+
end
154146
end
155147

156-
-- When the APICAST_LOAD_SERVICES_WHEN_NEEDED is enabled, but the config loader
157-
-- is boot, APICAST_LOAD_SERVICES_WHEN_NEEDED is going to be ignored. But in
158-
-- that case, env refers to a host and we need to reset it to pick the env
159-
-- again.
160-
local function reset_env()
161-
return resty_env.enabled('APICAST_LOAD_SERVICES_WHEN_NEEDED') and
162-
resty_env.value('APICAST_CONFIGURATION_LOADER') == 'boot'
148+
local function is_service_list_set()
149+
if resty_env.value('APICAST_SERVICES_LIST') then
150+
return true
151+
else
152+
return false
153+
end
163154
end
164155

165-
function _M:index(host)
156+
local function is_service_version_set()
157+
local vars = resty_env.list()
158+
for n, v in pairs(vars) do
159+
if match(n, "APICAST_SERVICE_\\d+_CONFIGURATION_VERSION") and v and v ~= '' then
160+
return true
161+
end
162+
end
163+
return false
164+
end
165+
166+
-- Returns a table that represents paths and query parameters for the current endpoint:
167+
-- http://${THREESCALE_PORTAL_ENDPOINT}/<env>.json?host=host
168+
-- http://${THREESCALE_PORTAL_ENDPOINT}/admin/api/account/proxy_configs/<env>.json?host=host&version=version
169+
local function configuration_endpoint_params(env, host, portal_endpoint_path)
170+
return portal_endpoint_path and {path = env, args = {host = host}}
171+
or {path = '/admin/api/account/proxy_configs/' .. env, args = {host = host, version = "latest"} }
172+
end
173+
174+
function _M:index_per_service()
166175
local http_client = self.http_client
167176

168177
if not http_client then
169178
return nil, 'not initialized'
170179
end
171180

172-
local path = self.path
173-
174-
if not path then
175-
return nil, 'wrong endpoint url'
176-
end
177-
178181
local env = resty_env.value('THREESCALE_DEPLOYMENT_ENV')
179182

180183
if not env then
181184
return nil, 'missing environment'
182185
end
183186

184-
local url = resty_url.join(self.endpoint, env .. '.json?' .. encode_args({ host = host }))
185-
local res, err = http_client.get(url)
187+
local configs = { services = array(), oidc = array() }
188+
189+
local res, err = self:services()
186190

187191
if not res and err then
188-
ngx.log(ngx.DEBUG, 'index get error: ', err, ' url: ', url)
192+
ngx.log(ngx.WARN, 'failed to get list of services: ', err, ' url: ', err.url)
189193
return nil, err
190194
end
191195

192-
ngx.log(ngx.DEBUG, 'index get status: ', res.status, ' url: ', url)
193-
194-
if res.status == 200 then
195-
return parse_resp_body(self, res.body)
196-
else
197-
return nil, 'invalid status'
198-
end
199-
end
200-
201-
function _M:load_configs_for_env_and_host(env, host)
202-
local url = endpoint_for_services_with_host(self.endpoint, env, host)
203-
204-
local response = self.http_client.get(url)
205-
206-
if response.status == 200 then
207-
return parse_resp_body(self, response.body)
208-
else
209-
ngx.log(ngx.ERR, 'failed to load proxy configs')
210-
return false
211-
end
212-
end
213-
214-
function _M:call(environment)
215-
local load_just_for_host = load_just_the_services_needed()
216-
217196
local service_regexp_filter = resty_env.value("APICAST_SERVICES_FILTER_BY_URL")
218197
if service_regexp_filter then
219198
local _, err = match("", service_regexp_filter, 'oj')
@@ -223,73 +202,103 @@ function _M:call(environment)
223202
end
224203
end
225204

226-
if self == _M or not self then
227-
local host = environment
228-
local m = _M.new()
229-
local ret, err = m:index(host)
230-
231-
if ret then
232-
return ret, err
233-
end
205+
local config
206+
for _, object in ipairs(res) do
207+
config, err = self:config(object.service, env, 'latest', service_regexp_filter)
234208

235-
if load_just_for_host then
236-
return m:call(host)
209+
if config then
210+
insert(configs, config)
237211
else
238-
return m:call()
212+
ngx.log(ngx.INFO, 'could not get configuration for service ', object.service.id, ': ', err)
239213
end
240214
end
241215

242-
local http_client = self.http_client
216+
for i, conf in ipairs(configs) do
217+
configs.services[i] = conf.content
243218

244-
if not http_client then
245-
return nil, 'not initialized'
246-
end
219+
-- Assign false instead of nil to avoid sparse arrays. cjson raises an
220+
-- error by default when converting sparse arrays.
221+
configs.oidc[i] = conf.oidc or false
247222

248-
if load_just_for_host then
249-
return self:load_configs_for_env_and_host(resty_env.value('THREESCALE_DEPLOYMENT_ENV'), environment)
223+
configs[i] = nil
250224
end
251225

252-
local env = environment or resty_env.value('THREESCALE_DEPLOYMENT_ENV')
226+
return cjson.encode(configs)
227+
end
228+
229+
function _M:index(host)
230+
local http_client = self.http_client
253231

254-
if reset_env() then
255-
env = resty_env.value('THREESCALE_DEPLOYMENT_ENV')
232+
if not http_client then
233+
return nil, 'not initialized'
256234
end
257235

236+
local proxy_config_path = self.path
237+
local env = resty_env.value('THREESCALE_DEPLOYMENT_ENV')
238+
258239
if not env then
259240
return nil, 'missing environment'
260241
end
261242

262-
local configs = { services = array(), oidc = array() }
263-
264-
local res, err = self:services()
243+
local endpoint_params = configuration_endpoint_params(env, host, proxy_config_path)
244+
local base_url = resty_url.join(self.endpoint, endpoint_params.path .. '.json')
245+
local query_args = encode_args(endpoint_params.args) ~= '' and '?'..encode_args(endpoint_params.args)
246+
local url = query_args and base_url..query_args or base_url
265247

266-
if not res and err then
267-
ngx.log(ngx.WARN, 'failed to get list of services: ', err, ' url: ', err.url)
248+
local res, err = http_client.get(url)
249+
if res and res.status == 200 and res.body then
250+
ngx.log(ngx.DEBUG, 'index downloaded config from url: ', url)
251+
return parse_resp_body(self, res.body)
252+
elseif not res and err then
253+
ngx.log(ngx.DEBUG, 'index get error: ', err, ' url: ', url)
268254
return nil, err
269255
end
270256

271-
local config
272-
for _, object in ipairs(res) do
273-
config, err = self:config(object.service, env, 'latest', service_regexp_filter)
257+
ngx.log(ngx.DEBUG, 'index get status: ', res.status, ' url: ', url)
274258

275-
if config then
276-
insert(configs, config)
277-
else
278-
ngx.log(ngx.INFO, 'could not get configuration for service ', object.service.id, ': ', err)
279-
end
259+
return nil, 'invalid status'
260+
end
261+
262+
function _M:call(host)
263+
if self == _M or not self then
264+
local m = _M.new()
265+
return m:call(host)
280266
end
281267

282-
for i, conf in ipairs(configs) do
283-
configs.services[i] = conf.content
268+
local proxy_config_path = self.path
284269

285-
-- Assign false instead of nil to avoid sparse arrays. cjson raises an
286-
-- error by default when converting sparse arrays.
287-
configs.oidc[i] = conf.oidc or false
270+
-- uses proxy config specific endpoints unless APICAST_SERVICE_%s_CONFIGURATION_VERSION
271+
-- When specific version for a specific service is defined,
272+
-- loading services one by one is required
273+
--
274+
-- APICAST_SERVICE_%s_CONFIGURATION_VERSION does not work then THREESCALE_PORTAL_ENDPOINT
275+
-- points to master (the API does not allow it), hence error is returned
288276

289-
configs[i] = nil
277+
local use_service_version = is_service_version_set()
278+
local use_service_list = is_service_list_set()
279+
local use_service_filter_by_url = is_service_filter_by_url_set()
280+
281+
if use_service_version and proxy_config_path then
282+
return nil, 'APICAST_SERVICE_%s_CONFIGURATION_VERSION cannot be used when proxy config path is provided'
290283
end
291284

292-
return cjson.encode(configs)
285+
if use_service_list and proxy_config_path then
286+
return nil, 'APICAST_SERVICES_LIST cannot be used when proxy config path is provided'
287+
end
288+
289+
if use_service_filter_by_url and proxy_config_path then
290+
return nil, 'APICAST_SERVICES_FILTER_BY_URL cannot be used when proxy config path is provided'
291+
end
292+
293+
if use_service_version then
294+
return self:index_per_service()
295+
elseif use_service_list then
296+
return self:index_per_service()
297+
elseif use_service_filter_by_url then
298+
return self:index_per_service()
299+
else
300+
return self:index(host)
301+
end
293302
end
294303

295304
local services_subset = function()
@@ -401,5 +410,4 @@ function _M:config(service, environment, version, service_regexp_filter)
401410
end
402411
end
403412

404-
405413
return _M

0 commit comments

Comments
 (0)