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

jonathanhurley pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/trunk by this push:
     new cd39737  [AMBARI-24812] - Implement New Upgrade Check Which Warns 
About Missing Plugin Checks (#2497)
cd39737 is described below

commit cd397373e6d7a0d976ee50e61466cfe184cfb6a7
Author: Jonathan Hurley <[email protected]>
AuthorDate: Tue Oct 23 14:22:01 2018 -0400

    [AMBARI-24812] - Implement New Upgrade Check Which Warns About Missing 
Plugin Checks (#2497)
---
 .../server/checks/PluginChecksLoadedCheck.java     | 172 +++++++++++++++++++++
 .../ambari/server/checks/UpgradeCheckRegistry.java |  68 +++++++-
 .../server/checks/PluginChecksLoadedCheckTest.java | 101 ++++++++++++
 .../PreUpgradeCheckResourceProviderTest.java       |   2 +-
 4 files changed, 335 insertions(+), 8 deletions(-)

diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/checks/PluginChecksLoadedCheck.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/checks/PluginChecksLoadedCheck.java
new file mode 100644
index 0000000..5538919
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/checks/PluginChecksLoadedCheck.java
@@ -0,0 +1,172 @@
+/*
+ * 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.ambari.server.checks;
+
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.ambari.annotations.UpgradeCheckInfo;
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.spi.upgrade.UpgradeCheckDescription;
+import org.apache.ambari.spi.upgrade.UpgradeCheckGroup;
+import org.apache.ambari.spi.upgrade.UpgradeCheckRequest;
+import org.apache.ambari.spi.upgrade.UpgradeCheckResult;
+import org.apache.ambari.spi.upgrade.UpgradeCheckStatus;
+import org.apache.ambari.spi.upgrade.UpgradeCheckType;
+import org.apache.ambari.spi.upgrade.UpgradeType;
+import org.codehaus.jackson.annotate.JsonProperty;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+/**
+ * The {@link PluginChecksLoadedCheck} class is used to display warnings if
+ * there were any stack plugin checks which were not able to be loaded.
+ */
+@Singleton
+@UpgradeCheckInfo(
+    group = UpgradeCheckGroup.INFORMATIONAL_WARNING,
+    required = { UpgradeType.ROLLING, UpgradeType.NON_ROLLING, 
UpgradeType.HOST_ORDERED })
+public class PluginChecksLoadedCheck extends ClusterCheck {
+
+  private static final UpgradeCheckDescription PLUGIN_CHECK_LOAD_FAILURE = new 
UpgradeCheckDescription(
+      "PLUGIN_CHECK_LOAD_FAILURE", UpgradeCheckType.CLUSTER, "Plugin Upgrade 
Checks",
+      new ImmutableMap.Builder<String, 
String>().put(UpgradeCheckDescription.DEFAULT,
+          "The following upgrade checks could not be loaded and were not run. "
+              + "Although this will not prevent your ability to upgrade, it is 
advised that you "
+              + "correct these checks before proceeding.").build());
+
+  @Inject
+  Provider<UpgradeCheckRegistry> m_upgradeCheckRegistryProvider;
+
+  /**
+   * Constructor.
+   */
+  public PluginChecksLoadedCheck() {
+    super(PLUGIN_CHECK_LOAD_FAILURE);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public UpgradeCheckResult perform(UpgradeCheckRequest request) throws 
AmbariException {
+    UpgradeCheckResult result = new UpgradeCheckResult(this, 
UpgradeCheckStatus.PASS);
+
+    // get the fully-qualified class names
+    Set<String> failedPluginClasses = 
m_upgradeCheckRegistryProvider.get().getFailedPluginClassNames();
+
+    // quick return a pass
+    if (null == failedPluginClasses || failedPluginClasses.isEmpty()) {
+      return result;
+    }
+
+    // strip out the package name for readability
+    Set<FailedPluginClassDetail> failedPluginSimpleClasses = 
failedPluginClasses.stream()
+        .map(FailedPluginClassDetail::new)
+        .collect(Collectors.toSet());
+
+    // check for failure
+    if (failedPluginClasses.size() > 0) {
+      result.setStatus(UpgradeCheckStatus.WARNING);
+      result.getFailedDetail().addAll(failedPluginSimpleClasses);
+      result.setFailReason(getFailReason(result, request));
+
+      result.getFailedOn().addAll(failedPluginSimpleClasses.stream()
+          .map(detail -> detail.toSimpleString())
+          .collect(Collectors.toSet()));
+    }
+
+    return result;
+  }
+
+  /**
+   * Used to represent serializable structured information about plugin upgrade
+   * checks which could not load.
+   */
+  static class FailedPluginClassDetail {
+    final String m_fullyQualifiedClass;
+
+    @JsonProperty("package_name")
+    final String m_packageName;
+
+    @JsonProperty("class_name")
+    final String m_className;
+
+    FailedPluginClassDetail(String fullyQualifiedClass) {
+      m_fullyQualifiedClass = fullyQualifiedClass;
+
+      int indexOfLastDot = fullyQualifiedClass.lastIndexOf('.');
+      if(indexOfLastDot >= 0) {
+        m_packageName = fullyQualifiedClass.substring(0, indexOfLastDot);
+        m_className = fullyQualifiedClass.substring(indexOfLastDot + 1);
+      } else {
+        m_packageName = "";
+        m_className = fullyQualifiedClass;
+      }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+      return m_fullyQualifiedClass;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toSimpleString() {
+      return m_className;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+      return Objects.hash(m_packageName, m_className);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj) {
+        return true;
+      }
+
+      if (obj == null) {
+        return false;
+      }
+
+      if (getClass() != obj.getClass()) {
+        return false;
+      }
+
+      FailedPluginClassDetail other = (FailedPluginClassDetail) obj;
+      return Objects.equals(m_packageName, other.m_packageName)
+          && Objects.equals(m_className, other.m_className);
+    }
+  }
+}
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/checks/UpgradeCheckRegistry.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/checks/UpgradeCheckRegistry.java
index 52ccc7e..44d8cba 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/checks/UpgradeCheckRegistry.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/checks/UpgradeCheckRegistry.java
@@ -18,6 +18,7 @@
 package org.apache.ambari.server.checks;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.LinkedList;
@@ -25,6 +26,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
+import java.util.stream.Collectors;
 
 import org.apache.ambari.annotations.UpgradeCheckInfo;
 import org.apache.ambari.server.AmbariException;
@@ -80,9 +82,10 @@ public class UpgradeCheckRegistry {
     new PreUpgradeCheckComparator());
 
   /**
-   * The upgrade checks discovered on a per-stack, per-upgrade pack basis.
+   * Represents the upgrade checks (both those which were able to be loaded and
+   * which failed to be loaded) which were discovered in the upgrade pack.
    */
-  private final Map<UpgradePack, Set<UpgradeCheck>> m_pluginChecks = new 
HashMap<>();
+  private final Map<UpgradePack, PluginUpgradeChecks> m_pluginChecks = new 
HashMap<>();
 
   /**
    * Register an upgrade check.
@@ -130,11 +133,13 @@ public class UpgradeCheckRegistry {
 
     // for any checks defined in the upgrade pack, add them since they have 
been
     // explicitely defined
-    Set<UpgradeCheck> pluginChecks = m_pluginChecks.get(upgradePack);
+    PluginUpgradeChecks pluginChecks = m_pluginChecks.get(upgradePack);
 
     // see if the upgrade checks have been processes for this pack yet
     if (null == pluginChecks) {
-      pluginChecks = new TreeSet<>(new PreUpgradeCheckComparator());
+      pluginChecks = new PluginUpgradeChecks(new TreeSet<>(new 
PreUpgradeCheckComparator()),
+          new TreeSet<>());
+
       m_pluginChecks.put(upgradePack, pluginChecks);
 
       List<String> pluginCheckClassNames = upgradePack.getPrerequisiteChecks();
@@ -145,12 +150,26 @@ public class UpgradeCheckRegistry {
 
     final Set<UpgradeCheck> combinedUpgradeChecks = new TreeSet<>(new 
PreUpgradeCheckComparator());
     combinedUpgradeChecks.addAll(builtInRequiredChecks);
-    combinedUpgradeChecks.addAll(pluginChecks);
+    combinedUpgradeChecks.addAll(pluginChecks.m_loadedChecks);
 
     return new LinkedList<>(combinedUpgradeChecks);
   }
 
   /**
+   * Gets all of the upgrade check classes defined in upgrade packs which were
+   * not able to be loaded and instantiated.
+   *
+   * @return all of the failed upgrade check classes.
+   */
+  public Set<String> getFailedPluginClassNames() {
+    Collection<PluginUpgradeChecks> pluginUpgradeChecks = 
m_pluginChecks.values();
+
+    return pluginUpgradeChecks.stream()
+        .flatMap(plugins -> plugins.m_failedChecks.stream())
+        .collect(Collectors.toSet());
+  }
+
+  /**
    * Uses the library classloader from the the target stack in order to find 
any
    * plugin-in {@link UpgradeCheck}s which are declared in the upgrade pack.
    *
@@ -161,7 +180,7 @@ public class UpgradeCheckRegistry {
    */
   @SuppressWarnings("unchecked")
   private void loadPluginUpgradeChecksFromStack(UpgradePack upgradePack,
-      Set<UpgradeCheck> pluginChecks) throws AmbariException {
+      PluginUpgradeChecks pluginChecks) throws AmbariException {
     List<String> pluginCheckClassNames = upgradePack.getPrerequisiteChecks();
     StackId ownerStackId = upgradePack.getOwnerStackId();
     StackInfo stackInfo = metainfoProvider.get().getStack(ownerStackId);
@@ -174,11 +193,14 @@ public class UpgradeCheckRegistry {
               pluginCheckClassName);
 
           UpgradeCheck upgradeCheck = 
m_injector.getInstance(upgradeCheckClass);
-          pluginChecks.add(upgradeCheck);
+          pluginChecks.m_loadedChecks.add(upgradeCheck);
 
           LOG.info("Registered pre-upgrade check {} for stack {}", 
upgradeCheckClass, ownerStackId);
         } catch (Exception exception) {
           LOG.error("Unable to load the upgrade check {}", 
pluginCheckClassName, exception);
+
+          // keep track of the failed check
+          pluginChecks.m_failedChecks.add(pluginCheckClassName);
         }
       }
     } else {
@@ -257,4 +279,36 @@ public class UpgradeCheckRegistry {
       return clazz1.getName().compareTo(clazz2.getName());
     }
   }
+
+  /**
+   * Encapsulates information about which plugins could and could not be loaded
+   * from a stack.
+   */
+  public static final class PluginUpgradeChecks {
+    /**
+     * The upgrade checks discovered on a per-stack, per-upgrade pack basis.
+     */
+    private final Set<UpgradeCheck> m_loadedChecks;
+
+    /**
+     * The upgrade checks discovered on a per-stack, per-upgrade pack basis
+     * which could not be loaded from the stack's 3rd party library
+     * {@link ClassLoader}.
+     */
+    private final Set<String> m_failedChecks;
+
+    /**
+     * Constructor.
+     *
+     * @param loadedChecks
+     *          the checks which have been successfully loaded from the stack.
+     * @param failedChecks
+     *          the checks which failed to load from the stack and cannot run.
+     */
+    private PluginUpgradeChecks(Set<UpgradeCheck> loadedChecks, Set<String> 
failedChecks) {
+      m_loadedChecks = loadedChecks;
+      m_failedChecks = failedChecks;
+    }
+
+  }
 }
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/checks/PluginChecksLoadedCheckTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/checks/PluginChecksLoadedCheckTest.java
new file mode 100644
index 0000000..980d6c1
--- /dev/null
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/checks/PluginChecksLoadedCheckTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.ambari.server.checks;
+
+import static org.easymock.EasyMock.expect;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.ambari.spi.upgrade.UpgradeCheckRequest;
+import org.apache.ambari.spi.upgrade.UpgradeCheckResult;
+import org.apache.ambari.spi.upgrade.UpgradeCheckStatus;
+import org.apache.ambari.spi.upgrade.UpgradeType;
+import org.apache.commons.lang.StringUtils;
+import org.easymock.EasyMockSupport;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.collect.Sets;
+import com.google.inject.Provider;
+
+/**
+ * Tests for {@link PluginChecksLoadedCheck}
+ */
+public class PluginChecksLoadedCheckTest extends EasyMockSupport {
+
+  private final PluginChecksLoadedCheck m_check = new 
PluginChecksLoadedCheck();
+  private final UpgradeCheckRegistry m_upgradeCheckRegistry = createNiceMock(
+      UpgradeCheckRegistry.class);
+
+  @Before
+  public void before() throws Exception {
+
+    m_check.m_upgradeCheckRegistryProvider = new 
Provider<UpgradeCheckRegistry>() {
+      @Override
+      public UpgradeCheckRegistry get() {
+        return m_upgradeCheckRegistry;
+      }
+    };
+  }
+
+  /**
+   * @throws Exception
+   */
+  @Test
+  public void testPerform() throws Exception {
+    Set<String> failedClasses = Sets.newHashSet("foo.bar.Baz", "foo.bar.Baz2");
+    expect(m_upgradeCheckRegistry.getFailedPluginClassNames()).andReturn(
+        failedClasses).atLeastOnce();
+
+    replayAll();
+
+    UpgradeCheckRequest request = new UpgradeCheckRequest(null, 
UpgradeType.ROLLING, null, null);
+    UpgradeCheckResult check = m_check.perform(request);
+
+    Assert.assertEquals(UpgradeCheckStatus.WARNING, check.getStatus());
+    List<Object> failedDetails = check.getFailedDetail();
+    assertEquals(2, failedDetails.size());
+    assertEquals(2, check.getFailedOn().size());
+    assertTrue(check.getFailedOn().contains("Baz"));
+
+    verifyAll();
+  }
+
+  /**
+   * @throws Exception
+   */
+  @Test
+  public void testPerformWithSuccess() throws Exception {
+    expect(m_upgradeCheckRegistry.getFailedPluginClassNames()).andReturn(
+        new HashSet<>()).atLeastOnce();
+    replayAll();
+
+    UpgradeCheckRequest request = new UpgradeCheckRequest(null, 
UpgradeType.ROLLING, null, null);
+    UpgradeCheckResult check = m_check.perform(request);
+
+    Assert.assertEquals(UpgradeCheckStatus.PASS, check.getStatus());
+    Assert.assertTrue(StringUtils.isBlank(check.getFailReason()));
+
+    verifyAll();
+  }
+}
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/PreUpgradeCheckResourceProviderTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/PreUpgradeCheckResourceProviderTest.java
index 4f5e298..631e7ec 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/PreUpgradeCheckResourceProviderTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/PreUpgradeCheckResourceProviderTest.java
@@ -227,7 +227,7 @@ public class PreUpgradeCheckResourceProviderTest extends 
EasyMockSupport {
     // types. The value being asserted here is a combination of built-in checks
     // which are required for the upgrade type as well as any provided checks
     // discovered in the stack
-    Assert.assertEquals(19, resources.size());
+    Assert.assertEquals(20, resources.size());
 
     // find the service check provided by the library classloader and verify 
it ran
     Resource customUpgradeCheck = null;

Reply via email to