You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by am...@apache.org on 2022/01/04 12:10:36 UTC

[knox] branch master updated: KNOX-2690 - Service URLs are missing from Knox UI after a temporary discovery failure (#516)

This is an automated email from the ASF dual-hosted git repository.

amagyar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git


The following commit(s) were added to refs/heads/master by this push:
     new 67e7dd3  KNOX-2690 - Service URLs are missing from Knox UI after a temporary discovery failure (#516)
67e7dd3 is described below

commit 67e7dd3589d562cf87536417c57aa1ff8dbf4ced
Author: Attila Magyar <m....@gmail.com>
AuthorDate: Tue Jan 4 13:09:45 2022 +0100

    KNOX-2690 - Service URLs are missing from Knox UI after a temporary discovery failure (#516)
---
 .../discovery/ambari/AmbariServiceDiscovery.java   | 30 -----------
 .../ambari/AmbariServiceDiscoveryTest.java         | 24 ---------
 .../cm/ClouderaManagerServiceDiscovery.java        | 58 ++++------------------
 .../cm/ClouderaManagerServiceDiscoveryTest.java    |  2 +-
 .../org/apache/knox/gateway/GatewayMessages.java   |  5 ++
 .../topology/monitor/DescriptorsMonitor.java       |  3 ++
 .../test/extension/DummyServiceDiscovery.java      |  5 --
 .../extension/PropertiesFileServiceDiscovery.java  |  6 +--
 .../test/extension/SneakyServiceDiscoveryImpl.java |  7 ---
 .../simple/SimpleDescriptorHandlerTest.java        | 23 ++-------
 .../topology/discovery/ServiceDiscovery.java       | 12 -----
 .../gateway/SimpleDescriptorHandlerFuncTest.java   | 27 +++++++---
 .../topology/simple/DiscoveryException.java        | 36 ++++++++++++++
 .../topology/simple/SimpleDescriptorHandler.java   |  2 +-
 .../topology/simple/SimpleDescriptorMessages.java  |  4 --
 15 files changed, 82 insertions(+), 162 deletions(-)

diff --git a/gateway-discovery-ambari/src/main/java/org/apache/knox/gateway/topology/discovery/ambari/AmbariServiceDiscovery.java b/gateway-discovery-ambari/src/main/java/org/apache/knox/gateway/topology/discovery/ambari/AmbariServiceDiscovery.java
index 9668a36..4dc3041 100644
--- a/gateway-discovery-ambari/src/main/java/org/apache/knox/gateway/topology/discovery/ambari/AmbariServiceDiscovery.java
+++ b/gateway-discovery-ambari/src/main/java/org/apache/knox/gateway/topology/discovery/ambari/AmbariServiceDiscovery.java
@@ -194,36 +194,6 @@ class AmbariServiceDiscovery implements ServiceDiscovery {
         return TYPE;
     }
 
-
-    @Override
-    public Map<String, Cluster> discover(GatewayConfig gatewayConfig, ServiceDiscoveryConfig discoveryConfig) {
-        Map<String, Cluster> clusters = new HashMap<>();
-
-        init(gatewayConfig);
-
-        String discoveryAddress = discoveryConfig.getAddress();
-
-        // Invoke Ambari REST API to discover the available clusters
-        String clustersDiscoveryURL = String.format(Locale.ROOT, "%s" + AMBARI_CLUSTERS_URI, discoveryAddress);
-
-        JSONObject json = restClient.invoke(clustersDiscoveryURL, discoveryConfig.getUser(), discoveryConfig.getPasswordAlias());
-
-        // Parse the cluster names from the response, and perform the cluster discovery
-        JSONArray clusterItems = (JSONArray) json.get("items");
-        for (Object clusterItem : clusterItems) {
-            String clusterName = (String) ((JSONObject)((JSONObject) clusterItem).get("Clusters")).get("cluster_name");
-            try {
-                Cluster c = discover(gatewayConfig, discoveryConfig, clusterName);
-                clusters.put(clusterName, c);
-            } catch (Exception e) {
-                log.clusterDiscoveryError(clusterName, e);
-            }
-        }
-
-        return clusters;
-    }
-
-
     @Override
     public Cluster discover(GatewayConfig gatewayConfig, ServiceDiscoveryConfig config, String clusterName) {
         AmbariCluster cluster = null;
diff --git a/gateway-discovery-ambari/src/test/java/org/apache/knox/gateway/topology/discovery/ambari/AmbariServiceDiscoveryTest.java b/gateway-discovery-ambari/src/test/java/org/apache/knox/gateway/topology/discovery/ambari/AmbariServiceDiscoveryTest.java
index ba36fb2..d94aae9 100644
--- a/gateway-discovery-ambari/src/test/java/org/apache/knox/gateway/topology/discovery/ambari/AmbariServiceDiscoveryTest.java
+++ b/gateway-discovery-ambari/src/test/java/org/apache/knox/gateway/topology/discovery/ambari/AmbariServiceDiscoveryTest.java
@@ -197,30 +197,6 @@ public class AmbariServiceDiscoveryTest {
     }
 
     @Test
-    public void testBulkClusterDiscovery() throws Exception {
-        final String discoveryAddress = "http://ambarihost:8080";
-        final String clusterName = "anotherCluster";
-        ServiceDiscovery sd = new TestAmbariServiceDiscovery(clusterName);
-
-        GatewayConfig gc = EasyMock.createNiceMock(GatewayConfig.class);
-        EasyMock.replay(gc);
-
-        ServiceDiscoveryConfig sdc = EasyMock.createNiceMock(ServiceDiscoveryConfig.class);
-        EasyMock.expect(sdc.getAddress()).andReturn(discoveryAddress).anyTimes();
-        EasyMock.expect(sdc.getUser()).andReturn(null).anyTimes();
-        EasyMock.replay(sdc);
-
-        Map<String, ServiceDiscovery.Cluster> clusters = sd.discover(gc, sdc);
-        assertNotNull(clusters);
-        assertEquals(1, clusters.size());
-        ServiceDiscovery.Cluster cluster = clusters.get(clusterName);
-        assertNotNull(cluster);
-        assertEquals(clusterName, cluster.getName());
-        assertTrue(AmbariCluster.class.isAssignableFrom(cluster.getClass()));
-        assertEquals(6, ((AmbariCluster) cluster).getComponents().size());
-    }
-
-    @Test
     public void testClusterDiscoveryWithExternalComponentConfigAugmentation() throws Exception {
         final String discoveryAddress = "http://ambarihost:8080";
         final String clusterName = "myCluster";
diff --git a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscovery.java b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscovery.java
index 424630c..2b6fc17 100644
--- a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscovery.java
+++ b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscovery.java
@@ -16,12 +16,9 @@
  */
 package org.apache.knox.gateway.topology.discovery.cm;
 
-import com.cloudera.api.swagger.ClustersResourceApi;
 import com.cloudera.api.swagger.RolesResourceApi;
 import com.cloudera.api.swagger.ServicesResourceApi;
 import com.cloudera.api.swagger.client.ApiException;
-import com.cloudera.api.swagger.model.ApiCluster;
-import com.cloudera.api.swagger.model.ApiClusterList;
 import com.cloudera.api.swagger.model.ApiConfigList;
 import com.cloudera.api.swagger.model.ApiRole;
 import com.cloudera.api.swagger.model.ApiRoleList;
@@ -63,7 +60,6 @@ public class ClouderaManagerServiceDiscovery implements ServiceDiscovery {
 
   static final String API_PATH = "api/v32";
 
-  private static final String CLUSTER_TYPE_ANY = "any";
   private static final String VIEW_SUMMARY     = "summary";
   private static final String VIEW_FULL        = "full";
 
@@ -146,33 +142,15 @@ public class ClouderaManagerServiceDiscovery implements ServiceDiscovery {
   }
 
   @Override
-  public Map<String, Cluster> discover(GatewayConfig gatewayConfig, ServiceDiscoveryConfig discoveryConfig) {
-    Map<String, Cluster> clusters = new HashMap<>();
-
-    DiscoveryApiClient client = getClient(discoveryConfig);
-    List<ApiCluster> apiClusters = getClusters(client);
-    for (ApiCluster apiCluster : apiClusters) {
-      String clusterName = apiCluster.getName();
-      log.discoveredCluster(clusterName, apiCluster.getFullVersion());
-
-      ClouderaManagerCluster cluster = discover(gatewayConfig, discoveryConfig, clusterName, client);
-      clusters.put(clusterName, cluster);
-    }
-
-    return clusters;
-  }
-
-  @Override
   public ClouderaManagerCluster discover(GatewayConfig          gatewayConfig,
                                          ServiceDiscoveryConfig discoveryConfig,
                                          String                 clusterName) {
-    return discover(gatewayConfig, discoveryConfig, clusterName, getClient(discoveryConfig));
+    return discover(discoveryConfig, clusterName, getClient(discoveryConfig));
   }
 
-  protected ClouderaManagerCluster discover(GatewayConfig          gatewayConfig,
-                                            ServiceDiscoveryConfig discoveryConfig,
-                                            String                 clusterName,
-                                            DiscoveryApiClient     client) {
+  protected ClouderaManagerCluster discover(ServiceDiscoveryConfig discoveryConfig,
+                                            String clusterName,
+                                            DiscoveryApiClient client) {
     ClouderaManagerCluster cluster = null;
 
     if (clusterName == null || clusterName.isEmpty()) {
@@ -183,7 +161,7 @@ public class ClouderaManagerServiceDiscovery implements ServiceDiscovery {
     try {
       cluster = discoverCluster(client, clusterName);
 
-      if (configChangeMonitor != null) {
+      if (configChangeMonitor != null && cluster != null) {
         // Notify the cluster config monitor about these cluster configuration details
         configChangeMonitor.addServiceConfiguration(cluster, discoveryConfig);
       }
@@ -194,31 +172,13 @@ public class ClouderaManagerServiceDiscovery implements ServiceDiscovery {
     return cluster;
   }
 
-  private static List<ApiCluster> getClusters(DiscoveryApiClient client) {
-    List<ApiCluster> clusters = new ArrayList<>();
-    try {
-      ClustersResourceApi clustersResourceApi = new ClustersResourceApi(client);
-      ApiClusterList clusterList = clustersResourceApi.readClusters(CLUSTER_TYPE_ANY, VIEW_SUMMARY);
-      if (clusterList != null) {
-        clusters.addAll(clusterList.getItems());
-      }
-    } catch (Exception e) {
-      log.clusterDiscoveryError(CLUSTER_TYPE_ANY, e);
-    }
-    return clusters;
-  }
-
   private static ClouderaManagerCluster discoverCluster(DiscoveryApiClient client, String clusterName)
       throws ApiException {
-    ClouderaManagerCluster cluster;
-
     ServicesResourceApi servicesResourceApi = new ServicesResourceApi(client);
     RolesResourceApi rolesResourceApi = new RolesResourceApi(client);
 
     log.discoveringCluster(clusterName);
 
-    cluster = new ClouderaManagerCluster(clusterName);
-
     Set<ServiceModel> serviceModels = new HashSet<>();
 
     ApiServiceList serviceList = getClusterServices(servicesResourceApi, clusterName);
@@ -271,11 +231,11 @@ public class ClouderaManagerServiceDiscovery implements ServiceDiscovery {
           }
         }
       }
+      ClouderaManagerCluster cluster = new ClouderaManagerCluster(clusterName);
+      cluster.addServiceModels(serviceModels);
+      return cluster;
     }
-
-    cluster.addServiceModels(serviceModels);
-
-    return cluster;
+    return null;
   }
 
   private static ApiServiceList getClusterServices(final ServicesResourceApi servicesResourceApi,
diff --git a/gateway-discovery-cm/src/test/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscoveryTest.java b/gateway-discovery-cm/src/test/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscoveryTest.java
index 1f947a0..67b0807 100644
--- a/gateway-discovery-cm/src/test/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscoveryTest.java
+++ b/gateway-discovery-cm/src/test/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscoveryTest.java
@@ -1178,7 +1178,7 @@ public class ClouderaManagerServiceDiscoveryTest {
 
     // Invoke the service discovery
     ClouderaManagerServiceDiscovery cmsd = new ClouderaManagerServiceDiscovery(true);
-    ServiceDiscovery.Cluster cluster = cmsd.discover(gwConf, sdConfig, clusterName, mockClient);
+    ServiceDiscovery.Cluster cluster = cmsd.discover(sdConfig, clusterName, mockClient);
     assertNotNull(cluster);
     assertEquals(clusterName, cluster.getName());
     return cluster;
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/GatewayMessages.java b/gateway-server/src/main/java/org/apache/knox/gateway/GatewayMessages.java
index da364bc..fb32c35 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/GatewayMessages.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/GatewayMessages.java
@@ -707,4 +707,9 @@ public interface GatewayMessages {
 
   @Message(level = MessageLevel.ERROR, text = "Error while initiatalizing {0}: {1}")
   void errorInitializingService(String implementation, String error, @StackTrace(level = MessageLevel.DEBUG) Exception e);
+
+  @Message(level = MessageLevel.ERROR,
+          text = "Unable to complete service discovery for cluster {0} topology = {1}.")
+  void failedToDiscoverClusterServices(String clusterName, String topologyName,
+                                       @StackTrace(level = MessageLevel.DEBUG) Exception e);
 }
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/topology/monitor/DescriptorsMonitor.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/topology/monitor/DescriptorsMonitor.java
index 1823b45..e00453a 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/services/topology/monitor/DescriptorsMonitor.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/topology/monitor/DescriptorsMonitor.java
@@ -34,6 +34,7 @@ import org.apache.knox.gateway.config.GatewayConfig;
 import org.apache.knox.gateway.i18n.messages.MessagesFactory;
 import org.apache.knox.gateway.services.security.AliasService;
 import org.apache.knox.gateway.services.topology.impl.DefaultTopologyService;
+import org.apache.knox.gateway.topology.simple.DiscoveryException;
 import org.apache.knox.gateway.topology.simple.SimpleDescriptorHandler;
 
 public class DescriptorsMonitor extends FileAlterationListenerAdaptor implements FileFilter {
@@ -123,6 +124,8 @@ public class DescriptorsMonitor extends FileAlterationListenerAdaptor implements
       for (List<String> descs : providerConfigReferences.values()) {
         descs.remove(descriptorName);
       }
+    } catch (DiscoveryException e) {
+      LOG.failedToDiscoverClusterServices(e.getClusterName(), e.getTopologyName(), e);
     } catch (Exception e) {
       LOG.simpleDescriptorHandlingError(file.getName(), e);
     }
diff --git a/gateway-server/src/test/java/org/apache/knox/gateway/topology/discovery/test/extension/DummyServiceDiscovery.java b/gateway-server/src/test/java/org/apache/knox/gateway/topology/discovery/test/extension/DummyServiceDiscovery.java
index f5d25db..2309279 100644
--- a/gateway-server/src/test/java/org/apache/knox/gateway/topology/discovery/test/extension/DummyServiceDiscovery.java
+++ b/gateway-server/src/test/java/org/apache/knox/gateway/topology/discovery/test/extension/DummyServiceDiscovery.java
@@ -66,11 +66,6 @@ public class DummyServiceDiscovery implements ServiceDiscovery {
     }
 
     @Override
-    public Map<String, Cluster> discover(GatewayConfig gwConfig, ServiceDiscoveryConfig config) {
-        return CLUSTERS;
-    }
-
-    @Override
     public Cluster discover(GatewayConfig gwConfig, ServiceDiscoveryConfig config, String clusterName) {
         return DUMMY;
     }
diff --git a/gateway-server/src/test/java/org/apache/knox/gateway/topology/discovery/test/extension/PropertiesFileServiceDiscovery.java b/gateway-server/src/test/java/org/apache/knox/gateway/topology/discovery/test/extension/PropertiesFileServiceDiscovery.java
index e5ad8f3..f6be077 100644
--- a/gateway-server/src/test/java/org/apache/knox/gateway/topology/discovery/test/extension/PropertiesFileServiceDiscovery.java
+++ b/gateway-server/src/test/java/org/apache/knox/gateway/topology/discovery/test/extension/PropertiesFileServiceDiscovery.java
@@ -45,9 +45,7 @@ class PropertiesFileServiceDiscovery implements ServiceDiscovery {
         return TYPE;
     }
 
-    @Override
-    public Map<String, ServiceDiscovery.Cluster> discover(GatewayConfig gatewayConfig,
-                                                          ServiceDiscoveryConfig discoveryConfig) {
+    private Map<String, ServiceDiscovery.Cluster> discover(ServiceDiscoveryConfig discoveryConfig) {
 
         Map<String, ServiceDiscovery.Cluster> result = new HashMap<>();
 
@@ -110,7 +108,7 @@ class PropertiesFileServiceDiscovery implements ServiceDiscovery {
     public ServiceDiscovery.Cluster discover(GatewayConfig          gwConfig,
                                              ServiceDiscoveryConfig discoveryConfig,
                                              String                 clusterName) {
-        Map<String, ServiceDiscovery.Cluster> clusters = discover(gwConfig, discoveryConfig);
+        Map<String, ServiceDiscovery.Cluster> clusters = discover(discoveryConfig);
         return clusters.get(clusterName);
     }
 
diff --git a/gateway-server/src/test/java/org/apache/knox/gateway/topology/discovery/test/extension/SneakyServiceDiscoveryImpl.java b/gateway-server/src/test/java/org/apache/knox/gateway/topology/discovery/test/extension/SneakyServiceDiscoveryImpl.java
index 71cc3a1..cf252fd 100644
--- a/gateway-server/src/test/java/org/apache/knox/gateway/topology/discovery/test/extension/SneakyServiceDiscoveryImpl.java
+++ b/gateway-server/src/test/java/org/apache/knox/gateway/topology/discovery/test/extension/SneakyServiceDiscoveryImpl.java
@@ -20,8 +20,6 @@ import org.apache.knox.gateway.config.GatewayConfig;
 import org.apache.knox.gateway.topology.discovery.ServiceDiscovery;
 import org.apache.knox.gateway.topology.discovery.ServiceDiscoveryConfig;
 
-import java.util.Map;
-
 public class SneakyServiceDiscoveryImpl implements ServiceDiscovery {
     @Override
     public String getType() {
@@ -29,11 +27,6 @@ public class SneakyServiceDiscoveryImpl implements ServiceDiscovery {
     }
 
     @Override
-    public Map<String, Cluster> discover(GatewayConfig gwConfig, ServiceDiscoveryConfig config) {
-        return null;
-    }
-
-    @Override
     public Cluster discover(GatewayConfig gwConfig, ServiceDiscoveryConfig config, String clusterName) {
         return null;
     }
diff --git a/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandlerTest.java b/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandlerTest.java
index 3610389..4623089 100644
--- a/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandlerTest.java
+++ b/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandlerTest.java
@@ -823,38 +823,24 @@ public class SimpleDescriptorHandlerTest {
 
     @Test
     public void shouldAllowKnoxServiceWithoutUrlsAndParams() throws Exception {
-      final String clusterName = "testCluster";
       File providerConfig =  File.createTempFile("testKnoxProvider", ".xml");
       FileUtils.write(providerConfig, TEST_PROVIDER_CONFIG, StandardCharsets.UTF_8);
 
-      final File discoveryConfig = File.createTempFile("testKnoxDiscoveryConfig", ".properties");
-      final Properties discoveryProperties = new Properties();
-      discoveryProperties.setProperty(clusterName + ".name", clusterName);
-
-      try (OutputStream outputStream = Files.newOutputStream(discoveryConfig.toPath())){
-          discoveryProperties.store(outputStream, null);
-      }
-
       File topologyFile = null;
       try {
         final File destDir = new File(System.getProperty("java.io.tmpdir")).getCanonicalFile();
         final GatewayConfig gc = EasyMock.createNiceMock(GatewayConfig.class);
 
-        final SimpleDescriptor testDescriptor = EasyMock.createNiceMock(SimpleDescriptor.class);
-        EasyMock.expect(testDescriptor.getName()).andReturn("testKnoxDescriptor").anyTimes();
-        EasyMock.expect(testDescriptor.getProviderConfig()).andReturn(providerConfig.getAbsolutePath()).anyTimes();
-        EasyMock.expect(testDescriptor.getDiscoveryAddress()).andReturn(discoveryConfig.getAbsolutePath()).anyTimes();
-        EasyMock.expect(testDescriptor.getDiscoveryType()).andReturn("PROPERTIES_FILE").anyTimes();
-        EasyMock.expect(testDescriptor.getDiscoveryUser()).andReturn(null).anyTimes();
-        EasyMock.expect(testDescriptor.getCluster()).andReturn(clusterName).anyTimes();
+        final SimpleDescriptorImpl testDescriptor = new SimpleDescriptorImpl();
+        testDescriptor.setProviderConfig(providerConfig.getAbsolutePath());
 
         final SimpleDescriptor.Service knoxService = EasyMock.createNiceMock(SimpleDescriptor.Service.class);
         EasyMock.expect(knoxService.getName()).andReturn("KNOX").anyTimes();
         EasyMock.expect(knoxService.getURLs()).andReturn(null).anyTimes();
         EasyMock.expect(knoxService.getParams()).andReturn(null).anyTimes();
         List<SimpleDescriptor.Service> serviceMocks = Collections.singletonList(knoxService);
-        EasyMock.expect(testDescriptor.getServices()).andReturn(serviceMocks).anyTimes();
-        EasyMock.replay(gc, knoxService, testDescriptor);
+        testDescriptor.setServices(serviceMocks);
+        EasyMock.replay(gc, knoxService);
 
         final Map<String, File> handleResult = SimpleDescriptorHandler.handle(gc, testDescriptor, destDir, destDir);
         topologyFile = handleResult.get(SimpleDescriptorHandler.RESULT_TOPOLOGY);
@@ -865,7 +851,6 @@ public class SimpleDescriptorHandlerTest {
         assertThat(topologyXml, hasXPath("/topology/service/role", is(equalTo("KNOX"))));
       } finally {
         providerConfig.delete();
-        discoveryConfig.delete();
         if (topologyFile != null) {
           topologyFile.delete();
         }
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/topology/discovery/ServiceDiscovery.java b/gateway-spi/src/main/java/org/apache/knox/gateway/topology/discovery/ServiceDiscovery.java
index 50c3af6..9533500 100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/topology/discovery/ServiceDiscovery.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/topology/discovery/ServiceDiscovery.java
@@ -37,18 +37,6 @@ public interface ServiceDiscovery {
      */
     String getType();
 
-
-    /**
-     * Discover details of all the clusters known to the target registry.
-     *
-     * @param gwConfig The gateway configuration
-     * @param config The configuration for the discovery invocation
-     *
-     * @return A Map of the discovered service data, keyed by the cluster name.
-     */
-    Map<String, Cluster> discover(GatewayConfig gwConfig, ServiceDiscoveryConfig config);
-
-
     /**
      * Discover details for a single cluster.
      *
diff --git a/gateway-test/src/test/java/org/apache/knox/gateway/SimpleDescriptorHandlerFuncTest.java b/gateway-test/src/test/java/org/apache/knox/gateway/SimpleDescriptorHandlerFuncTest.java
index 220e025..4344d74 100644
--- a/gateway-test/src/test/java/org/apache/knox/gateway/SimpleDescriptorHandlerFuncTest.java
+++ b/gateway-test/src/test/java/org/apache/knox/gateway/SimpleDescriptorHandlerFuncTest.java
@@ -279,13 +279,28 @@ public class SimpleDescriptorHandlerFuncTest {
     }
 
     @Override
-    public Map<String, Cluster> discover(GatewayConfig gwConfig, ServiceDiscoveryConfig config) {
-      return Collections.emptyMap();
-    }
-
-    @Override
     public Cluster discover(GatewayConfig gwConfig, ServiceDiscoveryConfig config, String clusterName) {
-      return null;
+      return new Cluster() {
+        @Override
+        public String getName() {
+          return null;
+        }
+
+        @Override
+        public List<String> getServiceURLs(String serviceName) {
+          return null;
+        }
+
+        @Override
+        public List<String> getServiceURLs(String serviceName, Map<String, String> serviceParams) {
+          return null;
+        }
+
+        @Override
+        public ZooKeeperConfig getZooKeeperConfiguration(String serviceName) {
+          return null;
+        }
+      };
     }
   }
 }
diff --git a/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/DiscoveryException.java b/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/DiscoveryException.java
new file mode 100644
index 0000000..b939aaa
--- /dev/null
+++ b/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/DiscoveryException.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.knox.gateway.topology.simple;
+
+public class DiscoveryException extends RuntimeException {
+    private final String clusterName;
+    private final String topologyName;
+
+    public DiscoveryException(String clusterName, String topologyName) {
+        this.clusterName = clusterName;
+        this.topologyName = topologyName;
+    }
+
+    public String getClusterName() {
+        return clusterName;
+    }
+
+    public String getTopologyName() {
+        return topologyName;
+    }
+}
diff --git a/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandler.java b/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandler.java
index ca22b68..d809319 100644
--- a/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandler.java
+++ b/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandler.java
@@ -110,7 +110,7 @@ public class SimpleDescriptorHandler {
         if (shouldPerformDiscovery(desc)) {
             cluster = performDiscovery(config, desc, gatewayServices);
             if (cluster == null) {
-                log.failedToDiscoverClusterServices(desc.getName());
+                throw new DiscoveryException(desc.getCluster(), desc.getName());
             }
         } else {
             log.discoveryNotConfiguredForDescriptor(desc.getName());
diff --git a/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorMessages.java b/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorMessages.java
index e2642ae..c3d2e92 100644
--- a/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorMessages.java
+++ b/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorMessages.java
@@ -32,10 +32,6 @@ public interface SimpleDescriptorMessages {
             text = "The \"{0}\" descriptor does not include discovery-type.")
     void missingDiscoveryTypeInDescriptor(String descriptorName);
 
-    @Message(level = MessageLevel.ERROR,
-            text = "Unable to complete service discovery for cluster {0}.")
-    void failedToDiscoverClusterServices(String descriptorName);
-
     @Message(level = MessageLevel.WARN,
             text = "No valid URLs were discovered for {0} in the {1} cluster.")
     void failedToDiscoverClusterServiceURLs(String serviceName, String clusterName);