You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by al...@apache.org on 2022/07/15 07:32:30 UTC
[dubbo] branch 3.1 updated: [3.1] Add new modules kubernetes and xds (#10325)
This is an automated email from the ASF dual-hosted git repository.
albumenj pushed a commit to branch 3.1
in repository https://gitbox.apache.org/repos/asf/dubbo.git
The following commit(s) were added to refs/heads/3.1 by this push:
new 17a0aa2730 [3.1] Add new modules kubernetes and xds (#10325)
17a0aa2730 is described below
commit 17a0aa2730e9a2f3d92963921d43603e21bec3bd
Author: ken.lj <ke...@gmail.com>
AuthorDate: Fri Jul 15 15:32:19 2022 +0800
[3.1] Add new modules kubernetes and xds (#10325)
* add modules, kubernetes and xds
* xds components configuration
---
dubbo-kubernetes/pom.xml | 81 +++++
.../kubernetes/KubernetesMeshEnvListener.java | 197 ++++++++++
.../KubernetesMeshEnvListenerFactory.java | 28 +-
.../registry/kubernetes/KubernetesRegistry.java | 12 +-
.../kubernetes/KubernetesRegistryFactory.java | 6 +-
.../kubernetes/KubernetesServiceDiscovery.java | 404 +++++++++++++++++++++
.../KubernetesServiceDiscoveryFactory.java | 18 +-
.../dubbo/registry/kubernetes/MeshConstant.java | 43 +++
.../kubernetes/NopKubernetesMeshEnvListener.java | 22 +-
.../kubernetes/util/KubernetesClientConst.java | 78 ++++
.../kubernetes/util/KubernetesConfigUtils.java | 118 ++++++
.../org.apache.dubbo.registry.RegistryFactory | 1 +
...g.apache.dubbo.registry.client.ServiceDiscovery | 1 +
...e.dubbo.registry.client.ServiceDiscoveryFactory | 1 +
...luster.router.mesh.route.MeshEnvListenerFactory | 1 +
.../kubernetes/KubernetesServiceDiscoveryTest.java | 198 ++++++++++
.../org.mockito.plugins.MockMaker | 1 +
dubbo-registry/pom.xml | 1 -
.../dubbo-registry-xds => dubbo-xds}/pom.xml | 12 +-
.../dubbo/registry/xds/XdsCertificateSigner.java | 0
.../java/org/apache/dubbo/registry/xds/XdsEnv.java | 0
.../org/apache/dubbo/registry/xds/XdsRegistry.java | 0
.../dubbo/registry/xds/XdsRegistryFactory.java | 0
.../dubbo/registry/xds/XdsServiceDiscovery.java | 0
.../registry/xds/XdsServiceDiscoveryFactory.java | 0
.../xds/istio/IstioCitadelCertificateSigner.java | 0
.../dubbo/registry/xds/istio/IstioConstant.java | 0
.../apache/dubbo/registry/xds/istio/IstioEnv.java | 0
.../dubbo/registry/xds/util/NodeBuilder.java | 0
.../dubbo/registry/xds/util/PilotExchanger.java | 0
.../apache/dubbo/registry/xds/util/XdsChannel.java | 0
.../xds/util/protocol/AbstractProtocol.java | 0
.../registry/xds/util/protocol/DeltaResource.java | 0
.../registry/xds/util/protocol/XdsProtocol.java | 0
.../xds/util/protocol/delta/DeltaEndpoint.java | 0
.../xds/util/protocol/delta/DeltaListener.java | 0
.../xds/util/protocol/delta/DeltaRoute.java | 0
.../xds/util/protocol/impl/EdsProtocol.java | 0
.../xds/util/protocol/impl/LdsProtocol.java | 0
.../xds/util/protocol/impl/RdsProtocol.java | 0
.../xds/util/protocol/message/Endpoint.java | 0
.../xds/util/protocol/message/EndpointResult.java | 0
.../xds/util/protocol/message/ListenerResult.java | 0
.../xds/util/protocol/message/RouteResult.java | 0
.../src/main/proto/ca.proto | 0
.../org.apache.dubbo.registry.RegistryFactory | 0
...g.apache.dubbo.registry.client.ServiceDiscovery | 0
...e.dubbo.registry.client.ServiceDiscoveryFactory | 0
....apache.dubbo.registry.xds.XdsCertificateSigner | 0
pom.xml | 2 +
50 files changed, 1177 insertions(+), 48 deletions(-)
diff --git a/dubbo-kubernetes/pom.xml b/dubbo-kubernetes/pom.xml
new file mode 100644
index 0000000000..f5250b30a3
--- /dev/null
+++ b/dubbo-kubernetes/pom.xml
@@ -0,0 +1,81 @@
+<?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.dubbo</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>${revision}</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>dubbo-kubernetes</artifactId>
+ <name>${project.artifactId}</name>
+ <description>The Kubernetes Integration</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-registry-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-common</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-metadata-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-all</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.fabric8</groupId>
+ <artifactId>kubernetes-client</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.fabric8</groupId>
+ <artifactId>kubernetes-server-mock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-junit-jupiter</artifactId>
+ <version>3.8.0</version>
+ <scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ </dependencies>
+
+
+</project>
diff --git a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListener.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListener.java
new file mode 100644
index 0000000000..1a0c1fa6b2
--- /dev/null
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListener.java
@@ -0,0 +1,197 @@
+/*
+ * 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.dubbo.registry.kubernetes;
+
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshAppRuleListener;
+import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListener;
+
+import com.google.gson.Gson;
+import io.fabric8.kubernetes.api.model.ListOptionsBuilder;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.kubernetes.client.Watch;
+import io.fabric8.kubernetes.client.Watcher;
+import io.fabric8.kubernetes.client.WatcherException;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class KubernetesMeshEnvListener implements MeshEnvListener {
+ public static final Logger logger = LoggerFactory.getLogger(KubernetesMeshEnvListener.class);
+ private volatile static boolean usingApiServer = false;
+ private volatile static KubernetesClient kubernetesClient;
+ private volatile static String namespace;
+
+ private final Map<String, MeshAppRuleListener> appRuleListenerMap = new ConcurrentHashMap<>();
+
+ private final Map<String, Watch> vsAppWatch = new ConcurrentHashMap<>();
+ private final Map<String, Watch> drAppWatch = new ConcurrentHashMap<>();
+
+ private final Map<String, String> vsAppCache = new ConcurrentHashMap<>();
+ private final Map<String, String> drAppCache = new ConcurrentHashMap<>();
+
+ public static void injectKubernetesEnv(KubernetesClient client, String configuredNamespace) {
+ usingApiServer = true;
+ kubernetesClient = client;
+ namespace = configuredNamespace;
+ }
+
+ @Override
+ public boolean isEnable() {
+ return usingApiServer;
+ }
+
+ @Override
+ public void onSubscribe(String appName, MeshAppRuleListener listener) {
+ appRuleListenerMap.put(appName, listener);
+ logger.info("Subscribe Mesh Rule in Kubernetes. AppName: " + appName);
+
+ // subscribe VisualService
+ subscribeVs(appName);
+
+ // subscribe DestinationRule
+ subscribeDr(appName);
+
+ // notify for start
+ notifyOnce(appName);
+ }
+
+ private void subscribeVs(String appName) {
+ if (vsAppWatch.containsKey(appName)) {
+ return;
+ }
+
+ try {
+ Watch watch = kubernetesClient
+ .customResource(
+ MeshConstant.getVsDefinition())
+ .watch(namespace, appName, null, new ListOptionsBuilder().build(), new Watcher<String>() {
+ @Override
+ public void eventReceived(Action action, String resource) {
+ logger.info("Received VS Rule notification. AppName: " + appName + " Action:" + action + " Resource:" + resource);
+
+ if (action == Action.ADDED || action == Action.MODIFIED) {
+ Map drRuleMap = new Gson().fromJson(resource, Map.class);
+ String vsRule = new Yaml(new SafeConstructor()).dump(drRuleMap);
+ vsAppCache.put(appName, vsRule);
+ if (drAppCache.containsKey(appName)) {
+ notifyListener(vsRule, appName, drAppCache.get(appName));
+ }
+ } else {
+ appRuleListenerMap.get(appName).receiveConfigInfo("");
+ }
+ }
+
+ @Override
+ public void onClose(WatcherException cause) {
+ // ignore
+ }
+ });
+ vsAppWatch.put(appName, watch);
+ try {
+ Map<String, Object> vsRule = kubernetesClient
+ .customResource(
+ MeshConstant.getVsDefinition())
+ .get(namespace, appName);
+ vsAppCache.put(appName, new Yaml(new SafeConstructor()).dump(vsRule));
+ } catch (Throwable ignore) {
+
+ }
+ } catch (IOException e) {
+ logger.error("Error occurred when listen kubernetes crd.", e);
+ }
+ }
+
+ private void notifyListener(String vsRule, String appName, String drRule) {
+ String rule = vsRule + "\n---\n" + drRule;
+ logger.info("Notify App Rule Listener. AppName: " + appName + " Rule:" + rule);
+
+ appRuleListenerMap.get(appName).receiveConfigInfo(rule);
+ }
+
+ private void subscribeDr(String appName) {
+ if (drAppWatch.containsKey(appName)) {
+ return;
+ }
+
+ try {
+ Watch watch = kubernetesClient
+ .customResource(
+ MeshConstant.getDrDefinition())
+ .watch(namespace, appName, null, new ListOptionsBuilder().build(), new Watcher<String>() {
+ @Override
+ public void eventReceived(Action action, String resource) {
+ logger.info("Received VS Rule notification. AppName: " + appName + " Action:" + action + " Resource:" + resource);
+
+ if (action == Action.ADDED || action == Action.MODIFIED) {
+ Map drRuleMap = new Gson().fromJson(resource, Map.class);
+ String drRule = new Yaml(new SafeConstructor()).dump(drRuleMap);
+
+ drAppCache.put(appName, drRule);
+ if (vsAppCache.containsKey(appName)) {
+ notifyListener(vsAppCache.get(appName), appName, drRule);
+ }
+ } else {
+ appRuleListenerMap.get(appName).receiveConfigInfo("");
+ }
+ }
+
+ @Override
+ public void onClose(WatcherException cause) {
+ // ignore
+ }
+ });
+ drAppWatch.put(appName, watch);
+ try {
+ Map<String, Object> drRule = kubernetesClient
+ .customResource(
+ MeshConstant.getDrDefinition())
+ .get(namespace, appName);
+ drAppCache.put(appName, new Yaml(new SafeConstructor()).dump(drRule));
+ } catch (Throwable ignore) {
+
+ }
+ } catch (IOException e) {
+ logger.error("Error occurred when listen kubernetes crd.", e);
+ }
+ }
+
+ private void notifyOnce(String appName) {
+ if (vsAppCache.containsKey(appName) && drAppCache.containsKey(appName)) {
+ notifyListener(vsAppCache.get(appName), appName, drAppCache.get(appName));
+ }
+ }
+
+ @Override
+ public void onUnSubscribe(String appName) {
+ appRuleListenerMap.remove(appName);
+
+ if (vsAppWatch.containsKey(appName)) {
+ vsAppWatch.remove(appName).close();
+ }
+ vsAppCache.remove(appName);
+
+ if (drAppWatch.containsKey(appName)) {
+ drAppWatch.remove(appName).close();
+ }
+ drAppCache.remove(appName);
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscoveryFactory.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListenerFactory.java
similarity index 52%
copy from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscoveryFactory.java
copy to dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListenerFactory.java
index 41eba21a70..9d1c6d06cf 100644
--- a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscoveryFactory.java
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListenerFactory.java
@@ -14,27 +14,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.registry.xds;
+package org.apache.dubbo.registry.kubernetes;
-import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.registry.client.AbstractServiceDiscoveryFactory;
-import org.apache.dubbo.registry.client.ServiceDiscovery;
-import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListener;
+import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListenerFactory;
-public class XdsServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory {
+import java.util.concurrent.atomic.AtomicBoolean;
- private static final Logger logger = LoggerFactory.getLogger(XdsServiceDiscoveryFactory.class);
+public class KubernetesMeshEnvListenerFactory implements MeshEnvListenerFactory {
+ public static final Logger logger = LoggerFactory.getLogger(KubernetesMeshEnvListenerFactory.class);
+ private final AtomicBoolean initialized = new AtomicBoolean(false);
+ private MeshEnvListener listener = null;
@Override
- protected ServiceDiscovery createDiscovery(URL registryURL) {
- XdsServiceDiscovery xdsServiceDiscovery = new XdsServiceDiscovery(ApplicationModel.defaultModel() ,registryURL);
+ public MeshEnvListener getListener() {
try {
- xdsServiceDiscovery.doInitialize(registryURL);
- } catch (Exception e) {
- logger.error("Error occurred when initialize xDS service discovery impl.", e);
+ if (initialized.compareAndSet(false, true)) {
+ listener = new NopKubernetesMeshEnvListener();
+ }
+ } catch (Throwable t) {
+ logger.info("Current Env not support Kubernetes.");
}
- return xdsServiceDiscovery;
+ return listener;
}
}
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistry.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistry.java
similarity index 81%
copy from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistry.java
copy to dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistry.java
index a4bd51e6b1..b221c89c2b 100644
--- a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistry.java
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistry.java
@@ -14,20 +14,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.registry.xds;
+package org.apache.dubbo.registry.kubernetes;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.registry.NotifyListener;
import org.apache.dubbo.registry.support.FailbackRegistry;
/**
- * Empty implements for xDS <br/>
- * xDS only support `Service Discovery` mode register <br/>
+ * Empty implements for Kubernetes <br/>
+ * Kubernetes only support `Service Discovery` mode register <br/>
* Used to compat past version like 2.6.x, 2.7.x with interface level register <br/>
- * {@link XdsServiceDiscovery} is the real implementation of xDS
+ * {@link KubernetesServiceDiscovery} is the real implementation of Kubernetes
*/
-public class XdsRegistry extends FailbackRegistry {
- public XdsRegistry(URL url) {
+public class KubernetesRegistry extends FailbackRegistry {
+ public KubernetesRegistry(URL url) {
super(url);
}
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistryFactory.java
similarity index 87%
copy from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java
copy to dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistryFactory.java
index 8fa130b197..fe0e0477d7 100644
--- a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistryFactory.java
@@ -14,13 +14,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.registry.xds;
+package org.apache.dubbo.registry.kubernetes;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.registry.Registry;
import org.apache.dubbo.registry.support.AbstractRegistryFactory;
-public class XdsRegistryFactory extends AbstractRegistryFactory {
+public class KubernetesRegistryFactory extends AbstractRegistryFactory {
@Override
protected String createRegistryCacheKey(URL url) {
@@ -29,6 +29,6 @@ public class XdsRegistryFactory extends AbstractRegistryFactory {
@Override
protected Registry createRegistry(URL url) {
- return new XdsRegistry(url);
+ return new KubernetesRegistry(url);
}
}
diff --git a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscovery.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscovery.java
new file mode 100644
index 0000000000..e0f47f1f95
--- /dev/null
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscovery.java
@@ -0,0 +1,404 @@
+/*
+ * 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.dubbo.registry.kubernetes;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.registry.client.AbstractServiceDiscovery;
+import org.apache.dubbo.registry.client.DefaultServiceInstance;
+import org.apache.dubbo.registry.client.ServiceInstance;
+import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
+import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
+import org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst;
+import org.apache.dubbo.registry.kubernetes.util.KubernetesConfigUtils;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ScopeModelUtil;
+
+import com.alibaba.fastjson.JSONObject;
+import io.fabric8.kubernetes.api.model.EndpointAddress;
+import io.fabric8.kubernetes.api.model.EndpointPort;
+import io.fabric8.kubernetes.api.model.EndpointSubset;
+import io.fabric8.kubernetes.api.model.Endpoints;
+import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.api.model.PodBuilder;
+import io.fabric8.kubernetes.api.model.Service;
+import io.fabric8.kubernetes.client.Config;
+import io.fabric8.kubernetes.client.DefaultKubernetesClient;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.kubernetes.client.Watch;
+import io.fabric8.kubernetes.client.Watcher;
+import io.fabric8.kubernetes.client.WatcherException;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
+
+public class KubernetesServiceDiscovery extends AbstractServiceDiscovery {
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private KubernetesClient kubernetesClient;
+
+ private String currentHostname;
+
+ private final URL registryURL;
+
+ private final String namespace;
+
+ private final boolean enableRegister;
+
+ public final static String KUBERNETES_PROPERTIES_KEY = "io.dubbo/metadata";
+
+ private final static ConcurrentHashMap<String, Watch> SERVICE_WATCHER = new ConcurrentHashMap<>(64);
+
+ private final static ConcurrentHashMap<String, Watch> PODS_WATCHER = new ConcurrentHashMap<>(64);
+
+ private final static ConcurrentHashMap<String, Watch> ENDPOINTS_WATCHER = new ConcurrentHashMap<>(64);
+
+ private final static ConcurrentHashMap<String, AtomicLong> SERVICE_UPDATE_TIME = new ConcurrentHashMap<>(64);
+
+ public KubernetesServiceDiscovery(ApplicationModel applicationModel, URL registryURL) {
+ super(applicationModel, registryURL);
+ Config config = KubernetesConfigUtils.createKubernetesConfig(registryURL);
+ this.kubernetesClient = new DefaultKubernetesClient(config);
+ this.currentHostname = System.getenv("HOSTNAME");
+ this.registryURL = registryURL;
+ this.namespace = config.getNamespace();
+ this.enableRegister = registryURL.getParameter(KubernetesClientConst.ENABLE_REGISTER, true);
+
+ boolean availableAccess;
+ try {
+ availableAccess = kubernetesClient.pods().withName(currentHostname).get() != null;
+ } catch (Throwable e) {
+ availableAccess = false;
+ }
+ if (!availableAccess) {
+ String message = "Unable to access api server. " +
+ "Please check your url config." +
+ " Master URL: " + config.getMasterUrl() +
+ " Hostname: " + currentHostname;
+ logger.error(message);
+ } else {
+ KubernetesMeshEnvListener.injectKubernetesEnv(kubernetesClient, namespace);
+ }
+ }
+
+ @Override
+ public void doDestroy() throws Exception {
+ SERVICE_WATCHER.forEach((k, v) -> v.close());
+ SERVICE_WATCHER.clear();
+
+ PODS_WATCHER.forEach((k, v) -> v.close());
+ PODS_WATCHER.clear();
+
+ ENDPOINTS_WATCHER.forEach((k, v) -> v.close());
+ ENDPOINTS_WATCHER.clear();
+
+ kubernetesClient.close();
+ }
+
+ @Override
+ public void doRegister(ServiceInstance serviceInstance) throws RuntimeException {
+ if (enableRegister) {
+ kubernetesClient
+ .pods()
+ .inNamespace(namespace)
+ .withName(currentHostname)
+ .edit(pod ->
+ new PodBuilder(pod)
+ .editOrNewMetadata()
+ .addToAnnotations(KUBERNETES_PROPERTIES_KEY, JSONObject.toJSONString(serviceInstance.getMetadata()))
+ .endMetadata()
+ .build());
+ if (logger.isInfoEnabled()) {
+ logger.info("Write Current Service Instance Metadata to Kubernetes pod. " +
+ "Current pod name: " + currentHostname);
+ }
+ }
+ }
+
+ /**
+ * Comparing to {@link AbstractServiceDiscovery#doUpdate(ServiceInstance)}, unregister() is unnecessary here.
+ */
+ @Override
+ public void doUpdate(ServiceInstance serviceInstance) throws RuntimeException {
+ reportMetadata(serviceInstance.getServiceMetadata());
+ this.doRegister(serviceInstance);
+ }
+
+ @Override
+ public void doUnregister(ServiceInstance serviceInstance) throws RuntimeException {
+ if (enableRegister) {
+ kubernetesClient
+ .pods()
+ .inNamespace(namespace)
+ .withName(currentHostname)
+ .edit(pod ->
+ new PodBuilder(pod)
+ .editOrNewMetadata()
+ .removeFromAnnotations(KUBERNETES_PROPERTIES_KEY)
+ .endMetadata()
+ .build());
+ if (logger.isInfoEnabled()) {
+ logger.info("Remove Current Service Instance from Kubernetes pod. Current pod name: " + currentHostname);
+ }
+ }
+ }
+
+ @Override
+ public Set<String> getServices() {
+ return kubernetesClient
+ .services()
+ .inNamespace(namespace)
+ .list()
+ .getItems()
+ .stream()
+ .map(service -> service.getMetadata().getName())
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public List<ServiceInstance> getInstances(String serviceName) throws NullPointerException {
+ Endpoints endpoints =
+ kubernetesClient
+ .endpoints()
+ .inNamespace(namespace)
+ .withName(serviceName)
+ .get();
+
+ return toServiceInstance(endpoints, serviceName);
+ }
+
+ @Override
+ public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws NullPointerException, IllegalArgumentException {
+ listener.getServiceNames().forEach(serviceName -> {
+ SERVICE_UPDATE_TIME.put(serviceName, new AtomicLong(0L));
+
+ // Watch Service Endpoint Modification
+ watchEndpoints(listener, serviceName);
+
+ // Watch Pods Modification, happens when ServiceInstance updated
+ watchPods(listener, serviceName);
+
+ // Watch Service Modification, happens when Service Selector updated, used to update pods watcher
+ watchService(listener, serviceName);
+ });
+ }
+
+ private void watchEndpoints(ServiceInstancesChangedListener listener, String serviceName) {
+ Watch watch = kubernetesClient
+ .endpoints()
+ .inNamespace(namespace)
+ .withName(serviceName)
+ .watch(new Watcher<Endpoints>() {
+ @Override
+ public void eventReceived(Action action, Endpoints resource) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Received Endpoint Event. Event type: " + action.name() +
+ ". Current pod name: " + currentHostname);
+ }
+
+ notifyServiceChanged(serviceName, listener);
+ }
+
+ @Override
+ public void onClose(WatcherException cause) {
+ // ignore
+ }
+ });
+
+ ENDPOINTS_WATCHER.put(serviceName, watch);
+ }
+
+ private void watchPods(ServiceInstancesChangedListener listener, String serviceName) {
+ Map<String, String> serviceSelector = getServiceSelector(serviceName);
+ if (serviceSelector == null) {
+ return;
+ }
+
+ Watch watch = kubernetesClient
+ .pods()
+ .inNamespace(namespace)
+ .withLabels(serviceSelector)
+ .watch(new Watcher<Pod>() {
+ @Override
+ public void eventReceived(Action action, Pod resource) {
+ if (Action.MODIFIED.equals(action)) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Received Pods Update Event. Current pod name: " + currentHostname);
+ }
+
+ notifyServiceChanged(serviceName, listener);
+ }
+ }
+
+ @Override
+ public void onClose(WatcherException cause) {
+ // ignore
+ }
+ });
+
+ PODS_WATCHER.put(serviceName, watch);
+ }
+
+ private void watchService(ServiceInstancesChangedListener listener, String serviceName) {
+ Watch watch = kubernetesClient
+ .services()
+ .inNamespace(namespace)
+ .withName(serviceName)
+ .watch(new Watcher<Service>() {
+ @Override
+ public void eventReceived(Action action, Service resource) {
+ if (Action.MODIFIED.equals(action)) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Received Service Update Event. Update Pods Watcher. " +
+ "Current pod name: " + currentHostname);
+ }
+
+ if (PODS_WATCHER.containsKey(serviceName)) {
+ PODS_WATCHER.get(serviceName).close();
+ PODS_WATCHER.remove(serviceName);
+ }
+ watchPods(listener, serviceName);
+ }
+ }
+
+ @Override
+ public void onClose(WatcherException cause) {
+ // ignore
+ }
+ });
+
+ SERVICE_WATCHER.put(serviceName, watch);
+ }
+
+ private void notifyServiceChanged(String serviceName, ServiceInstancesChangedListener listener) {
+ long receivedTime = System.nanoTime();
+
+ ServiceInstancesChangedEvent event;
+
+ event = new ServiceInstancesChangedEvent(serviceName, getInstances(serviceName));
+
+ AtomicLong updateTime = SERVICE_UPDATE_TIME.get(serviceName);
+ long lastUpdateTime = updateTime.get();
+
+ if (lastUpdateTime <= receivedTime) {
+ if (updateTime.compareAndSet(lastUpdateTime, receivedTime)) {
+ listener.onEvent(event);
+ return;
+ }
+ }
+
+ if (logger.isInfoEnabled()) {
+ logger.info("Discard Service Instance Data. " +
+ "Possible Cause: Newer message has been processed or Failed to update time record by CAS. " +
+ "Current Data received time: " + receivedTime + ". " +
+ "Newer Data received time: " + lastUpdateTime + ".");
+ }
+ }
+
+ @Override
+ public URL getUrl() {
+ return registryURL;
+ }
+
+ private Map<String, String> getServiceSelector(String serviceName) {
+ Service service = kubernetesClient.services().inNamespace(namespace).withName(serviceName).get();
+ if (service == null) {
+ return null;
+ }
+ return service.getSpec().getSelector();
+ }
+
+ private List<ServiceInstance> toServiceInstance(Endpoints endpoints, String serviceName) {
+ Map<String, String> serviceSelector = getServiceSelector(serviceName);
+ if (serviceSelector == null) {
+ return new LinkedList<>();
+ }
+ Map<String, Pod> pods = kubernetesClient
+ .pods()
+ .inNamespace(namespace)
+ .withLabels(serviceSelector)
+ .list()
+ .getItems()
+ .stream()
+ .collect(
+ Collectors.toMap(
+ pod -> pod.getMetadata().getName(),
+ pod -> pod));
+
+ List<ServiceInstance> instances = new LinkedList<>();
+ Set<Integer> instancePorts = new HashSet<>();
+
+ for (EndpointSubset endpointSubset : endpoints.getSubsets()) {
+ instancePorts.addAll(
+ endpointSubset.getPorts()
+ .stream().map(EndpointPort::getPort)
+ .collect(Collectors.toSet()));
+ }
+
+ for (EndpointSubset endpointSubset : endpoints.getSubsets()) {
+ for (EndpointAddress address : endpointSubset.getAddresses()) {
+ Pod pod = pods.get(address.getTargetRef().getName());
+ String ip = address.getIp();
+ if (pod == null) {
+ logger.warn("Unable to match Kubernetes Endpoint address with Pod. " +
+ "EndpointAddress Hostname: " + address.getTargetRef().getName());
+ continue;
+ }
+
+ instancePorts.forEach(port -> {
+ ServiceInstance serviceInstance = new DefaultServiceInstance(serviceName, ip, port, ScopeModelUtil.getApplicationModel(getUrl().getScopeModel()));
+
+ String properties = pod.getMetadata().getAnnotations().get(KUBERNETES_PROPERTIES_KEY);
+ if (StringUtils.isNotEmpty(properties)) {
+ serviceInstance.getMetadata().putAll(JSONObject.parseObject(properties, Map.class));
+ instances.add(serviceInstance);
+ } else {
+ logger.warn("Unable to find Service Instance metadata in Pod Annotations. " +
+ "Possibly cause: provider has not been initialized successfully. " +
+ "EndpointAddress Hostname: " + address.getTargetRef().getName());
+ }
+ });
+ }
+ }
+
+ return instances;
+ }
+
+ /**
+ * UT used only
+ */
+ @Deprecated
+ public void setCurrentHostname(String currentHostname) {
+ this.currentHostname = currentHostname;
+ }
+
+ /**
+ * UT used only
+ */
+ @Deprecated
+ public void setKubernetesClient(KubernetesClient kubernetesClient) {
+ this.kubernetesClient = kubernetesClient;
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryFactory.java
similarity index 67%
copy from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java
copy to dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryFactory.java
index 8fa130b197..7d11dfaaa8 100644
--- a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryFactory.java
@@ -14,21 +14,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.registry.xds;
+package org.apache.dubbo.registry.kubernetes;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.registry.Registry;
-import org.apache.dubbo.registry.support.AbstractRegistryFactory;
-
-public class XdsRegistryFactory extends AbstractRegistryFactory {
-
- @Override
- protected String createRegistryCacheKey(URL url) {
- return url.toFullString();
- }
+import org.apache.dubbo.registry.client.AbstractServiceDiscoveryFactory;
+import org.apache.dubbo.registry.client.ServiceDiscovery;
+public class KubernetesServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory {
@Override
- protected Registry createRegistry(URL url) {
- return new XdsRegistry(url);
+ protected ServiceDiscovery createDiscovery(URL registryURL) {
+ return new KubernetesServiceDiscovery(applicationModel, registryURL);
}
}
diff --git a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/MeshConstant.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/MeshConstant.java
new file mode 100644
index 0000000000..813bdd8259
--- /dev/null
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/MeshConstant.java
@@ -0,0 +1,43 @@
+/*
+ * 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.dubbo.registry.kubernetes;
+
+import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext;
+
+public class MeshConstant {
+ public static CustomResourceDefinitionContext getVsDefinition() {
+ // TODO cache
+ return new CustomResourceDefinitionContext.Builder()
+ .withGroup("service.dubbo.apache.org")
+ .withVersion("v1alpha1")
+ .withScope("Namespaced")
+ .withName("virtualservices.service.dubbo.apache.org")
+ .withPlural("virtualservices")
+ .withKind("VirtualService").build();
+ }
+
+ public static CustomResourceDefinitionContext getDrDefinition() {
+ // TODO cache
+ return new CustomResourceDefinitionContext.Builder()
+ .withGroup("service.dubbo.apache.org")
+ .withVersion("v1alpha1")
+ .withScope("Namespaced")
+ .withName("destinationrules.service.dubbo.apache.org")
+ .withPlural("destinationrules")
+ .withKind("DestinationRule").build();
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/NopKubernetesMeshEnvListener.java
similarity index 64%
copy from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java
copy to dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/NopKubernetesMeshEnvListener.java
index 8fa130b197..1238efb579 100644
--- a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/NopKubernetesMeshEnvListener.java
@@ -14,21 +14,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.registry.xds;
+package org.apache.dubbo.registry.kubernetes;
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.registry.Registry;
-import org.apache.dubbo.registry.support.AbstractRegistryFactory;
+import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshAppRuleListener;
+import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListener;
-public class XdsRegistryFactory extends AbstractRegistryFactory {
+public class NopKubernetesMeshEnvListener implements MeshEnvListener {
@Override
- protected String createRegistryCacheKey(URL url) {
- return url.toFullString();
+ public boolean isEnable() {
+ return false;
}
@Override
- protected Registry createRegistry(URL url) {
- return new XdsRegistry(url);
+ public void onSubscribe(String appName, MeshAppRuleListener listener) {
+
+ }
+
+ @Override
+ public void onUnSubscribe(String appName) {
+
}
}
diff --git a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesClientConst.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesClientConst.java
new file mode 100644
index 0000000000..d4591a249e
--- /dev/null
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesClientConst.java
@@ -0,0 +1,78 @@
+/*
+ * 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.dubbo.registry.kubernetes.util;
+
+public class KubernetesClientConst {
+ public static final String DEFAULT_MASTER_PLACEHOLDER = "DEFAULT_MASTER_HOST";
+ public static final String DEFAULT_MASTER_URL = "https://kubernetes.default.svc";
+
+ public final static String ENABLE_REGISTER = "enableRegister";
+
+ public final static String TRUST_CERTS = "trustCerts";
+
+ public final static String USE_HTTPS = "useHttps";
+
+ public static final String HTTP2_DISABLE = "http2Disable";
+
+ public final static String NAMESPACE = "namespace";
+
+ public final static String API_VERSION = "apiVersion";
+
+ public final static String CA_CERT_FILE = "caCertFile";
+
+ public final static String CA_CERT_DATA = "caCertData";
+
+ public final static String CLIENT_CERT_FILE = "clientCertFile";
+
+ public final static String CLIENT_CERT_DATA = "clientCertData";
+
+ public final static String CLIENT_KEY_FILE = "clientKeyFile";
+
+ public final static String CLIENT_KEY_DATA = "clientKeyData";
+
+ public final static String CLIENT_KEY_ALGO = "clientKeyAlgo";
+
+ public final static String CLIENT_KEY_PASSPHRASE = "clientKeyPassphrase";
+
+ public final static String OAUTH_TOKEN = "oauthToken";
+
+ public final static String USERNAME = "username";
+
+ public final static String PASSWORD = "password";
+
+ public final static String WATCH_RECONNECT_INTERVAL = "watchReconnectInterval";
+
+ public final static String WATCH_RECONNECT_LIMIT = "watchReconnectLimit";
+
+ public final static String CONNECTION_TIMEOUT = "connectionTimeout";
+
+ public final static String REQUEST_TIMEOUT = "requestTimeout";
+
+ public final static String ROLLING_TIMEOUT = "rollingTimeout";
+
+ public final static String LOGGING_INTERVAL = "loggingInterval";
+
+ public final static String HTTP_PROXY = "httpProxy";
+
+ public final static String HTTPS_PROXY = "httpsProxy";
+
+ public final static String PROXY_USERNAME = "proxyUsername";
+
+ public final static String PROXY_PASSWORD = "proxyPassword";
+
+ public final static String NO_PROXY = "noProxy";
+}
diff --git a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesConfigUtils.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesConfigUtils.java
new file mode 100644
index 0000000000..81505ca647
--- /dev/null
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesConfigUtils.java
@@ -0,0 +1,118 @@
+/*
+ * 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.dubbo.registry.kubernetes.util;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.StringUtils;
+
+import io.fabric8.kubernetes.client.Config;
+import io.fabric8.kubernetes.client.ConfigBuilder;
+
+import java.util.Base64;
+
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.API_VERSION;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CA_CERT_DATA;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CA_CERT_FILE;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_CERT_DATA;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_CERT_FILE;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_KEY_ALGO;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_KEY_DATA;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_KEY_FILE;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_KEY_PASSPHRASE;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CONNECTION_TIMEOUT;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.DEFAULT_MASTER_PLACEHOLDER;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.DEFAULT_MASTER_URL;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.HTTP2_DISABLE;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.HTTPS_PROXY;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.HTTP_PROXY;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.LOGGING_INTERVAL;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.NAMESPACE;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.NO_PROXY;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.OAUTH_TOKEN;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.PASSWORD;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.PROXY_PASSWORD;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.PROXY_USERNAME;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.REQUEST_TIMEOUT;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.ROLLING_TIMEOUT;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.TRUST_CERTS;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.USERNAME;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.USE_HTTPS;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.WATCH_RECONNECT_INTERVAL;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.WATCH_RECONNECT_LIMIT;
+
+public class KubernetesConfigUtils {
+
+ public static Config createKubernetesConfig(URL url) {
+ // Init default config
+ Config base = Config.autoConfigure(null);
+
+ // replace config with parameters if presents
+ return new ConfigBuilder(base) //
+ .withMasterUrl(buildMasterUrl(url)) //
+ .withApiVersion(url.getParameter(API_VERSION, base.getApiVersion())) //
+ .withNamespace(url.getParameter(NAMESPACE, base.getNamespace())) //
+ .withUsername(url.getParameter(USERNAME, base.getUsername())) //
+ .withPassword(url.getParameter(PASSWORD, base.getPassword())) //
+
+ .withOauthToken(url.getParameter(OAUTH_TOKEN, base.getOauthToken())) //
+
+ .withCaCertFile(url.getParameter(CA_CERT_FILE, base.getCaCertFile())) //
+ .withCaCertData(url.getParameter(CA_CERT_DATA, decodeBase64(base.getCaCertData()))) //
+
+ .withClientKeyFile(url.getParameter(CLIENT_KEY_FILE, base.getClientKeyFile())) //
+ .withClientKeyData(url.getParameter(CLIENT_KEY_DATA, decodeBase64(base.getClientKeyData()))) //
+
+ .withClientCertFile(url.getParameter(CLIENT_CERT_FILE, base.getClientCertFile())) //
+ .withClientCertData(url.getParameter(CLIENT_CERT_DATA, decodeBase64(base.getClientCertData()))) //
+
+ .withClientKeyAlgo(url.getParameter(CLIENT_KEY_ALGO, base.getClientKeyAlgo())) //
+ .withClientKeyPassphrase(url.getParameter(CLIENT_KEY_PASSPHRASE, base.getClientKeyPassphrase())) //
+
+ .withConnectionTimeout(url.getParameter(CONNECTION_TIMEOUT, base.getConnectionTimeout())) //
+ .withRequestTimeout(url.getParameter(REQUEST_TIMEOUT, base.getRequestTimeout())) //
+ .withRollingTimeout(url.getParameter(ROLLING_TIMEOUT, base.getRollingTimeout())) //
+
+ .withWatchReconnectInterval(url.getParameter(WATCH_RECONNECT_INTERVAL, base.getWatchReconnectInterval())) //
+ .withWatchReconnectLimit(url.getParameter(WATCH_RECONNECT_LIMIT, base.getWatchReconnectLimit())) //
+ .withLoggingInterval(url.getParameter(LOGGING_INTERVAL, base.getLoggingInterval())) //
+
+ .withTrustCerts(url.getParameter(TRUST_CERTS, base.isTrustCerts())) //
+ .withHttp2Disable(url.getParameter(HTTP2_DISABLE, base.isTrustCerts())) //
+
+ .withHttpProxy(url.getParameter(HTTP_PROXY, base.getHttpProxy())) //
+ .withHttpsProxy(url.getParameter(HTTPS_PROXY, base.getHttpsProxy())) //
+ .withProxyUsername(url.getParameter(PROXY_USERNAME, base.getProxyUsername())) //
+ .withProxyPassword(url.getParameter(PROXY_PASSWORD, base.getProxyPassword())) //
+ .withNoProxy(url.getParameter(NO_PROXY, base.getNoProxy())) //
+ .build();
+ }
+
+ private static String buildMasterUrl(URL url) {
+ if (DEFAULT_MASTER_PLACEHOLDER.equalsIgnoreCase(url.getHost())) {
+ return DEFAULT_MASTER_URL;
+ }
+ return (url.getParameter(USE_HTTPS, true) ?
+ "https://" : "http://")
+ + url.getHost() + ":" + url.getPort();
+ }
+
+ private static String decodeBase64(String str) {
+ return StringUtils.isNotEmpty(str) ?
+ new String(Base64.getDecoder().decode(str)) :
+ null;
+ }
+}
diff --git a/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000000..94177d81ea
--- /dev/null
+++ b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+kubernetes=org.apache.dubbo.registry.kubernetes.KubernetesRegistryFactory
\ No newline at end of file
diff --git a/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
new file mode 100644
index 0000000000..3e1b88e44c
--- /dev/null
+++ b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
@@ -0,0 +1 @@
+kubernetes=org.apache.dubbo.registry.kubernetes.KubernetesServiceDiscovery
\ No newline at end of file
diff --git a/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
new file mode 100644
index 0000000000..4301ab8b4b
--- /dev/null
+++ b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
@@ -0,0 +1 @@
+kubernetes=org.apache.dubbo.registry.kubernetes.KubernetesServiceDiscoveryFactory
\ No newline at end of file
diff --git a/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListenerFactory b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListenerFactory
new file mode 100644
index 0000000000..4dfae84b80
--- /dev/null
+++ b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListenerFactory
@@ -0,0 +1 @@
+kubernetes=org.apache.dubbo.registry.kubernetes.KubernetesMeshEnvListenerFactory
diff --git a/dubbo-kubernetes/src/test/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryTest.java b/dubbo-kubernetes/src/test/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryTest.java
new file mode 100644
index 0000000000..6b1a1b0318
--- /dev/null
+++ b/dubbo-kubernetes/src/test/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryTest.java
@@ -0,0 +1,198 @@
+/*
+ * 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.dubbo.registry.kubernetes;
+//
+//import org.apache.dubbo.common.URL;
+//import org.apache.dubbo.registry.client.DefaultServiceInstance;
+//import org.apache.dubbo.registry.client.ServiceInstance;
+//import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
+//import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
+//import org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst;
+//import org.apache.dubbo.rpc.model.ApplicationModel;
+//import org.apache.dubbo.rpc.model.ScopeModelUtil;
+//
+//import io.fabric8.kubernetes.api.model.Endpoints;
+//import io.fabric8.kubernetes.api.model.EndpointsBuilder;
+//import io.fabric8.kubernetes.api.model.Pod;
+//import io.fabric8.kubernetes.api.model.PodBuilder;
+//import io.fabric8.kubernetes.api.model.Service;
+//import io.fabric8.kubernetes.api.model.ServiceBuilder;
+//import io.fabric8.kubernetes.client.Config;
+//import io.fabric8.kubernetes.client.NamespacedKubernetesClient;
+//import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
+//import org.junit.jupiter.api.AfterEach;
+//import org.junit.jupiter.api.Assertions;
+//import org.junit.jupiter.api.BeforeEach;
+//import org.junit.jupiter.api.Test;
+//import org.junit.jupiter.api.extension.ExtendWith;
+//import org.mockito.ArgumentCaptor;
+//import org.mockito.Mockito;
+//import org.mockito.junit.jupiter.MockitoExtension;
+//
+//import java.util.HashMap;
+//import java.util.HashSet;
+//import java.util.Map;
+//
+//@ExtendWith({MockitoExtension.class})
+//public class KubernetesServiceDiscoveryTest {
+// public KubernetesServer mockServer = new KubernetesServer(false, true);
+//
+// private NamespacedKubernetesClient mockClient;
+//
+// private ServiceInstancesChangedListener mockListener = Mockito.mock(ServiceInstancesChangedListener.class);
+//
+// private URL serverUrl;
+//
+// private Map<String, String> selector;
+//
+// @BeforeEach
+// public void setUp() {
+// mockServer.before();
+// mockClient = mockServer.getClient();
+//
+// serverUrl = URL.valueOf(mockClient.getConfiguration().getMasterUrl())
+// .setProtocol("kubernetes")
+// .addParameter(KubernetesClientConst.USE_HTTPS, "false")
+// .addParameter(KubernetesClientConst.HTTP2_DISABLE, "true");
+// serverUrl.setScopeModel(ApplicationModel.defaultModel());
+//
+// System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
+// System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false");
+//
+// selector = new HashMap<>(4);
+// selector.put("l", "v");
+// Pod pod = new PodBuilder()
+// .withNewMetadata().withName("TestServer").withLabels(selector).endMetadata()
+// .build();
+//
+// Service service = new ServiceBuilder()
+// .withNewMetadata().withName("TestService").endMetadata()
+// .withNewSpec().withSelector(selector).endSpec().build();
+//
+// Endpoints endPoints = new EndpointsBuilder()
+// .withNewMetadata().withName("TestService").endMetadata()
+// .addNewSubset()
+// .addNewAddress().withIp("ip1")
+// .withNewTargetRef().withUid("uid1").withName("TestServer").endTargetRef().endAddress()
+// .addNewPort("Test", "Test", 12345, "TCP").endSubset()
+// .build();
+//
+// mockClient.pods().create(pod);
+// mockClient.services().create(service);
+// mockClient.endpoints().create(endPoints);
+// }
+//
+// @AfterEach
+// public void destroy() {
+// mockServer.after();
+// }
+//
+// @Test
+// public void testEndpointsUpdate() throws Exception {
+//
+// KubernetesServiceDiscovery serviceDiscovery = new KubernetesServiceDiscovery();
+// serviceDiscovery.initialize(serverUrl);
+//
+// serviceDiscovery.setCurrentHostname("TestServer");
+// serviceDiscovery.setKubernetesClient(mockClient);
+//
+// ServiceInstance serviceInstance = new DefaultServiceInstance("TestService", "Test", 12345, ScopeModelUtil.getApplicationModel(serviceDiscovery.getUrl().getScopeModel()));
+// serviceDiscovery.register(serviceInstance);
+//
+// HashSet<String> serviceList = new HashSet<>(4);
+// serviceList.add("TestService");
+// Mockito.when(mockListener.getServiceNames()).thenReturn(serviceList);
+// Mockito.doNothing().when(mockListener).onEvent(Mockito.any());
+//
+// serviceDiscovery.addServiceInstancesChangedListener(mockListener);
+// mockClient.endpoints().withName("TestService")
+// .edit(endpoints ->
+// new EndpointsBuilder(endpoints)
+// .editFirstSubset()
+// .addNewAddress()
+// .withIp("ip2")
+// .withNewTargetRef().withUid("uid2").withName("TestServer").endTargetRef()
+// .endAddress().endSubset()
+// .build());
+//
+// Thread.sleep(5000);
+// ArgumentCaptor<ServiceInstancesChangedEvent> eventArgumentCaptor =
+// ArgumentCaptor.forClass(ServiceInstancesChangedEvent.class);
+// Mockito.verify(mockListener, Mockito.times(2)).onEvent(eventArgumentCaptor.capture());
+// Assertions.assertEquals(2, eventArgumentCaptor.getValue().getServiceInstances().size());
+//
+// serviceDiscovery.unregister(serviceInstance);
+//
+// serviceDiscovery.destroy();
+// }
+//
+// @Test
+// public void testPodsUpdate() throws Exception {
+//
+// KubernetesServiceDiscovery serviceDiscovery = new KubernetesServiceDiscovery();
+// serviceDiscovery.initialize(serverUrl);
+//
+// serviceDiscovery.setCurrentHostname("TestServer");
+// serviceDiscovery.setKubernetesClient(mockClient);
+//
+// ServiceInstance serviceInstance = new DefaultServiceInstance("TestService", "Test", 12345, ScopeModelUtil.getApplicationModel(serviceDiscovery.getUrl().getScopeModel()));
+// serviceDiscovery.register(serviceInstance);
+//
+// HashSet<String> serviceList = new HashSet<>(4);
+// serviceList.add("TestService");
+// Mockito.when(mockListener.getServiceNames()).thenReturn(serviceList);
+// Mockito.doNothing().when(mockListener).onEvent(Mockito.any());
+//
+// serviceDiscovery.addServiceInstancesChangedListener(mockListener);
+//
+// serviceInstance = new DefaultServiceInstance("TestService", "Test12345", 12345, ScopeModelUtil.getApplicationModel(serviceDiscovery.getUrl().getScopeModel()));
+// serviceDiscovery.update(serviceInstance);
+//
+// Thread.sleep(5000);
+// ArgumentCaptor<ServiceInstancesChangedEvent> eventArgumentCaptor =
+// ArgumentCaptor.forClass(ServiceInstancesChangedEvent.class);
+// Mockito.verify(mockListener, Mockito.times(1)).onEvent(eventArgumentCaptor.capture());
+// Assertions.assertEquals(1, eventArgumentCaptor.getValue().getServiceInstances().size());
+//
+// serviceDiscovery.unregister(serviceInstance);
+//
+// serviceDiscovery.destroy();
+// }
+//
+// @Test
+// public void testGetInstance() throws Exception {
+// KubernetesServiceDiscovery serviceDiscovery = new KubernetesServiceDiscovery();
+// serviceDiscovery.initialize(serverUrl);
+//
+// serviceDiscovery.setCurrentHostname("TestServer");
+// serviceDiscovery.setKubernetesClient(mockClient);
+//
+// ServiceInstance serviceInstance = new DefaultServiceInstance("TestService", "Test", 12345, ScopeModelUtil.getApplicationModel(serviceDiscovery.getUrl().getScopeModel()));
+// serviceDiscovery.register(serviceInstance);
+//
+// serviceDiscovery.update(serviceInstance);
+//
+// Assertions.assertEquals(1, serviceDiscovery.getServices().size());
+// Assertions.assertEquals(1, serviceDiscovery.getInstances("TestService").size());
+//
+// Assertions.assertEquals(serviceInstance, serviceDiscovery.getLocalInstance());
+//
+// serviceDiscovery.unregister(serviceInstance);
+//
+// serviceDiscovery.destroy();
+// }
+//}
diff --git a/dubbo-kubernetes/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/dubbo-kubernetes/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000000..ca6ee9cea8
--- /dev/null
+++ b/dubbo-kubernetes/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1 @@
+mock-maker-inline
\ No newline at end of file
diff --git a/dubbo-registry/pom.xml b/dubbo-registry/pom.xml
index 8cb6984f2f..b72880197a 100644
--- a/dubbo-registry/pom.xml
+++ b/dubbo-registry/pom.xml
@@ -35,7 +35,6 @@
<module>dubbo-registry-zookeeper</module>
<module>dubbo-registry-nacos</module>
<module>dubbo-registry-multiple</module>
- <module>dubbo-registry-xds</module>
</modules>
<dependencies>
diff --git a/dubbo-registry/dubbo-registry-xds/pom.xml b/dubbo-xds/pom.xml
similarity index 93%
rename from dubbo-registry/dubbo-registry-xds/pom.xml
rename to dubbo-xds/pom.xml
index eb6e3798de..d162c2e163 100644
--- a/dubbo-registry/dubbo-registry-xds/pom.xml
+++ b/dubbo-xds/pom.xml
@@ -18,16 +18,20 @@
<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.dubbo</groupId>
- <artifactId>dubbo-registry</artifactId>
+ <artifactId>dubbo-parent</artifactId>
<version>${revision}</version>
+ <relativePath>../pom.xml</relativePath>
</parent>
- <modelVersion>4.0.0</modelVersion>
- <artifactId>dubbo-registry-xds</artifactId>
+ <artifactId>dubbo-xds</artifactId>
<name>${project.artifactId}</name>
- <description>The xDS registry module of Dubbo project</description>
+ <description>The xDS Integration</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
<dependencies>
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsCertificateSigner.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsCertificateSigner.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsCertificateSigner.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsCertificateSigner.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsEnv.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsEnv.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsEnv.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsEnv.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistry.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistry.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistry.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistry.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscovery.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscovery.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscovery.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscovery.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscoveryFactory.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscoveryFactory.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscoveryFactory.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscoveryFactory.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioCitadelCertificateSigner.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioCitadelCertificateSigner.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioCitadelCertificateSigner.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioCitadelCertificateSigner.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioConstant.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioConstant.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioConstant.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioConstant.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioEnv.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioEnv.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioEnv.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioEnv.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/NodeBuilder.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/NodeBuilder.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/NodeBuilder.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/NodeBuilder.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/PilotExchanger.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/PilotExchanger.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/PilotExchanger.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/PilotExchanger.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/XdsChannel.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/XdsChannel.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/XdsChannel.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/XdsChannel.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/AbstractProtocol.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/AbstractProtocol.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/AbstractProtocol.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/AbstractProtocol.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/DeltaResource.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/DeltaResource.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/DeltaResource.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/DeltaResource.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/XdsProtocol.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/XdsProtocol.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/XdsProtocol.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/XdsProtocol.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaEndpoint.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaEndpoint.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaEndpoint.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaEndpoint.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaListener.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaListener.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaListener.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaListener.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaRoute.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaRoute.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaRoute.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaRoute.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/EdsProtocol.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/EdsProtocol.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/EdsProtocol.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/EdsProtocol.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/LdsProtocol.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/LdsProtocol.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/LdsProtocol.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/LdsProtocol.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/RdsProtocol.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/RdsProtocol.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/RdsProtocol.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/RdsProtocol.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/Endpoint.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/Endpoint.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/Endpoint.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/Endpoint.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/EndpointResult.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/EndpointResult.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/EndpointResult.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/EndpointResult.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/ListenerResult.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/ListenerResult.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/ListenerResult.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/ListenerResult.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/RouteResult.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/RouteResult.java
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/RouteResult.java
rename to dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/RouteResult.java
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/proto/ca.proto b/dubbo-xds/src/main/proto/ca.proto
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/proto/ca.proto
rename to dubbo-xds/src/main/proto/ca.proto
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory b/dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
rename to dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery b/dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
rename to dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory b/dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
rename to dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
diff --git a/dubbo-registry/dubbo-registry-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.xds.XdsCertificateSigner b/dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.xds.XdsCertificateSigner
similarity index 100%
rename from dubbo-registry/dubbo-registry-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.xds.XdsCertificateSigner
rename to dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.xds.XdsCertificateSigner
diff --git a/pom.xml b/pom.xml
index 5fdea1c96b..3b69aee256 100644
--- a/pom.xml
+++ b/pom.xml
@@ -157,6 +157,8 @@
<module>dubbo-spring-boot</module>
<module>dubbo-native</module>
<module>dubbo-test</module>
+ <module>dubbo-kubernetes</module>
+ <module>dubbo-xds</module>
<module>dubbo-native-plugin</module>
</modules>