You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by ba...@apache.org on 2025/08/15 09:31:07 UTC
(apisix) branch master updated: fix: enable issue of endpointslices for k8s discovery (#11654)
This is an automated email from the ASF dual-hosted git repository.
baoyuan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new a9f184c97 fix: enable issue of endpointslices for k8s discovery (#11654)
a9f184c97 is described below
commit a9f184c9779f38f601346cefb5e830198c0dddba
Author: Sachin Maurya <57...@users.noreply.github.com>
AuthorDate: Fri Aug 15 15:01:01 2025 +0530
fix: enable issue of endpointslices for k8s discovery (#11654)
---
.github/workflows/kubernetes-ci.yml | 2 +
apisix/discovery/kubernetes/init.lua | 59 +++++++++--------
docs/en/latest/discovery/kubernetes.md | 13 ++--
docs/zh/latest/discovery/kubernetes.md | 13 ++--
t/kubernetes/configs/account.yaml | 5 +-
.../configs/{account.yaml => endpointslices.yaml} | 59 +++++++++++------
t/kubernetes/discovery/kubernetes3.t | 76 +++++++++++++++++-----
7 files changed, 149 insertions(+), 78 deletions(-)
diff --git a/.github/workflows/kubernetes-ci.yml b/.github/workflows/kubernetes-ci.yml
index 16f334372..06554db58 100644
--- a/.github/workflows/kubernetes-ci.yml
+++ b/.github/workflows/kubernetes-ci.yml
@@ -53,6 +53,8 @@ jobs:
kubectl apply -f ./t/kubernetes/configs/endpoint.yaml
+ kubectl apply -f ./t/kubernetes/configs/endpointslices.yaml
+
KUBERNETES_CLIENT_TOKEN_CONTENT=$(kubectl get secrets | grep apisix-test | awk '{system("kubectl get secret -o jsonpath={.data.token} "$1" | base64 --decode")}')
KUBERNETES_CLIENT_TOKEN_DIR="/tmp/var/run/secrets/kubernetes.io/serviceaccount"
diff --git a/apisix/discovery/kubernetes/init.lua b/apisix/discovery/kubernetes/init.lua
index 39fa69e58..695a9dd7f 100644
--- a/apisix/discovery/kubernetes/init.lua
+++ b/apisix/discovery/kubernetes/init.lua
@@ -25,6 +25,7 @@ local os = os
local error = error
local pcall = pcall
local setmetatable = setmetatable
+local type = type
local is_http = ngx.config.subsystem == "http"
local process = require("ngx.process")
local core = require("apisix.core")
@@ -60,32 +61,34 @@ local function on_endpoint_slices_modified(handle, endpoint)
core.table.clear(endpoint_buffer)
local endpointslices = endpoint.endpoints
- for _, endpointslice in ipairs(endpointslices or {}) do
- if endpointslice.addresses then
- local addresses = endpointslices.addresses
- for _, port in ipairs(endpoint.ports or {}) do
- local port_name
- if port.name then
- port_name = port.name
- elseif port.targetPort then
- port_name = tostring(port.targetPort)
- else
- port_name = tostring(port.port)
- end
-
- if endpointslice.conditions and endpointslice.condition.ready then
- local nodes = endpoint_buffer[port_name]
- if nodes == nil then
- nodes = core.table.new(0, #endpointslices * #addresses)
- endpoint_buffer[port_name] = nodes
+ if type(endpointslices) == "table" then
+ for _, endpointslice in ipairs(endpointslices) do
+ if endpointslice.addresses then
+ local addresses = endpointslice.addresses
+ for _, port in ipairs(endpoint.ports or {}) do
+ local port_name
+ if port.name then
+ port_name = port.name
+ elseif port.targetPort then
+ port_name = tostring(port.targetPort)
+ else
+ port_name = tostring(port.port)
end
- for _, address in ipairs(endpointslices.addresses) do
- core.table.insert(nodes, {
- host = address.ip,
- port = port.port,
- weight = handle.default_weight
- })
+ if endpointslice.conditions and endpointslice.conditions.ready then
+ local nodes = endpoint_buffer[port_name]
+ if nodes == nil then
+ nodes = core.table.new(0, #endpointslices * #addresses)
+ endpoint_buffer[port_name] = nodes
+ end
+
+ for _, address in ipairs(addresses) do
+ core.table.insert(nodes, {
+ host = address.ip,
+ port = port.port,
+ weight = handle.default_weight
+ })
+ end
end
end
end
@@ -448,7 +451,7 @@ local function single_mode_init(conf)
local default_weight = conf.default_weight
local endpoints_informer, err
- if conf.watch_endpoint_slices_schema then
+ if conf.watch_endpoint_slices then
endpoints_informer, err = informer_factory.new("discovery.k8s.io", "v1",
"EndpointSlice", "endpointslices", "")
else
@@ -462,7 +465,7 @@ local function single_mode_init(conf)
setup_namespace_selector(conf, endpoints_informer)
setup_label_selector(conf, endpoints_informer)
- if conf.watch_endpoint_slices_schema then
+ if conf.watch_endpoint_slices then
endpoints_informer.on_added = on_endpoint_slices_modified
endpoints_informer.on_modified = on_endpoint_slices_modified
else
@@ -554,7 +557,7 @@ local function multiple_mode_init(confs)
local default_weight = conf.default_weight
local endpoints_informer, err
- if conf.watch_endpoint_slices_schema then
+ if conf.watch_endpoint_slices then
endpoints_informer, err = informer_factory.new("discovery.k8s.io", "v1",
"EndpointSlice", "endpointslices", "")
else
@@ -568,7 +571,7 @@ local function multiple_mode_init(confs)
setup_namespace_selector(conf, endpoints_informer)
setup_label_selector(conf, endpoints_informer)
- if conf.watch_endpoint_slices_schema then
+ if conf.watch_endpoint_slices then
endpoints_informer.on_added = on_endpoint_slices_modified
endpoints_informer.on_modified = on_endpoint_slices_modified
else
diff --git a/docs/en/latest/discovery/kubernetes.md b/docs/en/latest/discovery/kubernetes.md
index f2000a144..728e2e332 100644
--- a/docs/en/latest/discovery/kubernetes.md
+++ b/docs/en/latest/discovery/kubernetes.md
@@ -302,7 +302,7 @@ A: The Kubernetes service discovery only uses privileged processes to [_List-Wat
**Q: What permissions do [_ServiceAccount_](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) require?**
-A: ServiceAccount requires the permissions of cluster-level [ get, list, watch ] endpoints resources, the declarative definition is as follows:
+A: ServiceAccount requires the permissions of cluster-level [ get, list, watch ] endpoints and endpointslices resources, the declarative definition is as follows:
```yaml
kind: ServiceAccount
@@ -315,11 +315,14 @@ metadata:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
- name: apisix-test
+ name: apisix-test
rules:
-- apiGroups: [ "" ]
- resources: [ endpoints,endpointslices ]
- verbs: [ get,list,watch ]
+ - apiGroups: [ "" ]
+ resources: [ endpoints]
+ verbs: [ get,list,watch ]
+ - apiGroups: [ "discovery.k8s.io" ]
+ resources: [ endpointslices ]
+ verbs: [ get,list,watch ]
---
apiVersion: rbac.authorization.k8s.io/v1
diff --git a/docs/zh/latest/discovery/kubernetes.md b/docs/zh/latest/discovery/kubernetes.md
index e10a53972..9e0b721cc 100644
--- a/docs/zh/latest/discovery/kubernetes.md
+++ b/docs/zh/latest/discovery/kubernetes.md
@@ -300,7 +300,7 @@ A: Kubernetes 服务发现只使用特权进程监听 Kubernetes Endpoints,然
**Q: [_ServiceAccount_](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) 需要的权限有哪些?**
-A: [_ServiceAccount_](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) 需要集群级 [ get,list,watch ] endpoints 资源的的权限,其声明式定义如下:
+A: [_ServiceAccount_](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) 需要集群级 [ get,list,watch ] endpoints 和 endpointslices 资源的的权限,其声明式定义如下:
```yaml
kind: ServiceAccount
@@ -313,11 +313,14 @@ metadata:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
- name: apisix-test
+ name: apisix-test
rules:
-- apiGroups: [ "" ]
- resources: [ endpoints,endpointslices ]
- verbs: [ get,list,watch ]
+ - apiGroups: [ "" ]
+ resources: [ endpoints]
+ verbs: [ get,list,watch ]
+ - apiGroups: [ "discovery.k8s.io" ]
+ resources: [ endpointslices ]
+ verbs: [ get,list,watch ]
---
apiVersion: rbac.authorization.k8s.io/v1
diff --git a/t/kubernetes/configs/account.yaml b/t/kubernetes/configs/account.yaml
index da7cf01f5..d461183b7 100644
--- a/t/kubernetes/configs/account.yaml
+++ b/t/kubernetes/configs/account.yaml
@@ -27,7 +27,10 @@ metadata:
name: apisix-test
rules:
- apiGroups: [ "" ]
- resources: [ endpoints ]
+ resources: [ endpoints]
+ verbs: [ get,list,watch ]
+ - apiGroups: [ "discovery.k8s.io" ]
+ resources: [ endpointslices ]
verbs: [ get,list,watch ]
---
apiVersion: rbac.authorization.k8s.io/v1
diff --git a/t/kubernetes/configs/account.yaml b/t/kubernetes/configs/endpointslices.yaml
similarity index 61%
copy from t/kubernetes/configs/account.yaml
copy to t/kubernetes/configs/endpointslices.yaml
index da7cf01f5..d22851233 100644
--- a/t/kubernetes/configs/account.yaml
+++ b/t/kubernetes/configs/endpointslices.yaml
@@ -15,30 +15,47 @@
# limitations under the License.
#
-kind: ServiceAccount
+kind: Namespace
apiVersion: v1
metadata:
- name: apisix-test
- namespace: default
+ name: ns-a
---
-kind: ClusterRole
-apiVersion: rbac.authorization.k8s.io/v1
+
+kind: EndpointSlice
+apiVersion: discovery.k8s.io/v1
+metadata:
+ name: epslice
+ namespace: ns-a
+addressType: IPv4
+endpoints: [ ]
+---
+
+kind: Namespace
+apiVersion: v1
+metadata:
+ name: ns-b
+---
+
+kind: EndpointSlice
+apiVersion: discovery.k8s.io/v1
metadata:
- name: apisix-test
-rules:
- - apiGroups: [ "" ]
- resources: [ endpoints ]
- verbs: [ get,list,watch ]
+ name: epslice
+ namespace: ns-b
+addressType: IPv4
+endpoints: [ ]
---
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
+
+kind: Namespace
+apiVersion: v1
metadata:
- name: apisix-test
-roleRef:
- apiGroup: rbac.authorization.k8s.io
- kind: ClusterRole
- name: apisix-test
-subjects:
- - kind: ServiceAccount
- name: apisix-test
- namespace: default
+ name: ns-c
+---
+
+kind: EndpointSlice
+apiVersion: discovery.k8s.io/v1
+metadata:
+ name: epslice
+ namespace: ns-c
+addressType: IPv4
+endpoints: [ ]
+---
diff --git a/t/kubernetes/discovery/kubernetes3.t b/t/kubernetes/discovery/kubernetes3.t
index 37a06b00f..60b224820 100644
--- a/t/kubernetes/discovery/kubernetes3.t
+++ b/t/kubernetes/discovery/kubernetes3.t
@@ -165,11 +165,11 @@ _EOC_
if op.op == "replace_endpointslices" then
method = "PATCH"
- path = "/apis/discovery.k8s.io/namespaces/" .. op.namespace .. "/endpointslices/" .. op.name
+ path = "/apis/discovery.k8s.io/v1/namespaces/" .. op.namespace .. "/endpointslices/" .. op.name
if #op.endpoints == 0 then
body = '[{"path":"/endpoints","op":"replace","value":[]}]'
else
- local t = { { op = "replace", path = "/endpoints", value = op.endpoints } }
+ local t = { { op = "replace", path = "/endpoints", value = op.endpoints }, { op = "replace", path = "/ports", value = op.ports } }
body = core.json.encode(t, true)
end
headers["Content-Type"] = "application/json-patch+json"
@@ -177,7 +177,7 @@ _EOC_
if op.op == "replace_labels" then
method = "PATCH"
- path = "/apis/discovery.k8s.io/namespaces/" .. op.namespace .. "/endpointslices/" .. op.name
+ path = "/apis/discovery.k8s.io/v1/namespaces/" .. op.namespace .. "/endpointslices/" .. op.name
local t = { { op = "replace", path = "/metadata/labels", value = op.labels } }
body = core.json.encode(t, true)
headers["Content-Type"] = "application/json-patch+json"
@@ -260,7 +260,7 @@ POST /operators
{
"op": "replace_endpointslices",
"namespace": "ns-a",
- "name": "ep",
+ "name": "epslice",
"endpoints": [
{
"addresses": [
@@ -289,7 +289,7 @@ POST /operators
],
"ports": [
{
- "name": "p",
+ "name": "p1",
"port": 5001
}
]
@@ -301,7 +301,7 @@ POST /operators
{
"op": "replace_endpointslices",
"namespace": "ns-b",
- "name": "ep",
+ "name": "epslice",
"endpoints": [
{
"addresses": [
@@ -330,7 +330,7 @@ POST /operators
],
"ports": [
{
- "name": "p",
+ "name": "p2",
"port": 5002
}
]
@@ -342,7 +342,7 @@ POST /operators
{
"op": "replace_endpointslices",
"namespace": "ns-c",
- "name": "ep",
+ "name": "epslice",
"endpoints": [
{
"addresses": [
@@ -362,7 +362,7 @@ POST /operators
"20.0.0.2"
],
"conditions": {
- "ready": true,
+ "ready": false,
"serving": true,
"terminating": false
},
@@ -371,7 +371,7 @@ POST /operators
],
"ports": [
{
- "name": "p",
+ "name": "p3",
"port": 5003
}
]
@@ -389,13 +389,13 @@ Content-type: application/json
--- request
GET /queries
[
- "first/ns-a/ep:p1","first/ns-a/ep:p2","first/ns-b/ep:p1","first/ns-b/ep:p2","first/ns-c/ep:5001","first/ns-c/ep:5002",
- "second/ns-a/ep:p1","second/ns-a/ep:p2","second/ns-b/ep:p1","second/ns-b/ep:p2","second/ns-c/ep:5001","second/ns-c/ep:5002"
+ "first/ns-a/epslice:p1","first/ns-a/epslice:p1","first/ns-b/epslice:p2","first/ns-b/epslice:p2","first/ns-c/epslice:p3","first/ns-c/epslice:p3",
+ "second/ns-a/epslice:p1","second/ns-a/epslice:p1","second/ns-b/epslice:p2","second/ns-b/epslice:p2","second/ns-c/epslice:p3","second/ns-c/epslice:p3"
]
--- more_headers
Content-type: application/json
--- response_body eval
-qr{ 0 0 2 2 0 0 0 0 2 2 0 0 }
+qr{ 2 2 2 2 2 2 2 2 2 2 2 2 }
@@ -428,17 +428,57 @@ discovery:
--- request
GET /queries
[
- "first/ns-a/ep:p1","first/ns-a/ep:p2","first/ns-b/ep:p1","first/ns-b/ep:p2","first/ns-c/ep:5001","first/ns-c/ep:5002",
- "second/ns-a/ep:p1","second/ns-a/ep:p2","second/ns-b/ep:p1","second/ns-b/ep:p2","second/ns-c/ep:5001","second/ns-c/ep:5002"
+ "first/ns-a/epslice:p1","first/ns-a/epslice:p1","first/ns-b/epslice:p2","first/ns-b/epslice:p2","first/ns-c/epslice:p3","first/ns-c/epslice:p3",
+ "second/ns-a/epslice:p1","second/ns-a/epslice:p1","second/ns-b/epslice:p2","second/ns-b/epslice:p2","second/ns-c/epslice:p3","second/ns-c/epslice:p3"
+]
+--- more_headers
+Content-type: application/json
+--- response_body eval
+qr{ 2 2 2 2 2 2 2 2 2 2 2 2 }
+
+
+
+=== TEST 4: use namespace selector equal
+--- yaml_config
+apisix:
+ node_listen: 1984
+deployment:
+ role: data_plane
+ role_data_plane:
+ config_provider: yaml
+discovery:
+ kubernetes:
+ - id: first
+ service:
+ host: ${KUBERNETES_SERVICE_HOST}
+ port: ${KUBERNETES_SERVICE_PORT}
+ client:
+ token_file: ${KUBERNETES_CLIENT_TOKEN_FILE}
+ watch_endpoint_slices: true
+ namespace_selector:
+ equal: ns-a
+ - id: second
+ service:
+ schema: "http"
+ host: "127.0.0.1"
+ port: "6445"
+ watch_endpoint_slices: true
+ client:
+ token: ${KUBERNETES_CLIENT_TOKEN}
+--- request
+GET /queries
+[
+ "first/ns-a/epslice:p1","first/ns-a/epslice:p1","first/ns-b/epslice:p2","first/ns-b/epslice:p2","first/ns-c/epslice:p3","first/ns-c/epslice:p3",
+ "second/ns-a/epslice:p1","second/ns-a/epslice:p1","second/ns-b/epslice:p2","second/ns-b/epslice:p2","second/ns-c/epslice:p3","second/ns-c/epslice:p3"
]
--- more_headers
Content-type: application/json
--- response_body eval
-qr{ 0 0 2 2 0 0 0 0 2 2 0 0 }
+qr{ 2 2 0 0 0 0 2 2 2 2 2 2 }
-=== TEST 4: test dump
+=== TEST 5: test dump
--- yaml_config eval: $::yaml_config
--- request
GET /dump
@@ -447,7 +487,7 @@ GET /dump
-=== TEST 5: test single mode dump
+=== TEST 6: test single mode dump
--- yaml_config eval: $::single_yaml_config
--- request
GET /dump