1616--
1717
1818local ngx = ngx
19+ local unpack = unpack
1920local ipairs = ipairs
2021local pairs = pairs
2122local string = string
@@ -25,7 +26,6 @@ local os = os
2526local error = error
2627local pcall = pcall
2728local setmetatable = setmetatable
28- local type = type
2929local is_http = ngx .config .subsystem == " http"
3030local process = require (" ngx.process" )
3131local core = require (" apisix.core" )
@@ -42,6 +42,7 @@ local endpoint_lrucache = core.lrucache.new({
4242})
4343
4444local endpoint_buffer = {}
45+ local kubernetes_service_name_label = " kubernetes.io/service-name"
4546
4647local function sort_nodes_cmp (left , right )
4748 if left .host ~= right .host then
@@ -51,69 +52,133 @@ local function sort_nodes_cmp(left, right)
5152 return left .port < right .port
5253end
5354
54- local function on_endpoint_slices_modified (handle , endpoint , operate )
55- if handle .namespace_selector and
56- not handle :namespace_selector (endpoint .metadata .namespace ) then
57- return
55+ local function update_endpoint_slices_cache (handle , endpoint_key , slice , slice_name )
56+ if not handle .endpoint_slices_cache [endpoint_key ] then
57+ handle .endpoint_slices_cache [endpoint_key ] = {}
5858 end
59+ local endpoint_slices = handle .endpoint_slices_cache [endpoint_key ]
60+ endpoint_slices [slice_name ] = slice
61+ end
5962
60- core .log .debug (core .json .delay_encode (endpoint ))
61- core .table .clear (endpoint_buffer )
62-
63- local endpointslices = endpoint .endpoints
64- if type (endpointslices ) == " table" then
65- for _ , endpointslice in ipairs (endpointslices ) do
66- if endpointslice .addresses then
67- local addresses = endpointslice .addresses
68- for _ , port in ipairs (endpoint .ports or {}) do
69- local port_name
70- if port .name then
71- port_name = port .name
72- elseif port .targetPort then
73- port_name = tostring (port .targetPort )
74- else
75- port_name = tostring (port .port )
76- end
77-
78- if endpointslice .conditions and endpointslice .conditions .ready then
79- local nodes = endpoint_buffer [port_name ]
80- if nodes == nil then
81- nodes = core .table .new (0 , # endpointslices * # addresses )
82- endpoint_buffer [port_name ] = nodes
83- end
84-
85- for _ , address in ipairs (addresses ) do
86- core .table .insert (nodes , {
87- host = address .ip ,
88- port = port .port ,
89- weight = handle .default_weight
90- })
91- end
92- end
93- end
63+ local function get_endpoints_from_cache (handle , endpoint_key )
64+ local endpoint_slices = handle .endpoint_slices_cache [endpoint_key ] or {}
65+ local endpoints = {}
66+ for _ , endpoint_slice in pairs (endpoint_slices ) do
67+ for port , targets in pairs (endpoint_slice ) do
68+ if not endpoints [port ] then
69+ endpoints [port ] = core .table .new (0 , # targets )
9470 end
71+ core .table .insert_tail (endpoints [port ], unpack (targets ))
9572 end
9673 end
9774
98- for _ , ports in pairs (endpoint_buffer ) do
99- for _ , nodes in pairs (ports ) do
100- core .table .sort (nodes , sort_nodes_cmp )
101- end
102- end
103- local endpoint_key = endpoint .metadata .namespace .. " /" .. endpoint .metadata .name
104- local endpoint_content = core .json .encode (endpoint_buffer , true )
105- local endpoint_version = ngx .crc32_long (endpoint_content )
75+ return endpoints
76+ end
10677
78+ local function update_endpoint_dict (handle , endpoints , endpoint_key )
79+ local endpoint_content = core .json .encode (endpoints , true )
80+ local endpoint_version = ngx .crc32_long (endpoint_content )
10781 local _ , err
10882 _ , err = handle .endpoint_dict :safe_set (endpoint_key .. " #version" , endpoint_version )
10983 if err then
110- core .log .error (" set endpoint version into discovery DICT failed, " , err )
111- return
84+ return false , " set endpoint version into discovery DICT failed, " .. err
11285 end
11386 _ , err = handle .endpoint_dict :safe_set (endpoint_key , endpoint_content )
11487 if err then
115- core .log .error (" set endpoint into discovery DICT failed, " , err )
11688 handle .endpoint_dict :delete (endpoint_key .. " #version" )
89+ return false , " set endpoint into discovery DICT failed, " .. err
90+ end
91+
92+ return true
93+ end
94+
95+ local function validate_endpoint_slice (endpoint_slice )
96+ if not endpoint_slice .metadata then
97+ return false , " endpoint_slice has no metadata, endpointSlice: "
98+ .. core .json .encode (endpoint_slice )
99+ end
100+ if not endpoint_slice .metadata .name then
101+ return false , " endpoint_slice has no metadata.name, endpointSlice: "
102+ .. core .json .encode (endpoint_slice )
103+ end
104+ if not endpoint_slice .metadata .namespace then
105+ return false , " endpoint_slice has no metadata.namespace, endpointSlice: "
106+ .. core .json .encode (endpoint_slice )
107+ end
108+ if not endpoint_slice .metadata .labels
109+ or not endpoint_slice .metadata .labels [kubernetes_service_name_label ] then
110+ return false , " endpoint_slice has no service-name, endpointSlice: "
111+ .. core .json .encode (endpoint_slice )
112+ end
113+
114+ return true
115+ end
116+
117+ local function on_endpoint_slices_modified (handle , endpoint_slice , operate )
118+ local ok , err = validate_endpoint_slice (endpoint_slice )
119+ if not ok then
120+ core .log .error (" endpoint_slice validation fail: " , err )
121+ return
122+ end
123+ if handle .namespace_selector and
124+ not handle :namespace_selector (endpoint_slice .metadata .namespace ) then
125+ return
126+ end
127+
128+ core .log .debug (" get endpoint_slice: " , core .json .delay_encode (endpoint_slice ))
129+ -- record nodes to every port in service
130+ local port_to_nodes = {}
131+
132+ local slice_endpoints = endpoint_slice .endpoints
133+ if not slice_endpoints or slice_endpoints == ngx .null then
134+ slice_endpoints = {}
135+ end
136+
137+ for _ , endpoint in ipairs (slice_endpoints ) do
138+ if endpoint .addresses
139+ and endpoint .conditions
140+ and endpoint .conditions .ready then
141+ local addresses = endpoint .addresses
142+ for _ , port in ipairs (endpoint_slice .ports or {}) do
143+ local port_name
144+ if port .name then
145+ port_name = port .name
146+ elseif port .targetPort then
147+ port_name = tostring (port .targetPort )
148+ else
149+ port_name = tostring (port .port )
150+ end
151+
152+ local nodes = port_to_nodes [port_name ]
153+ if nodes == nil then
154+ nodes = core .table .new (0 , # slice_endpoints * # addresses )
155+ port_to_nodes [port_name ] = nodes
156+ end
157+
158+ for _ , ip in ipairs (addresses ) do
159+ core .table .insert (nodes , {
160+ host = ip ,
161+ port = port .port ,
162+ weight = handle .default_weight
163+ })
164+ end
165+ end
166+ end
167+ end
168+
169+ local endpoint_key = endpoint_slice .metadata .namespace
170+ .. " /" .. endpoint_slice .metadata .labels [kubernetes_service_name_label ]
171+ update_endpoint_slices_cache (handle , endpoint_key , port_to_nodes , endpoint_slice .metadata .name )
172+
173+ local cached_endpoints = get_endpoints_from_cache (handle , endpoint_key )
174+ for _ , nodes in pairs (cached_endpoints ) do
175+ core .table .sort (nodes , sort_nodes_cmp )
176+ end
177+
178+ local ok , err = update_endpoint_dict (handle , cached_endpoints , endpoint_key )
179+ if not ok then
180+ core .log .error (" failed to update endpoint dict for endpoint: " , endpoint_key ,
181+ " , err: " , err )
117182 return
118183 end
119184 if operate == " list" then
@@ -122,6 +187,36 @@ local function on_endpoint_slices_modified(handle, endpoint, operate)
122187 end
123188end
124189
190+ local function on_endpoint_slices_deleted (handle , endpoint_slice )
191+ local ok , err = validate_endpoint_slice (endpoint_slice )
192+ if not ok then
193+ core .log .error (" endpoint_slice validation fail: " , err )
194+ return
195+ end
196+
197+ if handle .namespace_selector and
198+ not handle :namespace_selector (endpoint_slice .metadata .namespace ) then
199+ return
200+ end
201+
202+ core .log .debug (" delete endpoint_slice: " , core .json .delay_encode (endpoint_slice ))
203+
204+ local endpoint_key = endpoint_slice .metadata .namespace
205+ .. " /" .. endpoint_slice .metadata .labels [kubernetes_service_name_label ]
206+ update_endpoint_slices_cache (handle , endpoint_key , nil , endpoint_slice .metadata .name )
207+
208+ local cached_endpoints = get_endpoints_from_cache (handle , endpoint_key )
209+ for _ , nodes in pairs (cached_endpoints ) do
210+ core .table .sort (nodes , sort_nodes_cmp )
211+ end
212+
213+ ok , err = update_endpoint_dict (handle , cached_endpoints , endpoint_key )
214+ if not ok then
215+ core .log .error (" failed to update endpoint dict for endpoint: " , endpoint_key ,
216+ " , err: " , err )
217+ end
218+ end
219+
125220local function on_endpoint_modified (handle , endpoint , operate )
126221 if handle .namespace_selector and
127222 not handle :namespace_selector (endpoint .metadata .namespace ) then
@@ -162,26 +257,16 @@ local function on_endpoint_modified(handle, endpoint, operate)
162257 end
163258 end
164259
165- for _ , ports in pairs (endpoint_buffer ) do
166- for _ , nodes in pairs (ports ) do
167- core .table .sort (nodes , sort_nodes_cmp )
168- end
260+
261+ for _ , nodes in pairs (endpoint_buffer ) do
262+ core .table .sort (nodes , sort_nodes_cmp )
169263 end
170264
171265 local endpoint_key = endpoint .metadata .namespace .. " /" .. endpoint .metadata .name
172- local endpoint_content = core .json .encode (endpoint_buffer , true )
173- local endpoint_version = ngx .crc32_long (endpoint_content )
174-
175- local _ , err
176- _ , err = handle .endpoint_dict :safe_set (endpoint_key .. " #version" , endpoint_version )
177- if err then
178- core .log .error (" set endpoint version into discovery DICT failed, " , err )
179- return
180- end
181- _ , err = handle .endpoint_dict :safe_set (endpoint_key , endpoint_content )
182- if err then
183- core .log .error (" set endpoint into discovery DICT failed, " , err )
184- handle .endpoint_dict :delete (endpoint_key .. " #version" )
266+ local ok , err = update_endpoint_dict (handle , endpoint_buffer , endpoint_key )
267+ if not ok then
268+ core .log .error (" failed to update endpoint dict for endpoint: " , endpoint_key ,
269+ " , err: " , err )
185270 return
186271 end
187272 if operate == " list" then
207292local function pre_list (handle )
208293 handle .current_keys_hash = {}
209294 handle .existing_keys = handle .endpoint_dict :get_keys (0 )
295+ if handle .endpoint_slices_cache then
296+ handle .endpoint_slices_cache = {}
297+ end
210298end
211299
212300
@@ -499,11 +587,14 @@ local function single_mode_init(conf)
499587 if conf .watch_endpoint_slices then
500588 endpoints_informer .on_added = on_endpoint_slices_modified
501589 endpoints_informer .on_modified = on_endpoint_slices_modified
590+ endpoints_informer .on_deleted = on_endpoint_slices_deleted
591+ endpoints_informer .endpoint_slices_cache = {}
502592 else
503593 endpoints_informer .on_added = on_endpoint_modified
504594 endpoints_informer .on_modified = on_endpoint_modified
595+ endpoints_informer .on_deleted = on_endpoint_deleted
505596 end
506- endpoints_informer . on_deleted = on_endpoint_deleted
597+
507598 endpoints_informer .pre_list = pre_list
508599 endpoints_informer .post_list = post_list
509600
@@ -605,11 +696,14 @@ local function multiple_mode_init(confs)
605696 if conf .watch_endpoint_slices then
606697 endpoints_informer .on_added = on_endpoint_slices_modified
607698 endpoints_informer .on_modified = on_endpoint_slices_modified
699+ endpoints_informer .on_deleted = on_endpoint_slices_deleted
700+ endpoints_informer .endpoint_slices_cache = {}
608701 else
609702 endpoints_informer .on_added = on_endpoint_modified
610703 endpoints_informer .on_modified = on_endpoint_modified
704+ endpoints_informer .on_deleted = on_endpoint_deleted
611705 end
612- endpoints_informer . on_deleted = on_endpoint_deleted
706+
613707 endpoints_informer .pre_list = pre_list
614708 endpoints_informer .post_list = post_list
615709
0 commit comments