Browse Source

AMBARI-17758. LogSearch Integration NullPointerException when LogSearch connection not available. (rnettleton)

Bob Nettleton 9 years ago
parent
commit
c1fafca03f

+ 5 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java

@@ -55,6 +55,8 @@ import org.apache.ambari.server.controller.internal.MemberResourceProvider;
 import org.apache.ambari.server.controller.internal.RepositoryVersionResourceProvider;
 import org.apache.ambari.server.controller.internal.ServiceResourceProvider;
 import org.apache.ambari.server.controller.internal.UpgradeResourceProvider;
+import org.apache.ambari.server.controller.logging.LoggingRequestHelperFactory;
+import org.apache.ambari.server.controller.logging.LoggingRequestHelperFactoryImpl;
 import org.apache.ambari.server.controller.metrics.MetricPropertyProviderFactory;
 import org.apache.ambari.server.controller.metrics.timeline.cache.TimelineMetricCacheEntryFactory;
 import org.apache.ambari.server.controller.metrics.timeline.cache.TimelineMetricCacheProvider;
@@ -367,6 +369,9 @@ public class ControllerModule extends AbstractModule {
 
     bind(AuthenticationEntryPoint.class).to(AmbariEntryPoint.class).in(Scopes.SINGLETON);
 
+    // factory to create LoggingRequestHelper instances for LogSearch integration
+    bind(LoggingRequestHelperFactory.class).to(LoggingRequestHelperFactoryImpl.class);
+
     requestStaticInjection(DatabaseConsistencyCheckHelper.class);
     requestStaticInjection(KerberosChecker.class);
     requestStaticInjection(AuthorizationHelper.class);

+ 22 - 8
ambari-server/src/main/java/org/apache/ambari/server/controller/logging/LogSearchDataRetrievalService.java

@@ -65,6 +65,9 @@ public class LogSearchDataRetrievalService extends AbstractService {
   @Inject
   private Configuration configuration;
 
+  @Inject
+  private LoggingRequestHelperFactory loggingRequestHelperFactory;
+
   /**
    * A Cache of host+component names to a set of log files associated with
    *  that Host/Component combination.  This data is retrieved from the
@@ -154,20 +157,31 @@ public class LogSearchDataRetrievalService extends AbstractService {
       return result;
     } else {
       // create URI and add to cache before returning
-      LoggingRequestHelper helper =
-        new LoggingRequestHelperFactoryImpl().getHelper(getController(), cluster);
-      String tailFileURI =
-        helper.createLogFileTailURI(baseURI, component, host);
-
-      if (tailFileURI != null) {
-        logFileTailURICache.put(key, tailFileURI);
-        return tailFileURI;
+      if (loggingRequestHelperFactory != null) {
+        LoggingRequestHelper helper =
+          loggingRequestHelperFactory.getHelper(getController(), cluster);
+
+        if (helper != null) {
+          String tailFileURI =
+            helper.createLogFileTailURI(baseURI, component, host);
+
+          if (tailFileURI != null) {
+            logFileTailURICache.put(key, tailFileURI);
+            return tailFileURI;
+          }
+        }
+      } else {
+        LOG.debug("LoggingRequestHelperFactory not set on the retrieval service, this probably indicates an error in setup of this service.");
       }
     }
 
     return null;
   }
 
+  protected void setLoggingRequestHelperFactory(LoggingRequestHelperFactory loggingRequestHelperFactory) {
+    this.loggingRequestHelperFactory = loggingRequestHelperFactory;
+  }
+
   private void startLogSearchFileNameRequest(String host, String component, String cluster) {
     executor.execute(new LogSearchFileNameRequestRunnable(host, component, cluster));
   }

+ 4 - 2
ambari-server/src/main/java/org/apache/ambari/server/controller/logging/LoggingSearchPropertyProvider.java

@@ -92,8 +92,10 @@ public class LoggingSearchPropertyProvider implements PropertyProvider {
             // generate the URIs that can be used by clients to obtain search results/tail log results/etc
             final String searchEngineURI = ambariManagementController.getAmbariServerURI(getFullPathToSearchEngine(clusterName));
             final String logFileTailURI = logSearchDataRetrievalService.getLogFileTailURI(searchEngineURI, mappedComponentNameForLogSearch, hostName, clusterName);
-            // all log files are assumed to be service types for now
-            listOfFileDefinitions.add(new LogFileDefinitionInfo(fileName, LogFileType.SERVICE, searchEngineURI, logFileTailURI));
+            if (logFileTailURI != null) {
+              // all log files are assumed to be service types for now
+              listOfFileDefinitions.add(new LogFileDefinitionInfo(fileName, LogFileType.SERVICE, searchEngineURI, logFileTailURI));
+            }
           }
 
           loggingInfo.setListOfLogFileDefinitions(listOfFileDefinitions);

+ 105 - 0
ambari-server/src/test/java/org/apache/ambari/server/controller/logging/LogSearchDataRetrievalServiceTest.java

@@ -0,0 +1,105 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.ambari.server.controller.logging;
+
+import org.easymock.EasyMockSupport;
+import org.junit.Test;
+
+import static org.easymock.EasyMock.expect;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * This test verifies the basic behavior of the
+ *   LogSearchDataRetrievalServiceTest, and should
+ *   verify the interaction with its dependencies, as
+ *   well as the interaction with the LogSearch
+ *   server.
+ *
+ */
+public class LogSearchDataRetrievalServiceTest {
+
+  @Test
+  public void testGetTailFileWhenHelperIsAvailable() throws Exception {
+    final String expectedHostName = "c6401.ambari.apache.org";
+    final String expectedComponentName = "DATANODE";
+    final String expectedClusterName = "clusterone";
+    final String expectedResultURI = "http://localhost/test/result";
+
+    EasyMockSupport mockSupport = new EasyMockSupport();
+
+    LoggingRequestHelperFactory helperFactoryMock =
+      mockSupport.createMock(LoggingRequestHelperFactory.class);
+
+    LoggingRequestHelper helperMock =
+      mockSupport.createMock(LoggingRequestHelper.class);
+
+    expect(helperFactoryMock.getHelper(null, expectedClusterName)).andReturn(helperMock);
+    expect(helperMock.createLogFileTailURI("http://localhost", expectedComponentName, expectedHostName)).andReturn(expectedResultURI);
+
+    mockSupport.replayAll();
+
+    LogSearchDataRetrievalService retrievalService =
+      new LogSearchDataRetrievalService();
+    retrievalService.setLoggingRequestHelperFactory(helperFactoryMock);
+    // call the initialization routine called by the Google framework
+    retrievalService.doStart();
+
+    String resultTailFileURI =
+      retrievalService.getLogFileTailURI("http://localhost", expectedComponentName, expectedHostName, expectedClusterName);
+
+    assertEquals("TailFileURI was not returned as expected",
+                 expectedResultURI, resultTailFileURI);
+
+    mockSupport.verifyAll();
+  }
+
+  @Test
+  public void testGetTailFileWhenRequestHelperIsNull() throws Exception {
+    final String expectedHostName = "c6401.ambari.apache.org";
+    final String expectedComponentName = "DATANODE";
+    final String expectedClusterName = "clusterone";
+
+    EasyMockSupport mockSupport = new EasyMockSupport();
+
+    LoggingRequestHelperFactory helperFactoryMock =
+      mockSupport.createMock(LoggingRequestHelperFactory.class);
+
+    // return null, to simulate the case where LogSearch Server is
+    // not available for some reason
+    expect(helperFactoryMock.getHelper(null, expectedClusterName)).andReturn(null);
+
+    mockSupport.replayAll();
+
+    LogSearchDataRetrievalService retrievalService =
+      new LogSearchDataRetrievalService();
+    retrievalService.setLoggingRequestHelperFactory(helperFactoryMock);
+    // call the initialization routine called by the Google framework
+    retrievalService.doStart();
+
+    String resultTailFileURI =
+      retrievalService.getLogFileTailURI("http://localhost", expectedComponentName, expectedHostName, expectedClusterName);
+
+    assertNull("TailFileURI should be null in this case",
+               resultTailFileURI);
+
+    mockSupport.verifyAll();
+
+  }
+
+}

+ 153 - 0
ambari-server/src/test/java/org/apache/ambari/server/controller/logging/LoggingSearchPropertyProviderTest.java

@@ -217,6 +217,159 @@ public class LoggingSearchPropertyProviderTest {
     mockSupport.verifyAll();
   }
 
+  /**
+   * Verifies the following:
+   *
+   *   1. That this PropertyProvider implementation uses
+   *      the expected interfaces to make queries to the LogSearch
+   *      service.
+   *   2. That the PropertyProvider queries the current HostComponent
+   *      resource in order to obtain the correct information to send to
+   *      LogSearch.
+   *   3. That the output of the LogSearch query is properly set on the
+   *      HostComponent resource in the expected structure.
+   *   4. That the proper error-handling is in place in the event that a null
+   *      tail log URI is returned by the retrieval service.
+   *
+   *
+   * @throws Exception
+   */
+  @Test
+  public void testBasicCallWithNullTailLogURIReturned() throws Exception {
+    final String expectedLogFilePath =
+      "/var/log/hdfs/hdfs_namenode.log";
+
+    final String expectedSearchEnginePath = "/api/v1/clusters/clusterone/logging/searchEngine";
+
+    final String expectedAmbariURL = "http://c6401.ambari.apache.org:8080";
+
+    final String expectedStackName = "HDP";
+    final String expectedStackVersion = "2.4";
+    final String expectedComponentName = "NAMENODE";
+    final String expectedServiceName = "HDFS";
+    final String expectedLogSearchComponentName = "hdfs_namenode";
+
+    EasyMockSupport mockSupport = new EasyMockSupport();
+
+    Resource resourceMock =
+      mockSupport.createMock(Resource.class);
+    expect(resourceMock.getPropertyValue(PropertyHelper.getPropertyId("HostRoles", "component_name"))).andReturn(expectedComponentName).atLeastOnce();
+    expect(resourceMock.getPropertyValue(PropertyHelper.getPropertyId("HostRoles", "host_name"))).andReturn("c6401.ambari.apache.org").atLeastOnce();
+    expect(resourceMock.getPropertyValue(PropertyHelper.getPropertyId("HostRoles", "cluster_name"))).andReturn("clusterone").atLeastOnce();
+
+    Capture<HostComponentLoggingInfo> captureLogInfo = Capture.newInstance();
+    // expect set method to be called
+    resourceMock.setProperty(eq("logging"), capture(captureLogInfo));
+
+    LogLevelQueryResponse levelQueryResponse =
+      new LogLevelQueryResponse();
+
+    levelQueryResponse.setTotalCount("3");
+    // setup test data for log levels
+    List<NameValuePair> testListOfLogLevels =
+      new LinkedList<NameValuePair>();
+    testListOfLogLevels.add(new NameValuePair("ERROR", "150"));
+    testListOfLogLevels.add(new NameValuePair("WARN", "500"));
+    testListOfLogLevels.add(new NameValuePair("INFO", "2200"));
+
+    levelQueryResponse.setNameValueList(testListOfLogLevels);
+
+    Request requestMock =
+      mockSupport.createMock(Request.class);
+
+    Predicate predicateMock =
+      mockSupport.createMock(Predicate.class);
+
+    AmbariManagementController controllerMock =
+      mockSupport.createMock(AmbariManagementController.class);
+
+    AmbariMetaInfo metaInfoMock =
+      mockSupport.createMock(AmbariMetaInfo.class);
+
+    Clusters clustersMock =
+      mockSupport.createMock(Clusters.class);
+
+    Cluster clusterMock =
+      mockSupport.createMock(Cluster.class);
+
+    StackId stackIdMock =
+      mockSupport.createMock(StackId.class);
+
+    ComponentInfo componentInfoMock =
+      mockSupport.createMock(ComponentInfo.class);
+
+    LogDefinition logDefinitionMock =
+      mockSupport.createMock(LogDefinition.class);
+
+    LogSearchDataRetrievalService dataRetrievalServiceMock =
+      mockSupport.createMock(LogSearchDataRetrievalService.class);
+
+    LoggingRequestHelperFactory loggingRequestHelperFactoryMock =
+      mockSupport.createMock(LoggingRequestHelperFactory.class);
+
+    LoggingRequestHelper loggingRequestHelperMock =
+      mockSupport.createMock(LoggingRequestHelper.class);
+
+    expect(dataRetrievalServiceMock.getLogFileNames(expectedLogSearchComponentName, "c6401.ambari.apache.org", "clusterone")).andReturn(Collections.singleton(expectedLogFilePath)).atLeastOnce();
+    // return null, to simulate the case when the LogSearch service goes down, and the helper object
+    // is not available to continue servicing the request.
+    expect(dataRetrievalServiceMock.getLogFileTailURI(expectedAmbariURL + expectedSearchEnginePath, expectedLogSearchComponentName, "c6401.ambari.apache.org", "clusterone")).andReturn(null).atLeastOnce();
+
+
+    expect(controllerMock.getAmbariServerURI(expectedSearchEnginePath)).
+      andReturn(expectedAmbariURL + expectedSearchEnginePath).atLeastOnce();
+    expect(controllerMock.getAmbariMetaInfo()).andReturn(metaInfoMock).atLeastOnce();
+    expect(controllerMock.getClusters()).andReturn(clustersMock).atLeastOnce();
+    expect(clustersMock.getCluster("clusterone")).andReturn(clusterMock).atLeastOnce();
+    expect(stackIdMock.getStackName()).andReturn(expectedStackName).atLeastOnce();
+    expect(stackIdMock.getStackVersion()).andReturn(expectedStackVersion).atLeastOnce();
+    expect(clusterMock.getCurrentStackVersion()).andReturn(stackIdMock).atLeastOnce();
+    expect(loggingRequestHelperFactoryMock.getHelper(anyObject(AmbariManagementController.class), anyObject(String.class)))
+      .andReturn(loggingRequestHelperMock).atLeastOnce();
+
+    expect(metaInfoMock.getComponentToService(expectedStackName, expectedStackVersion, expectedComponentName)).andReturn(expectedServiceName).atLeastOnce();
+    expect(metaInfoMock.getComponent(expectedStackName, expectedStackVersion, expectedServiceName, expectedComponentName)).andReturn(componentInfoMock).atLeastOnce();
+
+    expect(componentInfoMock.getLogs()).andReturn(Collections.singletonList(logDefinitionMock)).atLeastOnce();
+    expect(logDefinitionMock.getLogId()).andReturn(expectedLogSearchComponentName).atLeastOnce();
+
+    mockSupport.replayAll();
+
+    LoggingSearchPropertyProvider propertyProvider =
+      new LoggingSearchPropertyProvider();
+
+    propertyProvider.setAmbariManagementController(controllerMock);
+    propertyProvider.setLogSearchDataRetrievalService(dataRetrievalServiceMock);
+    propertyProvider.setLoggingRequestHelperFactory(loggingRequestHelperFactoryMock);
+
+    Set<Resource> returnedResources =
+      propertyProvider.populateResources(Collections.singleton(resourceMock), requestMock, predicateMock);
+
+    // verify that the property provider attached
+    // the expected logging structure to the associated resource
+
+    assertEquals("Returned resource set was of an incorrect size",
+      1, returnedResources.size());
+
+    HostComponentLoggingInfo returnedLogInfo =
+      captureLogInfo.getValue();
+
+    assertNotNull("Returned log info should not be null",
+      returnedLogInfo);
+
+    assertEquals("Returned component was not the correct name",
+      "hdfs_namenode", returnedLogInfo.getComponentName());
+
+    assertEquals("Returned list of log file names for this component was incorrect",
+      0, returnedLogInfo.getListOfLogFileDefinitions().size());
+
+    // verify that the log level count information
+    // was not added to the HostComponent resource
+    assertNull(returnedLogInfo.getListOfLogLevels());
+
+    mockSupport.verifyAll();
+  }
+
   /**
    * Verifies that this property provider implementation will
    * properly handle the case of LogSearch not being deployed in