This is an automated email from the ASF dual-hosted git repository.
albumenj pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/dubbo.git
The following commit(s) were added to refs/heads/3.0 by this push:
new c125b99 Add Kubernetes Mesh Rule Support (#8350)
c125b99 is described below
commit c125b99189650b2f3ae148fb19c970435bba3563
Author: Albumen Kevin <[email protected]>
AuthorDate: Wed Aug 4 18:46:39 2021 +0800
Add Kubernetes Mesh Rule Support (#8350)
* Add Kubernetes Mesh Rule Support
* pretty comment
* fix ut
---
.../router/mesh/route/MeshAppRuleListener.java | 13 +-
.../cluster/router/mesh/route/MeshEnvListener.java | 38 ++++
.../cluster/router/mesh/route/MeshRuleManager.java | 33 ++--
.../org/apache/dubbo/common/utils/PojoUtils.java | 96 ++++++++++
.../dubbo/config/utils/ConfigValidationUtils.java | 2 +-
.../apache/dubbo/config/AbstractConfigTest.java | 4 +-
.../kubernetes/KubernetesMeshEnvListener.java | 197 +++++++++++++++++++++
.../kubernetes/KubernetesServiceDiscovery.java | 2 +
.../dubbo/registry/kubernetes/MeshConstant.java | 43 +++++
...o.rpc.cluster.router.mesh.route.MeshEnvListener | 1 +
10 files changed, 412 insertions(+), 17 deletions(-)
diff --git
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java
index 838c7ef..000a385 100644
---
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java
+++
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java
@@ -21,12 +21,15 @@ import
org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.PojoUtils;
import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup;
import
org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRule;
import
org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceRule;
import
org.apache.dubbo.rpc.cluster.router.mesh.util.VsDestinationGroupRuleDispatcher;
import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
+import org.yaml.snakeyaml.representer.Representer;
import java.text.MessageFormat;
import java.util.Map;
@@ -56,18 +59,20 @@ public class MeshAppRuleListener implements
ConfigurationListener {
VsDestinationGroup vsDestinationGroup = new VsDestinationGroup();
vsDestinationGroup.setAppName(appName);
- Yaml yaml = new Yaml();
- Yaml yaml2 = new Yaml();
+ Representer representer = new Representer();
+ representer.getPropertyUtils().setSkipMissingProperties(true);
+
+ Yaml yaml = new Yaml(new SafeConstructor());
Iterable<Object> objectIterable = yaml.loadAll(configInfo);
for (Object result : objectIterable) {
Map resultMap = (Map) result;
if ("DestinationRule".equals(resultMap.get("kind"))) {
- DestinationRule destinationRule =
yaml2.loadAs(yaml2.dump(result), DestinationRule.class);
+ DestinationRule destinationRule =
PojoUtils.mapToPojo(resultMap, DestinationRule.class);
vsDestinationGroup.getDestinationRuleList().add(destinationRule);
} else if ("VirtualService".equals(resultMap.get("kind"))) {
- VirtualServiceRule virtualServiceRule =
yaml2.loadAs(yaml2.dump(result), VirtualServiceRule.class);
+ VirtualServiceRule virtualServiceRule =
PojoUtils.mapToPojo(resultMap, VirtualServiceRule.class);
vsDestinationGroup.getVirtualServiceRuleList().add(virtualServiceRule);
}
}
diff --git
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshEnvListener.java
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshEnvListener.java
new file mode 100644
index 0000000..4b92457
--- /dev/null
+++
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshEnvListener.java
@@ -0,0 +1,38 @@
+/*
+ * 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.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.extension.SPI;
+
+/**
+ * Mesh Rule Listener
+ * Such as Kubernetes, Service Mesh (xDS) environment support define rule in
env
+ */
+@SPI
+public interface MeshEnvListener {
+ /**
+ * @return whether current environment support listen
+ */
+ default boolean isEnable() {
+ return false;
+ }
+
+ void onSubscribe(String appName, MeshAppRuleListener listener);
+
+ void onUnSubscribe(String appName);
+}
diff --git
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManager.java
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManager.java
index 3be0972..87fdede 100644
---
a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManager.java
+++
b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManager.java
@@ -18,11 +18,13 @@
package org.apache.dubbo.rpc.cluster.router.mesh.route;
import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.rpc.model.ApplicationModel;
import java.util.Collection;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -39,23 +41,34 @@ public final class MeshRuleManager {
MeshAppRuleListener meshAppRuleListener = new MeshAppRuleListener(app);
String appRuleDataId = app + MESH_RULE_DATA_ID_SUFFIX;
DynamicConfiguration configuration =
ApplicationModel.getEnvironment().getDynamicConfiguration()
- .orElse(null);
+ .orElse(null);
- if (configuration == null) {
- logger.warn("Doesn't support DynamicConfiguration!");
+ Set<MeshEnvListener> envListeners =
ExtensionLoader.getExtensionLoader(MeshEnvListener.class).getSupportedExtensionInstances();
+
+ if (configuration == null &&
envListeners.stream().noneMatch(MeshEnvListener::isEnable)) {
+ logger.warn("Doesn't support Configuration!");
return;
}
- try {
- String rawConfig = configuration.getConfig(appRuleDataId,
DynamicConfiguration.DEFAULT_GROUP, 5000L);
- if (rawConfig != null) {
- meshAppRuleListener.receiveConfigInfo(rawConfig);
+ if(configuration != null) {
+ try {
+ String rawConfig = configuration.getConfig(appRuleDataId,
DynamicConfiguration.DEFAULT_GROUP, 5000L);
+ if (rawConfig != null) {
+ meshAppRuleListener.receiveConfigInfo(rawConfig);
+ }
+ } catch (Throwable throwable) {
+ logger.error("get MeshRuleManager app rule failed.",
throwable);
+ }
+
+ configuration.addListener(appRuleDataId,
DynamicConfiguration.DEFAULT_GROUP, meshAppRuleListener);
+ }
+
+ for (MeshEnvListener envListener : envListeners) {
+ if(envListener.isEnable()) {
+ envListener.onSubscribe(app, meshAppRuleListener);
}
- } catch (Throwable throwable) {
- logger.error("get MeshRuleManager app rule failed.", throwable);
}
- configuration.addListener(appRuleDataId,
DynamicConfiguration.DEFAULT_GROUP, meshAppRuleListener);
APP_RULE_LISTENERS.put(app, meshAppRuleListener);
}
diff --git
a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java
b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java
index 1a8330a..889ada1 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java
@@ -23,6 +23,7 @@ import org.apache.dubbo.common.logger.LoggerFactory;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
+import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -30,6 +31,8 @@ import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -41,6 +44,7 @@ import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Properties;
import java.util.TreeMap;
import java.util.WeakHashMap;
@@ -50,6 +54,8 @@ import java.util.concurrent.ConcurrentSkipListMap;
import java.util.function.Consumer;
import java.util.function.Supplier;
+import static org.apache.dubbo.common.utils.ClassUtils.isAssignableFrom;
+
/**
* PojoUtils. Travel object deeply, and convert complex type to simple type.
* <p/>
@@ -68,6 +74,8 @@ public class PojoUtils {
private static final ConcurrentMap<String, Method> NAME_METHODS_CACHE =
new ConcurrentHashMap<String, Method>();
private static final ConcurrentMap<Class<?>, ConcurrentMap<String, Field>>
CLASS_FIELD_CACHE = new ConcurrentHashMap<Class<?>, ConcurrentMap<String,
Field>>();
private static final boolean GENERIC_WITH_CLZ =
Boolean.parseBoolean(ConfigUtils.getProperty(CommonConstants.GENERIC_WITH_CLZ_KEY,
"true"));
+ private static final List<Class<?>> CLASS_CAN_BE_STRING =
Arrays.asList(Byte.class, Short.class, Integer.class,
+ Long.class, Float.class, Double.class, Boolean.class, Character.class);
public static Object[] generalize(Object[] objs) {
Object[] dests = new Object[objs.length];
@@ -686,4 +694,92 @@ public class PojoUtils {
}
}
+ /**
+ * convert map to a specific class instance
+ *
+ * @param map map wait for convert
+ * @param cls the specified class
+ * @param <T> the type of {@code cls}
+ * @return class instance declare in param {@code cls}
+ * @throws ReflectiveOperationException if the instance creation is failed
+ * @since 2.7.10
+ */
+ public static <T> T mapToPojo(Map<String, Object> map, Class<T> cls)
throws ReflectiveOperationException {
+ T instance = cls.getDeclaredConstructor().newInstance();
+ Map<String, Field> beanPropertyFields =
ReflectUtils.getBeanPropertyFields(cls);
+ for (Map.Entry<String, Field> entry : beanPropertyFields.entrySet()) {
+ String name = entry.getKey();
+ Field field = entry.getValue();
+ Object mapObject = map.get(name);
+ if (mapObject == null) {
+ continue;
+ }
+
+ Type type = field.getGenericType();
+ Object fieldObject = getFieldObject(mapObject, type);
+ field.set(instance, fieldObject);
+ }
+
+ return instance;
+ }
+
+ private static Object getFieldObject(Object mapObject, Type fieldType)
throws ReflectiveOperationException {
+ if (fieldType instanceof Class<?>) {
+ return convertClassType(mapObject, (Class<?>) fieldType);
+ } else if (fieldType instanceof ParameterizedType) {
+ return convertParameterizedType(mapObject, (ParameterizedType)
fieldType);
+ } else if (fieldType instanceof GenericArrayType || fieldType
instanceof TypeVariable<?> || fieldType instanceof WildcardType) {
+ // ignore these type currently
+ return null;
+ } else {
+ throw new IllegalArgumentException("Unrecognized Type: " +
fieldType.toString());
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object convertClassType(Object mapObject, Class<?> type)
throws ReflectiveOperationException {
+ if (type.isPrimitive() || isAssignableFrom(type,
mapObject.getClass())) {
+ return mapObject;
+ } else if (Objects.equals(type, String.class) &&
CLASS_CAN_BE_STRING.contains(mapObject.getClass())) {
+ // auto convert specified type to string
+ return mapObject.toString();
+ } else if (mapObject instanceof Map) {
+ return mapToPojo((Map<String, Object>) mapObject, type);
+ } else {
+ // type didn't match and mapObject is not another Map struct.
+ // we just ignore this situation.
+ return null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object convertParameterizedType(Object mapObject,
ParameterizedType type) throws ReflectiveOperationException {
+ Type rawType = type.getRawType();
+ if (!isAssignableFrom((Class<?>) rawType, mapObject.getClass())) {
+ return null;
+ }
+
+ Type[] actualTypeArguments = type.getActualTypeArguments();
+ if (isAssignableFrom(Map.class, (Class<?>) rawType)) {
+ Map<Object, Object> map = (Map<Object, Object>)
mapObject.getClass().getDeclaredConstructor().newInstance();
+ for (Map.Entry<Object, Object> entry : ((Map<Object, Object>)
mapObject).entrySet()) {
+ Object key = getFieldObject(entry.getKey(),
actualTypeArguments[0]);
+ Object value = getFieldObject(entry.getValue(),
actualTypeArguments[1]);
+ map.put(key, value);
+ }
+
+ return map;
+ } else if (isAssignableFrom(Collection.class, (Class<?>) rawType)) {
+ Collection<Object> collection = (Collection<Object>)
mapObject.getClass().getDeclaredConstructor().newInstance();
+ for (Object m : (Iterable<?>) mapObject) {
+ Object ele = getFieldObject(m, actualTypeArguments[0]);
+ collection.add(ele);
+ }
+
+ return collection;
+ } else {
+ // ignore other type currently
+ return null;
+ }
+ }
}
diff --git
a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java
b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java
index 20c8e1a..03211ca 100644
---
a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java
+++
b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java
@@ -702,7 +702,7 @@ public class ConfigValidationUtils {
return;
}
if (value.length() > maxlength) {
- throw new IllegalStateException("Invalid " + property + "=\"" +
value + "\" is longer than " + maxlength);
+ logger.error("Invalid " + property + "=\"" + value + "\" is longer
than " + maxlength);
}
if (pattern != null) {
Matcher matcher = pattern.matcher(value);
diff --git
a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java
b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java
index 450ba35..9718452 100644
---
a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java
+++
b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java
@@ -209,7 +209,7 @@ public class AbstractConfigTest {
@Test
public void checkLength() throws Exception {
- Assertions.assertThrows(IllegalStateException.class, () -> {
+ Assertions.assertDoesNotThrow(() -> {
StringBuilder builder = new StringBuilder();
for (int i = 0; i <= 200; i++) {
builder.append('a');
@@ -220,7 +220,7 @@ public class AbstractConfigTest {
@Test
public void checkPathLength() throws Exception {
- Assertions.assertThrows(IllegalStateException.class, () -> {
+ Assertions.assertDoesNotThrow(() -> {
StringBuilder builder = new StringBuilder();
for (int i = 0; i <= 200; i++) {
builder.append('a');
diff --git
a/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListener.java
b/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListener.java
new file mode 100644
index 0000000..1a0c1fa
--- /dev/null
+++
b/dubbo-registry/dubbo-registry-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-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscovery.java
b/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscovery.java
index 43262c7..0f61a57 100644
---
a/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscovery.java
+++
b/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscovery.java
@@ -96,6 +96,8 @@ public class KubernetesServiceDiscovery extends
AbstractServiceDiscovery {
" Master URL: " + config.getMasterUrl() +
" Hostname: " + currentHostname;
logger.error(message);
+ } else {
+ KubernetesMeshEnvListener.injectKubernetesEnv(kubernetesClient,
namespace);
}
}
diff --git
a/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/MeshConstant.java
b/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/MeshConstant.java
new file mode 100644
index 0000000..813bdd8
--- /dev/null
+++
b/dubbo-registry/dubbo-registry-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-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListener
b/dubbo-registry/dubbo-registry-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListener
new file mode 100644
index 0000000..b44c869
--- /dev/null
+++
b/dubbo-registry/dubbo-registry-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListener
@@ -0,0 +1 @@
+kubernetes=org.apache.dubbo.registry.kubernetes.KubernetesMeshEnvListener