This is an automated email from the ASF dual-hosted git repository.
jooks pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new a44198595d [type:feat]:init ingress controller sofa (#5253)
a44198595d is described below
commit a44198595d00dd5d11bf867d9e8252022fc60abf
Author: Runqi Zhao <[email protected]>
AuthorDate: Wed Nov 1 10:41:34 2023 +0800
[type:feat]:init ingress controller sofa (#5253)
* [type:feat]:init ingress controller grpc
* [type:feat]:init ingress controller grpc
---
.github/workflows/integrated-test-k8s-ingress.yml | 1 +
.../shenyu-examples-sofa-service/k8s/ingress.yml | 87 ++++++
.../k8s/shenyu-examples-sofa.yml | 84 ++++++
.../k8s/shenyu-zookeeper.yml | 87 ++++++
shenyu-integrated-test/pom.xml | 1 +
.../Dockerfile | 29 ++
.../deploy/deploy-shenyu.yaml | 103 +++++++
.../deploy/kind-config.yaml | 36 +++
.../pom.xml | 156 ++++++++++
.../script/build_k8s_cluster.sh | 25 ++
.../script/healthcheck.sh | 36 +++
.../script/services.list | 18 ++
.../SofaIngressControllerIntegratedBootstrap.java | 36 +++
.../test/k8s/ingress/sofa/dto/SofaTestData.java | 51 ++++
.../src/main/resources/application-local.yml | 55 ++++
.../src/main/resources/application.yml | 18 ++
.../sofa/SofaPluginShareThreadPoolTest.java | 47 +++
.../apache/shenyu/k8s/common/IngressConstants.java | 33 +++
.../apache/shenyu/k8s/parser/IngressParser.java | 6 +-
.../org/apache/shenyu/k8s/parser/SofaParser.java | 330 +++++++++++++++++++++
.../shenyu/k8s/reconciler/IngressReconciler.java | 47 ++-
21 files changed, 1270 insertions(+), 16 deletions(-)
diff --git a/.github/workflows/integrated-test-k8s-ingress.yml
b/.github/workflows/integrated-test-k8s-ingress.yml
index 2839b2a37b..e8766fcbf9 100644
--- a/.github/workflows/integrated-test-k8s-ingress.yml
+++ b/.github/workflows/integrated-test-k8s-ingress.yml
@@ -34,6 +34,7 @@ jobs:
- shenyu-integrated-test-k8s-ingress-websocket
- shenyu-integrated-test-k8s-ingress-brpc
- shenyu-integrated-test-k8s-ingress-grpc
+ - shenyu-integrated-test-k8s-ingress-sofa
runs-on: ubuntu-latest
if: github.repository == 'apache/shenyu'
steps:
diff --git
a/shenyu-examples/shenyu-examples-sofa/shenyu-examples-sofa-service/k8s/ingress.yml
b/shenyu-examples/shenyu-examples-sofa/shenyu-examples-sofa-service/k8s/ingress.yml
new file mode 100644
index 0000000000..d26dc076e5
--- /dev/null
+++
b/shenyu-examples/shenyu-examples-sofa/shenyu-examples-sofa-service/k8s/ingress.yml
@@ -0,0 +1,87 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+apiVersion: v1
+kind: Service
+metadata:
+ name: sofa-find-id
+ namespace: shenyu-ingress
+ annotations:
+ kubernetes.io/ingress.class: shenyu
+ shenyu.apache.org/plugin-sofa-enabled: 'true'
+ shenyu.apache.org/plugin-sofa-app-name: sofa
+ shenyu.apache.org/plugin-context-path-path: /sofa
+ shenyu.apache.org/plugin-sofa-path: /sofa/findById
+ shenyu.apache.org/plugin-sofa-rpc-type: sofa
+ shenyu.apache.org/plugin-sofa-service-name:
org.apache.shenyu.examples.sofa.api.service.SofaSingleParamService
+ shenyu.apache.org/plugin-sofa-method-name: findById
+ shenyu.apache.org/plugin-sofa-params-type: java.lang.String
+ shenyu.apache.org/plugin-sofa-rpc-expand: |
+ {"loadbalance":"hash","retries":3,"timeout":-1}
+spec:
+ selector:
+ app: shenyu-examples-sofa
+ ports:
+ - port: 8888
+
+---
+
+apiVersion: v1
+kind: Service
+metadata:
+ name: sofa-find-all
+ namespace: shenyu-ingress
+ annotations:
+ kubernetes.io/ingress.class: shenyu
+ shenyu.apache.org/plugin-sofa-enabled: 'true'
+ shenyu.apache.org/plugin-context-path-path: /sofa
+ shenyu.apache.org/plugin-sofa-app-name: sofa
+ shenyu.apache.org/plugin-sofa-path: /sofa/findAll
+ shenyu.apache.org/plugin-sofa-rpc-type: sofa
+ shenyu.apache.org/plugin-sofa-service-name:
org.apache.shenyu.examples.sofa.api.service.SofaSingleParamService
+ shenyu.apache.org/plugin-sofa-method-name: findAll
+ shenyu.apache.org/plugin-sofa-rpc-expand: |
+ {"loadbalance":"hash","retries":3,"timeout":-1}
+spec:
+ selector:
+ app: shenyu-examples-sofa
+ ports:
+ - port: 8888
+
+---
+
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ namespace: shenyu-ingress
+ labels:
+ shenyu.apache.org/metadata-labels-1: sofa-find-id
+ shenyu.apache.org/metadata-labels-2: sofa-find-all
+ annotations:
+ kubernetes.io/ingress.class: shenyu
+ shenyu.apache.org/plugin-sofa-enabled: 'true'
+ shenyu.apache.org/zookeeper-register-address: shenyu-zk:2181
+ name: demo-ingress
+spec:
+ rules:
+ - http:
+ paths:
+ - backend:
+ service:
+ name: shenyu-examples-sofa
+ port:
+ number: 8888
+ path: /sofa/
+ pathType: Prefix
diff --git
a/shenyu-examples/shenyu-examples-sofa/shenyu-examples-sofa-service/k8s/shenyu-examples-sofa.yml
b/shenyu-examples/shenyu-examples-sofa/shenyu-examples-sofa-service/k8s/shenyu-examples-sofa.yml
new file mode 100644
index 0000000000..5bc7ee0e97
--- /dev/null
+++
b/shenyu-examples/shenyu-examples-sofa/shenyu-examples-sofa-service/k8s/shenyu-examples-sofa.yml
@@ -0,0 +1,84 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: shenyu-examples-sofa-deployment
+ namespace: shenyu-ingress
+ labels:
+ app: shenyu-examples-sofa
+ all: shenyu-examples-sofa
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: shenyu-examples-sofa
+ all: shenyu-examples-sofa
+ strategy: {}
+ template:
+ metadata:
+ labels:
+ app: shenyu-examples-sofa
+ all: shenyu-examples-sofa
+ spec:
+ containers:
+ - image: shenyu-examples-sofa
+ name: shenyu-examples-sofa
+ livenessProbe:
+ exec:
+ command:
+ - wget -q -O - http://localhost:8081/actuator/health | grep UP
|| exit 1
+ initialDelaySeconds: 10
+ failureThreshold: 3
+ timeoutSeconds: 2
+ env:
+ - name: shenyu.register.serverLists
+ value: http://shenyu-admin:9095
+ - name: com.alipay.sofa.rpc.registry-address
+ value: zookeeper://shenyu-zk:2181
+ ports:
+ - containerPort: 28011
+ - containerPort: 8888
+ imagePullPolicy: IfNotPresent
+ restartPolicy: Always
+status: {}
+
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: shenyu-examples-sofa-service
+ namespace: shenyu-ingress
+ labels:
+ app: shenyu-examples-sofa
+ all: shenyu-examples-sofa
+spec:
+ selector:
+ app: shenyu-examples-sofa
+ all: shenyu-examples-sofa
+ type: NodePort
+ ports:
+ - name: "8888"
+ port: 8888
+ targetPort: 8888
+ nodePort: 31111
+ - name: "28011"
+ port: 28011
+ targetPort: 28011
+ nodePort: 31112
+status:
+ loadBalancer: {}
diff --git
a/shenyu-examples/shenyu-examples-sofa/shenyu-examples-sofa-service/k8s/shenyu-zookeeper.yml
b/shenyu-examples/shenyu-examples-sofa/shenyu-examples-sofa-service/k8s/shenyu-zookeeper.yml
new file mode 100644
index 0000000000..8f4f1931c1
--- /dev/null
+++
b/shenyu-examples/shenyu-examples-sofa/shenyu-examples-sofa-service/k8s/shenyu-zookeeper.yml
@@ -0,0 +1,87 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: shenyu-ingress
+
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ namespace: shenyu-ingress
+ labels:
+ app: shenyu-zk
+ all: shenyu-examples-sofa
+ name: shenyu-zk
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: shenyu-zk
+ all: shenyu-examples-sofa
+ strategy: {}
+ template:
+ metadata:
+ namespace: shenyu-ingress
+ labels:
+ app: shenyu-zk
+ all: shenyu-examples-sofa
+ spec:
+ containers:
+ - image: zookeeper:latest
+ name: shenyu-zk
+ resources: {}
+ ports:
+ - containerPort: 2181
+ name: client
+ - containerPort: 2888
+ name: server
+ - containerPort: 3888
+ name: leader-election
+ - containerPort: 8080
+ name: website
+ restartPolicy: Always
+status: {}
+
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: shenyu-zk
+ namespace: shenyu-ingress
+ labels:
+ app: shenyu-zk
+ all: shenyu-examples-sofa
+spec:
+ type: NodePort
+ selector:
+ app: shenyu-zk
+ all: shenyu-examples-sofa
+ ports:
+ - name: "client"
+ port: 2181
+ targetPort: 2181
+ - name: "server"
+ port: 2888
+ targetPort: 2888
+ - name: "election"
+ port: 3888
+ targetPort: 3888
+ - name: "website"
+ port: 8080
+ targetPort: 8080
diff --git a/shenyu-integrated-test/pom.xml b/shenyu-integrated-test/pom.xml
index 81b29a3c96..dd767cf680 100644
--- a/shenyu-integrated-test/pom.xml
+++ b/shenyu-integrated-test/pom.xml
@@ -53,6 +53,7 @@
<module>shenyu-integrated-test-k8s-ingress-websocket</module>
<module>shenyu-integrated-test-k8s-ingress-brpc</module>
<module>shenyu-integrated-test-k8s-ingress-grpc</module>
+ <module>shenyu-integrated-test-k8s-ingress-sofa</module>
</modules>
<properties>
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/Dockerfile
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/Dockerfile
new file mode 100644
index 0000000000..db79bc5dbb
--- /dev/null
+++ b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/Dockerfile
@@ -0,0 +1,29 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+FROM openjdk:8-jre-alpine
+
+ENV APP_NAME shenyu-integrated-test-k8s-ingress-sofa
+ENV LOCAL_PATH /opt/${APP_NAME}
+
+RUN mkdir -p ${LOCAL_PATH}
+
+ADD target/${APP_NAME}.jar ${LOCAL_PATH}
+
+WORKDIR ${LOCAL_PATH}
+EXPOSE 9195
+
+CMD java -jar ${APP_NAME}.jar
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/deploy/deploy-shenyu.yaml
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/deploy/deploy-shenyu.yaml
new file mode 100644
index 0000000000..a38325bda2
--- /dev/null
+++
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/deploy/deploy-shenyu.yaml
@@ -0,0 +1,103 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+apiVersion: v1
+automountServiceAccountToken: true
+kind: ServiceAccount
+metadata:
+ name: shenyu-ingress-controller
+ namespace: shenyu-ingress
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: shenyu-ingress-controller
+ namespace: shenyu-ingress
+ labels:
+ app: shenyu-ingress-controller
+ all: shenyu-ingress-controller
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: shenyu-ingress-controller
+ template:
+ metadata:
+ labels:
+ app: shenyu-ingress-controller
+ spec:
+ containers:
+ - name: shenyu-ingress-controller
+ image: apache/shenyu-integrated-test-k8s-ingress-sofa:latest
+ ports:
+ - containerPort: 9195
+ imagePullPolicy: IfNotPresent
+ serviceAccountName: shenyu-ingress-controller
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: shenyu-ingress-controller
+ namespace: shenyu-ingress
+spec:
+ selector:
+ app: shenyu-ingress-controller
+ type: NodePort
+ ports:
+ - port: 9195
+ targetPort: 9195
+ nodePort: 30095
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: shenyu-ingress-controller
+ namespace: shenyu-ingress
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - namespaces
+ - services
+ - endpoints
+ - secrets
+ - pods
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - networking.k8s.io
+ resources:
+ - ingresses
+ verbs:
+ - get
+ - list
+ - watch
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: shenyu-ingress-controller
+ namespace: shenyu-ingress
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: shenyu-ingress-controller
+subjects:
+- kind: ServiceAccount
+ name: shenyu-ingress-controller
+ namespace: shenyu-ingress
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/deploy/kind-config.yaml
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/deploy/kind-config.yaml
new file mode 100644
index 0000000000..18b7da990e
--- /dev/null
+++
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/deploy/kind-config.yaml
@@ -0,0 +1,36 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+kind: Cluster
+apiVersion: kind.x-k8s.io/v1alpha4
+nodes:
+ - role: control-plane
+ kubeadmConfigPatches:
+ - |
+ kind: InitConfiguration
+ nodeRegistration:
+ kubeletExtraArgs:
+ node-labels: "ingress-ready=true"
+ extraPortMappings:
+ - containerPort: 80
+ hostPort: 80
+ protocol: TCP
+ - containerPort: 443
+ hostPort: 443
+ protocol: TCP
+ - containerPort: 30095
+ hostPort: 30095
+ protocol: TCP
\ No newline at end of file
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/pom.xml
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/pom.xml
new file mode 100644
index 0000000000..6c2052bb3f
--- /dev/null
+++ b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/pom.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.shenyu</groupId>
+ <artifactId>shenyu-integrated-test</artifactId>
+ <version>2.6.1-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>shenyu-integrated-test-k8s-ingress-sofa</artifactId>
+ <name>shenyu-integrated-test-k8s-ingress-sofa</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.alipay.sofa</groupId>
+ <artifactId>sofa-rpc-all</artifactId>
+ <version>5.7.6</version>
+ <exclusions>
+ <exclusion>
+ <groupId>net.jcip</groupId>
+ <artifactId>jcip-annotations</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>io.grpc</groupId>
+ <artifactId>grpc-core</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.curator</groupId>
+ <artifactId>curator-client</artifactId>
+ <version>4.0.1</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.curator</groupId>
+ <artifactId>curator-framework</artifactId>
+ <version>4.0.1</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.curator</groupId>
+ <artifactId>curator-recipes</artifactId>
+ <version>4.0.1</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.shenyu</groupId>
+ <artifactId>shenyu-spring-boot-starter-plugin-sofa</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.shenyu</groupId>
+ <artifactId>shenyu-spring-boot-starter-plugin-jwt</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.shenyu</groupId>
+ <artifactId>shenyu-integrated-test-common</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <!-- shenyu kubernetes controller begin -->
+ <dependency>
+ <groupId>org.apache.shenyu</groupId>
+ <artifactId>shenyu-spring-boot-starter-k8s</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <!-- shenyu kubernetes controller end -->
+ </dependencies>
+
+ <profiles>
+ <profile>
+ <id>it</id>
+ <properties>
+
<docker.buildArg.APP_NAME>shenyu-integrated-test-k8s-ingress-sofa</docker.buildArg.APP_NAME>
+
<docker.image.tag.repo>apache/shenyu-integrated-test-k8s-ingress-sofa</docker.image.tag.repo>
+ <docker.image.tag.tagName>latest</docker.image.tag.tagName>
+ </properties>
+ <build>
+ <finalName>shenyu-integrated-test-k8s-ingress-sofa</finalName>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <version>${spring-boot.version}</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>repackage</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+
<mainClass>org.apache.shenyu.integrated.test.k8s.ingress.sofa.SofaIngressControllerIntegratedBootstrap
+ </mainClass>
+ <executable>true</executable>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>io.fabric8</groupId>
+ <artifactId>docker-maven-plugin</artifactId>
+ <version>${docker-maven-plugin.version}</version>
+ <configuration>
+ <images>
+ <image>
+
<name>apache/shenyu-integrated-test-k8s-ingress-sofa</name>
+ <build>
+
<contextDir>${project.basedir}</contextDir>
+ </build>
+ </image>
+ </images>
+ </configuration>
+ <executions>
+ <execution>
+ <id>start</id>
+ <goals>
+ <goal>build</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <skipTests>false</skipTests>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
\ No newline at end of file
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/script/build_k8s_cluster.sh
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/script/build_k8s_cluster.sh
new file mode 100644
index 0000000000..4d5ceccabf
--- /dev/null
+++
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/script/build_k8s_cluster.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+kind load docker-image "shenyu-examples-sofa:latest"
+kind load docker-image "apache/shenyu-integrated-test-k8s-ingress-sofa:latest"
+kubectl apply -f
./shenyu-examples/shenyu-examples-sofa/shenyu-examples-sofa-service/k8s//shenyu-zookeeper.yml
+kubectl wait --for=condition=Ready pod -l app=shenyu-zk -n shenyu-ingress
+kubectl apply -f
./shenyu-examples/shenyu-examples-sofa/shenyu-examples-sofa-service/k8s/shenyu-examples-sofa.yml
+kubectl apply -f
./shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/deploy/deploy-shenyu.yaml
+kubectl apply -f
./shenyu-examples/shenyu-examples-sofa/shenyu-examples-sofa-service/k8s/ingress.yml
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/script/healthcheck.sh
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/script/healthcheck.sh
new file mode 100644
index 0000000000..22a76034ab
--- /dev/null
+++
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/script/healthcheck.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+PRGDIR=`dirname "$0"`
+for service in `grep -v -E "^$|^#" ${PRGDIR}/services.list`
+do
+ for loop in `seq 1 30`
+ do
+ status=`curl -o /dev/null -s -w %{http_code} $service`
+ echo -e "curl $service response $status"
+
+ if [ $status -eq 200 ]; then
+ break
+ fi
+
+ sleep 2
+ done
+done
+
+sleep 3
+echo -e "\n-------------------"
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/script/services.list
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/script/services.list
new file mode 100644
index 0000000000..ef0e742c04
--- /dev/null
+++
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/script/services.list
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+http://localhost:30095/actuator/health
+http://localhost:31184/actuator/health
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/src/main/java/org/apache/shenyu/integrated/test/k8s/ingress/sofa/SofaIngressControllerIntegratedBootstrap.java
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/src/main/java/org/apache/shenyu/integrated/test/k8s/ingress/sofa/SofaIngressControllerIntegratedBootstrap.java
new file mode 100644
index 0000000000..d6513ab6b4
--- /dev/null
+++
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/src/main/java/org/apache/shenyu/integrated/test/k8s/ingress/sofa/SofaIngressControllerIntegratedBootstrap.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.integrated.test.k8s.ingress.sofa;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * The type sofa integrated bootstrap.
+ */
+@SpringBootApplication
+public class SofaIngressControllerIntegratedBootstrap {
+ /**
+ * main method of app.
+ *
+ * @param args args
+ */
+ public static void main(final String[] args) {
+ SpringApplication.run(SofaIngressControllerIntegratedBootstrap.class);
+ }
+}
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/src/main/java/org/apache/shenyu/integrated/test/k8s/ingress/sofa/dto/SofaTestData.java
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/src/main/java/org/apache/shenyu/integrated/test/k8s/ingress/sofa/dto/SofaTestData.java
new file mode 100644
index 0000000000..3956aa699c
--- /dev/null
+++
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/src/main/java/org/apache/shenyu/integrated/test/k8s/ingress/sofa/dto/SofaTestData.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.integrated.test.k8s.ingress.sofa.dto;
+
+public class SofaTestData {
+
+ private String id;
+
+ private String name;
+
+ /**
+ * get id.
+ *
+ * @return id
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * get name.
+ *
+ * @return name
+ */
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String toString() {
+ return "SofaTest{"
+ + "id='" + id + '\''
+ + ", name='" + name + '\''
+ + '}';
+ }
+}
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/src/main/resources/application-local.yml
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/src/main/resources/application-local.yml
new file mode 100644
index 0000000000..643c44178e
--- /dev/null
+++
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/src/main/resources/application-local.yml
@@ -0,0 +1,55 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+server:
+ port: 9195
+ address: 0.0.0.0
+
+spring:
+ main:
+ allow-bean-definition-overriding: true
+ application:
+ name: shenyu-bootstrap
+
+management:
+ health:
+ defaults:
+ enabled: false
+
+shenyu:
+ switchConfig:
+ local: true
+ cross:
+ enabled: true
+ exclude:
+ enabled: true
+ paths:
+ - /favicon.ico
+ - /actuator/health
+ local:
+ enabled: true
+ sha512Key:
"BA3253876AED6BC22D4A6FF53D8406C6AD864195ED144AB5C87621B6C233B548BAEAE6956DF346EC8C17F5EA10F35EE3CBC514797ED7DDD3145464E2A0BAB413"
+ sharedPool:
+ enable: true
+
+logging:
+ level:
+ root: info
+ org.springframework.boot: info
+ org.apache.ibatis: info
+ org.apache.shenyu.bonuspoint: info
+ org.apache.shenyu.lottery: info
+ org.apache.shenyu: info
+
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/src/main/resources/application.yml
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/src/main/resources/application.yml
new file mode 100644
index 0000000000..393ad24df1
--- /dev/null
+++
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/src/main/resources/application.yml
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+spring:
+ profiles:
+ active: local
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/src/test/java/org/apache/shenyu/integrated/test/k8s/ingress/sofa/SofaPluginShareThreadPoolTest.java
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/src/test/java/org/apache/shenyu/integrated/test/k8s/ingress/sofa/SofaPluginShareThreadPoolTest.java
new file mode 100644
index 0000000000..a8bc54f8d3
--- /dev/null
+++
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/src/test/java/org/apache/shenyu/integrated/test/k8s/ingress/sofa/SofaPluginShareThreadPoolTest.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.integrated.test.k8s.ingress.sofa;
+
+import com.google.gson.reflect.TypeToken;
+import org.apache.shenyu.integrated.test.k8s.ingress.sofa.dto.SofaTestData;
+import org.apache.shenyu.integratedtest.common.AbstractPluginDataInit;
+import org.apache.shenyu.integratedtest.common.helper.HttpHelper;
+import org.hamcrest.core.Is;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+public class SofaPluginShareThreadPoolTest extends AbstractPluginDataInit {
+
+ private static final HttpHelper HTTP_HELPER = HttpHelper.INSTANCE;
+
+ @BeforeAll
+ public static void setup() {
+ HTTP_HELPER.setGatewayEndpoint("http://localhost:30095");
+ }
+
+ @Test
+ public void testHelloWorld() throws IOException {
+ SofaTestData response =
HttpHelper.INSTANCE.getFromGateway("/sofa/findById?id=1001", new
TypeToken<SofaTestData>() { }.getType());
+ assertThat(response.getName(), Is.is("hello world shenyu Sofa,
findById"));
+ assertThat(response.getId(), Is.is("1001"));
+ }
+}
diff --git
a/shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/common/IngressConstants.java
b/shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/common/IngressConstants.java
index e20cfa5143..1f8ef13e14 100644
---
a/shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/common/IngressConstants.java
+++
b/shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/common/IngressConstants.java
@@ -218,4 +218,37 @@ public class IngressConstants {
//The configuration key to specify the Grpc parameter type for the plugin,
in string
public static final String PLUGIN_GRPC_PARAMETER_TYPE =
"shenyu.apache.org/plugin-grpc-parameter-type";
+
+ //The configuration key to specify the Sofa application name for the
plugin, in string
+ public static final String PLUGIN_SOFA_APP_NAME =
"shenyu.apache.org/plugin-sofa-app-name";
+
+ //The configuration key to specify the Sofa service name for the plugin,
in string
+ public static final String PLUGIN_SOFA_SERVICE_NAME =
"shenyu.apache.org/plugin-sofa-service-name";
+
+ //The configuration key to specify the Sofa method name for the plugin, in
string
+ public static final String PLUGIN_SOFA_METHOD_NAME =
"shenyu.apache.org/plugin-sofa-method-name";
+
+ //The configuration key to specify the Sofa params type for the plugin, in
string
+ public static final String PLUGIN_SOFA_PARAMS_TYPE =
"shenyu.apache.org/plugin-sofa-params-type";
+
+ //The configuration key to specify the Sofa rpc type for the plugin, in
string
+ public static final String PLUGIN_SOFA_RPC_TYPE =
"shenyu.apache.org/plugin-sofa-rpc-type";
+
+ //The configuration key to specify the Sofa context path for the plugin,
in string
+ public static final String PLUGIN_SOFA_CONTEXT_PATH =
"shenyu.apache.org/plugin-sofa-context-path";
+
+ //The configuration key to specify the Sofa path for the plugin, in string
+ public static final String PLUGIN_SOFA_PATH =
"shenyu.apache.org/plugin-sofa-path";
+
+ //The configuration key to specify the Sofa enabled for the plugin, in
string
+ public static final String PLUGIN_SOFA_ENABLED =
"shenyu.apache.org/plugin-sofa-enabled";
+
+ //The configuration key to specify the Sofa rpc expand for the plugin, in
string
+ public static final String PLUGIN_SOFA_RPC_EXPAND =
"shenyu.apache.org/plugin-sofa-rpc-expand";
+
+ //The configuration key to specify the Sofa parameter type for the plugin,
in string
+ public static final String PLUGIN_SOFA_PARAMETER_TYPE =
"shenyu.apache.org/plugin-sofa-parameter-type";
+
+ //The configuration key to specify the Sofa parameter type for the plugin,
in string
+ public static final String PLUGIN_SOFA_RPC_EXT =
"shenyu.apache.org/plugin-sofa-rpc-ext";
}
diff --git
a/shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/parser/IngressParser.java
b/shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/parser/IngressParser.java
index 6d9f2b18ea..99c1c420d2 100644
---
a/shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/parser/IngressParser.java
+++
b/shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/parser/IngressParser.java
@@ -71,8 +71,9 @@ public class IngressParser implements
K8sResourceListParser<V1Ingress> {
boolean webSocketEnabled = getBooleanAnnotation(ingress,
IngressConstants.PLUGIN_WEB_SOCKET_ENABLED);
boolean brpcEnabled = getBooleanAnnotation(ingress,
IngressConstants.PLUGIN_BRPC_ENABLED);
boolean grpcEnabled = getBooleanAnnotation(ingress,
IngressConstants.PLUGIN_GRPC_ENABLED);
+ boolean sofaEnabled = getBooleanAnnotation(ingress,
IngressConstants.PLUGIN_SOFA_ENABLED);
- if (!dubboEnabled || !motanEnabled) {
+ if (!dubboEnabled || !motanEnabled || !sofaEnabled) {
contextPathParse(ingress, shenyuMemoryConfigList, coreV1Api);
}
if (dubboEnabled) {
@@ -93,6 +94,9 @@ public class IngressParser implements
K8sResourceListParser<V1Ingress> {
} else if (grpcEnabled) {
GrpcParser grpcParser = new GrpcParser(serviceLister,
endpointsLister);
shenyuMemoryConfigList.add(grpcParser.parse(ingress, coreV1Api));
+ } else if (sofaEnabled) {
+ SofaParser sofaParser = new SofaParser(serviceLister,
endpointsLister);
+ shenyuMemoryConfigList.add(sofaParser.parse(ingress, coreV1Api));
} else {
DivideIngressParser divideIngressParser = new
DivideIngressParser(serviceLister, endpointsLister);
shenyuMemoryConfigList.add(divideIngressParser.parse(ingress,
coreV1Api));
diff --git
a/shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/parser/SofaParser.java
b/shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/parser/SofaParser.java
new file mode 100644
index 0000000000..c8ff4a2067
--- /dev/null
+++
b/shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/parser/SofaParser.java
@@ -0,0 +1,330 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.k8s.parser;
+
+import io.kubernetes.client.informer.cache.Lister;
+import io.kubernetes.client.openapi.ApiException;
+import io.kubernetes.client.openapi.apis.CoreV1Api;
+import io.kubernetes.client.openapi.models.V1Endpoints;
+import io.kubernetes.client.openapi.models.V1HTTPIngressPath;
+import io.kubernetes.client.openapi.models.V1Ingress;
+import io.kubernetes.client.openapi.models.V1IngressBackend;
+import io.kubernetes.client.openapi.models.V1IngressRule;
+import io.kubernetes.client.openapi.models.V1IngressServiceBackend;
+import io.kubernetes.client.openapi.models.V1IngressTLS;
+import io.kubernetes.client.openapi.models.V1Secret;
+import io.kubernetes.client.openapi.models.V1Service;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.shenyu.common.config.ssl.SslCrtAndKeyStream;
+import org.apache.shenyu.common.dto.ConditionData;
+import org.apache.shenyu.common.dto.MetaData;
+import org.apache.shenyu.common.dto.RuleData;
+import org.apache.shenyu.common.dto.SelectorData;
+import org.apache.shenyu.common.dto.convert.rule.impl.SofaRuleHandle;
+import org.apache.shenyu.common.enums.LoadBalanceEnum;
+import org.apache.shenyu.common.enums.MatchModeEnum;
+import org.apache.shenyu.common.enums.OperatorEnum;
+import org.apache.shenyu.common.enums.ParamTypeEnum;
+import org.apache.shenyu.common.enums.PluginEnum;
+import org.apache.shenyu.common.enums.RpcTypeEnum;
+import org.apache.shenyu.common.enums.SelectorTypeEnum;
+import org.apache.shenyu.common.utils.GsonUtils;
+import org.apache.shenyu.k8s.common.IngressConfiguration;
+import org.apache.shenyu.k8s.common.IngressConstants;
+import org.apache.shenyu.k8s.common.ShenyuMemoryConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Parser of Ingress Sofa Annotations.
+ */
+public class SofaParser implements K8sResourceParser<V1Ingress> {
+
+ private static final Logger LOG =
LoggerFactory.getLogger(SofaParser.class);
+
+ private final Lister<V1Service> serviceLister;
+
+ private final Lister<V1Endpoints> endpointsLister;
+
+ /**
+ * SofaIngressParser Constructor.
+ *
+ * @param serviceInformer serviceInformer
+ * @param endpointsInformer endpointsInformer
+ */
+ public SofaParser(final Lister<V1Service> serviceInformer, final
Lister<V1Endpoints> endpointsInformer) {
+ this.serviceLister = serviceInformer;
+ this.endpointsLister = endpointsInformer;
+ }
+
+ /**
+ * Parse ingress to ShenyuMemoryConfig.
+ *
+ * @param ingress ingress resource
+ * @param coreV1Api coreV1Api
+ * @return ShenyuMemoryConfig
+ */
+ @Override
+ public ShenyuMemoryConfig parse(final V1Ingress ingress, final CoreV1Api
coreV1Api) {
+ ShenyuMemoryConfig res = new ShenyuMemoryConfig();
+
+ if (Objects.nonNull(ingress.getSpec())) {
+ // Parse the sofa backend
+ V1IngressBackend sofaBackend =
ingress.getSpec().getDefaultBackend();
+ List<V1IngressRule> rules = ingress.getSpec().getRules();
+ List<V1IngressTLS> tlsList = ingress.getSpec().getTls();
+
+ String namespace =
Objects.requireNonNull(ingress.getMetadata()).getNamespace();
+
+ if (Objects.isNull(rules) || CollectionUtils.isEmpty(rules)) {
+ // if rules is null, sofaBackend become global default
+ if (Objects.nonNull(sofaBackend) &&
Objects.nonNull(sofaBackend.getService())) {
+ IngressConfiguration defaultRouteConfig =
getSofaRouteConfig(ingress.getMetadata().getAnnotations());
+ res.setGlobalDefaultBackend(Pair.of(Pair.of(namespace +
"/" + ingress.getMetadata().getName(), sofaBackend.getService().getName()),
+ defaultRouteConfig));
+ }
+ } else {
+ // if rules is not null, sofaBackend is default in this ingress
+ List<IngressConfiguration> routeList = new
ArrayList<>(rules.size());
+ for (V1IngressRule ingressRule : rules) {
+ List<IngressConfiguration> routes =
parseIngressRule(ingressRule,
+
Objects.requireNonNull(ingress.getMetadata()).getNamespace(),
ingress.getMetadata().getAnnotations(), ingress.getMetadata().getLabels());
+ routeList.addAll(routes);
+ }
+ res.setRouteConfigList(routeList);
+ }
+
+ // Parse tls
+ if (Objects.nonNull(tlsList) &&
CollectionUtils.isNotEmpty(tlsList)) {
+ List<SslCrtAndKeyStream> sslList = new ArrayList<>();
+ for (V1IngressTLS tls : tlsList) {
+ if (Objects.nonNull(tls.getSecretName()) &&
Objects.nonNull(tls.getHosts()) && CollectionUtils.isNotEmpty(tls.getHosts())) {
+ try {
+ V1Secret secret =
coreV1Api.readNamespacedSecret(tls.getSecretName(), namespace, "ture");
+ if (secret.getData() != null) {
+ InputStream keyCertChainInputStream = new
ByteArrayInputStream(secret.getData().get("tls.crt"));
+ InputStream keyInputStream = new
ByteArrayInputStream(secret.getData().get("tls.key"));
+ tls.getHosts().forEach(host ->
+ sslList.add(new
SslCrtAndKeyStream(host, keyCertChainInputStream, keyInputStream))
+ );
+ }
+ } catch (ApiException e) {
+ LOG.error("parse tls failed ", e);
+ }
+ }
+ }
+ res.setTlsConfigList(sslList);
+ }
+ }
+ return res;
+ }
+
+ private String parsePort(final V1IngressServiceBackend service) {
+ if (Objects.nonNull(service.getPort())) {
+ if (service.getPort().getNumber() != null &&
service.getPort().getNumber() > 0) {
+ return String.valueOf(service.getPort().getNumber());
+ } else if (service.getPort().getName() != null &&
!"".equals(service.getPort().getName().trim())) {
+ return service.getPort().getName().trim();
+ }
+ }
+ return null;
+ }
+
+ private List<IngressConfiguration> parseIngressRule(final V1IngressRule
ingressRule,
+ final String namespace,
+ final Map<String,
String> annotations,
+ final Map<String,
String> labels) {
+ List<IngressConfiguration> res = new ArrayList<>();
+ ConditionData hostCondition = Objects.nonNull(ingressRule.getHost()) ?
createHostCondition(ingressRule.getHost()) : null;
+ if (Objects.nonNull(ingressRule.getHttp())) {
+ List<V1HTTPIngressPath> paths = ingressRule.getHttp().getPaths();
+ if (Objects.nonNull(paths)) {
+ for (V1HTTPIngressPath path : paths) {
+ if (path.getPath() == null) {
+ continue;
+ }
+ OperatorEnum operator = getOperator(path.getPathType());
+ ConditionData pathCondition =
createPathCondition(path.getPath(), operator);
+ List<ConditionData> conditionList = new ArrayList<>(2);
+ if (Objects.nonNull(hostCondition)) {
+ conditionList.add(hostCondition);
+ }
+ conditionList.add(pathCondition);
+
+ SelectorData selectorData =
createSelectorData(path.getPath(), conditionList);
+ List<RuleData> ruleDataList = new ArrayList<>();
+ List<MetaData> metaDataList = new ArrayList<>();
+ for (String label : labels.keySet()) {
+ Map<String, String> metadataAnnotations =
serviceLister.namespace(namespace).get(labels.get(label)).getMetadata().getAnnotations();
+ SofaRuleHandle ruleHandle =
createSofaRuleHandle(annotations);
+ List<ConditionData> ruleConditionList =
getRuleConditionList(metadataAnnotations);
+ RuleData ruleData =
createRuleData(metadataAnnotations, ruleHandle, ruleConditionList);
+ MetaData metaData = parseMetaData(metadataAnnotations);
+ ruleDataList.add(ruleData);
+ metaDataList.add(metaData);
+ }
+ res.add(new IngressConfiguration(selectorData,
ruleDataList, metaDataList));
+ }
+ }
+ }
+ return res;
+ }
+
+ private List<ConditionData> getRuleConditionList(final Map<String, String>
annotations) {
+ final List<ConditionData> ruleConditionList = new ArrayList<>();
+ ConditionData ruleCondition = new ConditionData();
+ ruleCondition.setOperator(OperatorEnum.EQ.getAlias());
+ ruleCondition.setParamType(ParamTypeEnum.URI.getName());
+
ruleCondition.setParamValue(annotations.get(IngressConstants.PLUGIN_SOFA_PATH));
+ ruleConditionList.add(ruleCondition);
+ return ruleConditionList;
+ }
+
+ private ConditionData createHostCondition(final String host) {
+ ConditionData hostCondition = new ConditionData();
+ hostCondition.setParamType(ParamTypeEnum.DOMAIN.getName());
+ hostCondition.setOperator(OperatorEnum.EQ.getAlias());
+ hostCondition.setParamValue(host);
+ return hostCondition;
+ }
+
+ private OperatorEnum getOperator(final String pathType) {
+ if ("ImplementationSpecific".equals(pathType)) {
+ return OperatorEnum.MATCH;
+ } else if ("Prefix".equals(pathType)) {
+ return OperatorEnum.STARTS_WITH;
+ } else if ("Exact".equals(pathType)) {
+ return OperatorEnum.EQ;
+ } else {
+ LOG.info("Invalid path type, set it with match operator");
+ return OperatorEnum.MATCH;
+ }
+ }
+
+ private ConditionData createPathCondition(final String path, final
OperatorEnum operator) {
+ ConditionData pathCondition = new ConditionData();
+ pathCondition.setOperator(operator.getAlias());
+ pathCondition.setParamType(ParamTypeEnum.URI.getName());
+ pathCondition.setParamValue(path);
+ return pathCondition;
+ }
+
+ private SofaRuleHandle createSofaRuleHandle(final Map<String, String>
annotations) {
+ SofaRuleHandle sofaRuleHandle = new SofaRuleHandle();
+ if (Objects.nonNull(annotations)) {
+
sofaRuleHandle.setLoadBalance(annotations.getOrDefault(IngressConstants.LOADBALANCER_ANNOTATION_KEY,
LoadBalanceEnum.RANDOM.getName()));
+
sofaRuleHandle.setTimeout(Long.parseLong(annotations.getOrDefault(IngressConstants.TIMEOUT_ANNOTATION_KEY,
"3000")));
+
sofaRuleHandle.setRetries(Integer.parseInt(annotations.getOrDefault(IngressConstants.RETRY_ANNOTATION_KEY,
"3")));
+ }
+ return sofaRuleHandle;
+ }
+
+ private SelectorData createSelectorData(final String path, final
List<ConditionData> conditionList) {
+ return SelectorData.builder()
+ .pluginId(String.valueOf(PluginEnum.SOFA.getCode()))
+ .pluginName(PluginEnum.SOFA.getName())
+ .name(path)
+ .matchMode(MatchModeEnum.AND.getCode())
+ .type(SelectorTypeEnum.CUSTOM_FLOW.getCode())
+ .enabled(true)
+ .logged(false)
+ .continued(true)
+ .conditionList(conditionList)
+ .build();
+ }
+
+ private RuleData createRuleData(final Map<String, String>
metadataAnnotations, final SofaRuleHandle ruleHandle, final List<ConditionData>
ruleConditionList) {
+ return RuleData.builder()
+
.name(metadataAnnotations.get(IngressConstants.PLUGIN_SOFA_PATH))
+ .pluginName(PluginEnum.SOFA.getName())
+ .matchMode(MatchModeEnum.AND.getCode())
+ .conditionDataList(ruleConditionList)
+ .handle(GsonUtils.getInstance().toJson(ruleHandle))
+ .loged(true)
+ .enabled(true)
+ .build();
+ }
+
+ private MetaData parseMetaData(final Map<String, String> annotations) {
+ return MetaData.builder()
+
.appName(annotations.get(IngressConstants.PLUGIN_SOFA_APP_NAME))
+ .path(annotations.get(IngressConstants.PLUGIN_SOFA_PATH))
+
.rpcType(annotations.get(IngressConstants.PLUGIN_SOFA_RPC_TYPE))
+
.rpcExt(annotations.get(IngressConstants.PLUGIN_SOFA_RPC_EXPAND))
+
.serviceName(annotations.get(IngressConstants.PLUGIN_SOFA_SERVICE_NAME))
+
.methodName(annotations.get(IngressConstants.PLUGIN_SOFA_METHOD_NAME))
+
.parameterTypes(annotations.get(IngressConstants.PLUGIN_SOFA_PARAMS_TYPE))
+ .enabled(true)
+ .build();
+ }
+
+ private IngressConfiguration getSofaRouteConfig(final Map<String, String>
annotations) {
+ final ConditionData conditionData = new ConditionData();
+ conditionData.setParamName("sofa");
+ conditionData.setParamType(ParamTypeEnum.URI.getName());
+ conditionData.setOperator(OperatorEnum.PATH_PATTERN.getAlias());
+ conditionData.setParamValue("/**");
+
+ final SelectorData selectorData = SelectorData.builder()
+ .name("sofa-selector")
+ .sort(Integer.MAX_VALUE)
+ .conditionList(Collections.singletonList(conditionData))
+ .enabled(true)
+ .id(IngressConstants.ID)
+ .pluginName(PluginEnum.SOFA.getName())
+ .pluginId(String.valueOf(PluginEnum.SOFA.getCode()))
+ .logged(false)
+ .continued(true)
+ .matchMode(MatchModeEnum.AND.getCode())
+ .type(SelectorTypeEnum.FULL_FLOW.getCode()).build();
+
+ final RuleData ruleData = RuleData.builder()
+ .selectorId(IngressConstants.ID)
+ .pluginName(PluginEnum.SOFA.getName())
+ .name("sofa-rule")
+ .matchMode(MatchModeEnum.AND.getCode())
+ .conditionDataList(Collections.singletonList(conditionData))
+ .loged(false)
+ .enabled(true)
+ .sort(Integer.MAX_VALUE).build();
+
+ MetaData metaData = new MetaData();
+ if (Objects.nonNull(annotations)) {
+
metaData.setAppName(annotations.getOrDefault(IngressConstants.PLUGIN_SOFA_APP_NAME,
"sofa"));
+
metaData.setMethodName(annotations.getOrDefault(IngressConstants.PLUGIN_SOFA_METHOD_NAME,
"methodName"));
+
metaData.setPath(annotations.getOrDefault(IngressConstants.PLUGIN_SOFA_PATH,
"/sofa/findAll"));
+
metaData.setRpcType(annotations.getOrDefault(IngressConstants.PLUGIN_SOFA_RPC_TYPE,
RpcTypeEnum.SOFA.getName()));
+
metaData.setServiceName(annotations.getOrDefault(IngressConstants.PLUGIN_SOFA_SERVICE_NAME,
"findAll"));
+
metaData.setContextPath(annotations.getOrDefault(IngressConstants.PLUGIN_SOFA_CONTEXT_PATH,
"/sofa"));
+
metaData.setRpcExt(annotations.getOrDefault(IngressConstants.PLUGIN_SOFA_RPC_EXT,
"{\"loadbalance\":\"hash\",\"retries\":3,\"timeout\":-1}"));
+
metaData.setParameterTypes(annotations.getOrDefault(IngressConstants.PLUGIN_SOFA_PARAMETER_TYPE,
""));
+
metaData.setEnabled(Boolean.parseBoolean(annotations.getOrDefault(IngressConstants.PLUGIN_SOFA_ENABLED,
"true")));
+ }
+ return new IngressConfiguration(selectorData, Arrays.asList(ruleData),
Arrays.asList(metaData));
+ }
+}
diff --git
a/shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/reconciler/IngressReconciler.java
b/shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/reconciler/IngressReconciler.java
index bf6beb8a03..f1629a8d40 100644
---
a/shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/reconciler/IngressReconciler.java
+++
b/shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/reconciler/IngressReconciler.java
@@ -129,21 +129,7 @@ public class IngressReconciler implements Reconciler {
final V1Ingress v1Ingress =
this.ingressLister.namespace(request.getNamespace()).get(request.getName());
final V1Ingress oldIngress =
IngressCache.getInstance().get(request.getNamespace(), request.getName());
Map<String, String> annotations =
v1Ingress.getMetadata().getAnnotations();
- if
(Objects.equals(annotations.get(IngressConstants.PLUGIN_DUBBO_ENABLED),
"true")) {
- String zookeeperUrl = getZookeeperUrl(annotations, request);
- enablePlugin(shenyuCacheRepository, PluginEnum.DUBBO,
zookeeperUrl);
- } else if
(Objects.equals(annotations.get(IngressConstants.PLUGIN_MOTAN_ENABLED),
"true")) {
- String zookeeperUrl = getZookeeperUrl(annotations, request);
- enablePlugin(shenyuCacheRepository, PluginEnum.MOTAN,
zookeeperUrl);
- } else if
(Objects.equals(annotations.get(IngressConstants.PLUGIN_SPRING_CLOUD_ENABLED),
"true")) {
- enablePlugin(shenyuCacheRepository, PluginEnum.SPRING_CLOUD, null);
- } else if
(Objects.equals(annotations.get(IngressConstants.PLUGIN_WEB_SOCKET_ENABLED),
"true")) {
- enablePlugin(shenyuCacheRepository, PluginEnum.WEB_SOCKET, null);
- } else if
(Objects.equals(annotations.get(IngressConstants.PLUGIN_BRPC_ENABLED), "true"))
{
- enablePlugin(shenyuCacheRepository, PluginEnum.BRPC, null);
- } else if
(Objects.equals(annotations.get(IngressConstants.PLUGIN_GRPC_ENABLED), "true"))
{
- enablePlugin(shenyuCacheRepository, PluginEnum.GRPC, null);
- }
+ enablePluginsBasedOnAnnotations(annotations, request);
if (Objects.isNull(v1Ingress)) {
if (Objects.nonNull(oldIngress)) {
// Delete ingress binding selectors
@@ -203,6 +189,27 @@ public class IngressReconciler implements Reconciler {
return new Result(false);
}
+ private void enablePluginsBasedOnAnnotations(final Map<String, String>
annotations, final Request request) {
+ if
(Objects.equals(annotations.get(IngressConstants.PLUGIN_DUBBO_ENABLED),
"true")) {
+ String zookeeperUrl = getZookeeperUrl(annotations, request);
+ enablePlugin(shenyuCacheRepository, PluginEnum.DUBBO,
zookeeperUrl);
+ } else if
(Objects.equals(annotations.get(IngressConstants.PLUGIN_MOTAN_ENABLED),
"true")) {
+ String zookeeperUrl = getZookeeperUrl(annotations, request);
+ enablePlugin(shenyuCacheRepository, PluginEnum.MOTAN,
zookeeperUrl);
+ } else if
(Objects.equals(annotations.get(IngressConstants.PLUGIN_SPRING_CLOUD_ENABLED),
"true")) {
+ enablePlugin(shenyuCacheRepository, PluginEnum.SPRING_CLOUD, null);
+ } else if
(Objects.equals(annotations.get(IngressConstants.PLUGIN_WEB_SOCKET_ENABLED),
"true")) {
+ enablePlugin(shenyuCacheRepository, PluginEnum.WEB_SOCKET, null);
+ } else if
(Objects.equals(annotations.get(IngressConstants.PLUGIN_BRPC_ENABLED), "true"))
{
+ enablePlugin(shenyuCacheRepository, PluginEnum.BRPC, null);
+ } else if
(Objects.equals(annotations.get(IngressConstants.PLUGIN_GRPC_ENABLED), "true"))
{
+ enablePlugin(shenyuCacheRepository, PluginEnum.GRPC, null);
+ } else if
(Objects.equals(annotations.get(IngressConstants.PLUGIN_SOFA_ENABLED), "true"))
{
+ String zookeeperUrl = getZookeeperUrl(annotations, request);
+ enablePlugin(shenyuCacheRepository, PluginEnum.SOFA, zookeeperUrl);
+ }
+ }
+
private void doDeleteConfigByIngress(final Request request, final
V1Ingress oldIngress) {
List<String> selectorList = new ArrayList<>();
if
(Objects.equals(oldIngress.getMetadata().getAnnotations().get(IngressConstants.PLUGIN_DUBBO_ENABLED),
"true")) {
@@ -219,6 +226,9 @@ public class IngressReconciler implements Reconciler {
selectorList = deleteSelectorByIngressName(request.getNamespace(),
request.getName(), PluginEnum.BRPC.getName(), "");
} else if
(Objects.equals(oldIngress.getMetadata().getAnnotations().get(IngressConstants.PLUGIN_GRPC_ENABLED),
"true")) {
selectorList = deleteSelectorByIngressName(request.getNamespace(),
request.getName(), PluginEnum.GRPC.getName(), "");
+ } else if
(Objects.equals(oldIngress.getMetadata().getAnnotations().get(IngressConstants.PLUGIN_SOFA_ENABLED),
"true")) {
+ selectorList = deleteSelectorByIngressName(request.getNamespace(),
request.getName(), PluginEnum.SOFA.getName(),
+
oldIngress.getMetadata().getAnnotations().get(IngressConstants.PLUGIN_SOFA_CONTEXT_PATH));
} else {
selectorList = deleteSelectorByIngressName(request.getNamespace(),
request.getName(), PluginEnum.DIVIDE.getName(), "");
}
@@ -235,6 +245,8 @@ public class IngressReconciler implements Reconciler {
IngressSelectorCache.getInstance().remove(request.getNamespace(),
request.getName(), PluginEnum.BRPC.getName());
} else if
(Objects.equals(oldIngress.getMetadata().getAnnotations().get(IngressConstants.PLUGIN_GRPC_ENABLED),
"true")) {
IngressSelectorCache.getInstance().remove(request.getNamespace(),
request.getName(), PluginEnum.GRPC.getName());
+ } else if
(Objects.equals(oldIngress.getMetadata().getAnnotations().get(IngressConstants.PLUGIN_SOFA_ENABLED),
"true")) {
+
IngressSelectorCache.getInstance().remove(request.getNamespace(),
request.getName(), PluginEnum.SOFA.getName());
} else {
IngressSelectorCache.getInstance().remove(request.getNamespace(),
request.getName(), PluginEnum.DIVIDE.getName());
}
@@ -283,6 +295,8 @@ public class IngressReconciler implements Reconciler {
return "{\"registerProtocol\":\"zk\",\"registerAddress\":\"" +
zookeeperUrl +
"\",\"corethreads\":0,\"threads\":2147483647,\"queues\":0,\"threadpool\":\"shared\"}";
case WEB_SOCKET:
return "{multiSelectorHandle: 1}";
+ case SOFA:
+ return "{\"protocol\":\"zookeeper\",\"register\":\"" +
zookeeperUrl + "\",\"threadpool\":\"shared\"}";
case GRPC:
return
"{\"multiSelectorHandle\":\"1\",\"multiRuleHandle\":\"0\",\"threadpool\":\"shared\"}";
default:
@@ -562,6 +576,7 @@ public class IngressReconciler implements Reconciler {
String pluginWebSocketEnabled =
ingress.getMetadata().getAnnotations().get(IngressConstants.PLUGIN_WEB_SOCKET_ENABLED);
String pluginBrpcEnabled =
ingress.getMetadata().getAnnotations().get(IngressConstants.PLUGIN_BRPC_ENABLED);
String pluginGrpcEnabled =
ingress.getMetadata().getAnnotations().get(IngressConstants.PLUGIN_GRPC_ENABLED);
+ String pluginSofaEnabled =
ingress.getMetadata().getAnnotations().get(IngressConstants.PLUGIN_SOFA_ENABLED);
if ((Boolean.TRUE.toString()).equals(pluginDubboEnabled)) {
pluginName = PluginEnum.DUBBO.getName();
} else if ((Boolean.TRUE.toString()).equals(pluginMotanEnabled)) {
@@ -574,6 +589,8 @@ public class IngressReconciler implements Reconciler {
pluginName = PluginEnum.BRPC.getName();
} else if ((Boolean.TRUE.toString()).equals(pluginGrpcEnabled)) {
pluginName = PluginEnum.GRPC.getName();
+ } else if ((Boolean.TRUE.toString()).equals(pluginSofaEnabled)) {
+ pluginName = PluginEnum.SOFA.getName();
} else {
pluginName = PluginEnum.DIVIDE.getName();
}