Return-Path: X-Original-To: apmail-ambari-commits-archive@www.apache.org Delivered-To: apmail-ambari-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id B7C0F10D1A for ; Sat, 12 Sep 2015 13:22:42 +0000 (UTC) Received: (qmail 45883 invoked by uid 500); 12 Sep 2015 13:22:42 -0000 Delivered-To: apmail-ambari-commits-archive@ambari.apache.org Received: (qmail 45791 invoked by uid 500); 12 Sep 2015 13:22:42 -0000 Mailing-List: contact commits-help@ambari.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: ambari-dev@ambari.apache.org Delivered-To: mailing list commits@ambari.apache.org Received: (qmail 45780 invoked by uid 99); 12 Sep 2015 13:22:42 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 12 Sep 2015 13:22:42 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 6D696E060C; Sat, 12 Sep 2015 13:22:42 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: jonathanhurley@apache.org To: commits@ambari.apache.org Date: Sat, 12 Sep 2015 13:22:43 -0000 Message-Id: In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [2/2] ambari git commit: AMBARI-13064 - Provide Summary Of Skipped Failures During Upgrade (jonathanhurley) AMBARI-13064 - Provide Summary Of Skipped Failures During Upgrade (jonathanhurley) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/c7a714cf Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/c7a714cf Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/c7a714cf Branch: refs/heads/trunk Commit: c7a714cf0f4d0a86f6840a5e5d7997a185ae4166 Parents: 70ca850 Author: Jonathan Hurley Authored: Thu Sep 10 15:06:21 2015 -0400 Committer: Jonathan Hurley Committed: Fri Sep 11 22:51:25 2015 -0400 ---------------------------------------------------------------------- .../internal/UpgradeResourceProvider.java | 6 +- .../server/orm/dao/HostRoleCommandDAO.java | 29 +++ .../orm/entities/HostRoleCommandEntity.java | 19 +- .../upgrades/AutoSkipFailedSummaryAction.java | 189 +++++++++++++++++++ .../ambari/server/state/UpgradeHelper.java | 16 +- .../ambari/server/state/stack/UpgradePack.java | 1 + .../state/stack/upgrade/ClusterGrouping.java | 35 ++-- .../state/stack/upgrade/ColocatedGrouping.java | 26 ++- .../server/state/stack/upgrade/Grouping.java | 33 ++-- .../state/stack/upgrade/ServerActionTask.java | 2 - .../stack/upgrade/ServiceCheckGrouping.java | 49 +++-- .../stack/upgrade/StageWrapperBuilder.java | 127 ++++++++++++- .../server/orm/dao/HostRoleCommandDAOTest.java | 155 +++++++++++++++ .../stack/upgrade/StageWrapperBuilderTest.java | 162 ++++++++++++++++ 14 files changed, 784 insertions(+), 65 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/c7a714cf/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java index b45f1ac..19a3397 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java @@ -35,7 +35,6 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -import com.google.gson.Gson; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.Role; import org.apache.ambari.server.RoleCommand; @@ -66,7 +65,6 @@ import org.apache.ambari.server.controller.spi.SystemException; import org.apache.ambari.server.controller.spi.UnsupportedPropertyException; import org.apache.ambari.server.controller.utilities.PredicateBuilder; import org.apache.ambari.server.controller.utilities.PropertyHelper; -import org.apache.ambari.server.orm.dao.HostDAO; import org.apache.ambari.server.orm.dao.HostRoleCommandDAO; import org.apache.ambari.server.orm.dao.HostRoleCommandStatusSummaryDTO; import org.apache.ambari.server.orm.dao.RepositoryVersionDAO; @@ -104,6 +102,7 @@ import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.gson.Gson; import com.google.inject.Inject; import com.google.inject.Provider; @@ -204,9 +203,6 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider @Inject private static HostRoleCommandDAO s_hostRoleCommandDAO = null; - @Inject - private static HostDAO s_hostDAO = null; - /** * Used to generated the correct tasks and stages during an upgrade. */ http://git-wip-us.apache.org/repos/asf/ambari/blob/c7a714cf/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostRoleCommandDAO.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostRoleCommandDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostRoleCommandDAO.java index 06799a0..70e2940 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostRoleCommandDAO.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostRoleCommandDAO.java @@ -313,6 +313,35 @@ public class HostRoleCommandDAO { } /** + * Finds all the {@link HostRoleCommandEntity}s for the given request that are + * between the specified stage IDs and have the specified status. + * + * @param requestId + * the request ID + * @param status + * the command status to query for (not {@code null}). + * @param minStageId + * the lowest stage ID to requests tasks for. + * @param maxStageId + * the highest stage ID to request tasks for. + * @return the tasks that satisfy the specified parameters. + */ + @RequiresSession + public List findByStatusBetweenStages(long requestId, + HostRoleStatus status, long minStageId, long maxStageId) { + + TypedQuery query = entityManagerProvider.get().createNamedQuery( + "HostRoleCommandEntity.findByStatusBetweenStages", HostRoleCommandEntity.class); + + query.setParameter("requestId", requestId); + query.setParameter("status", status); + query.setParameter("minStageId", minStageId); + query.setParameter("maxStageId", maxStageId); + + return daoUtils.selectList(query); + } + + /** * Gets requests that have tasks in any of the specified statuses. * * @param statuses http://git-wip-us.apache.org/repos/asf/ambari/blob/c7a714cf/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostRoleCommandEntity.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostRoleCommandEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostRoleCommandEntity.java index ae78890..e0662fb 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostRoleCommandEntity.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostRoleCommandEntity.java @@ -59,7 +59,8 @@ import org.apache.commons.lang.ArrayUtils; @NamedQuery(name = "HostRoleCommandEntity.findByCommandStatuses", query = "SELECT command FROM HostRoleCommandEntity command WHERE command.status IN :statuses ORDER BY command.requestId, command.stageId"), @NamedQuery(name = "HostRoleCommandEntity.findByHostId", query = "SELECT command FROM HostRoleCommandEntity command WHERE command.hostId=:hostId"), @NamedQuery(name = "HostRoleCommandEntity.findByHostRole", query = "SELECT command FROM HostRoleCommandEntity command WHERE command.hostEntity.hostName=:hostName AND command.requestId=:requestId AND command.stageId=:stageId AND command.role=:role ORDER BY command.taskId"), - @NamedQuery(name = "HostRoleCommandEntity.findByHostRoleNullHost", query = "SELECT command FROM HostRoleCommandEntity command WHERE command.hostEntity IS NULL AND command.requestId=:requestId AND command.stageId=:stageId AND command.role=:role") + @NamedQuery(name = "HostRoleCommandEntity.findByHostRoleNullHost", query = "SELECT command FROM HostRoleCommandEntity command WHERE command.hostEntity IS NULL AND command.requestId=:requestId AND command.stageId=:stageId AND command.role=:role"), + @NamedQuery(name = "HostRoleCommandEntity.findByStatusBetweenStages", query = "SELECT command FROM HostRoleCommandEntity command WHERE command.requestId = :requestId AND command.stageId >= :minStageId AND command.stageId <= :maxStageId AND command.status = :status") }) public class HostRoleCommandEntity { @@ -492,4 +493,20 @@ public class HostRoleCommandEntity { public void setTopologyLogicalTaskEntity(TopologyLogicalTaskEntity topologyLogicalTaskEntity) { this.topologyLogicalTaskEntity = topologyLogicalTaskEntity; } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + StringBuilder buffer = new StringBuilder("HostRoleCommandEntity{ "); + buffer.append("taskId").append(taskId); + buffer.append(", stageId=").append(stageId); + buffer.append(", requestId=").append(requestId); + buffer.append(", role=").append(role); + buffer.append(", roleCommand=").append(roleCommand); + buffer.append(", exitcode=").append(exitcode); + buffer.append("}"); + return buffer.toString(); + } } http://git-wip-us.apache.org/repos/asf/ambari/blob/c7a714cf/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/AutoSkipFailedSummaryAction.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/AutoSkipFailedSummaryAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/AutoSkipFailedSummaryAction.java new file mode 100644 index 0000000..9a84e38 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/AutoSkipFailedSummaryAction.java @@ -0,0 +1,189 @@ +/** + * 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.serveraction.upgrades; + +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentMap; + +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.actionmanager.HostRoleCommand; +import org.apache.ambari.server.actionmanager.HostRoleStatus; +import org.apache.ambari.server.actionmanager.ServiceComponentHostEventWrapper; +import org.apache.ambari.server.agent.CommandReport; +import org.apache.ambari.server.orm.dao.HostRoleCommandDAO; +import org.apache.ambari.server.orm.dao.UpgradeDAO; +import org.apache.ambari.server.orm.entities.HostRoleCommandEntity; +import org.apache.ambari.server.orm.entities.UpgradeGroupEntity; +import org.apache.ambari.server.orm.entities.UpgradeItemEntity; +import org.apache.ambari.server.serveraction.AbstractServerAction; +import org.apache.ambari.server.state.ServiceComponentHostEvent; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.inject.Inject; + +/** + * The {@link AutoSkipFailedSummaryAction} is used to check if any + * {@link HostRoleCommand}s were skipped automatically after they failed during + * an upgrade. This will be automatically marked as + * {@link HostRoleStatus#COMPLETED} if there are no skipped failures. Otherwise + * it will be placed into {@link HostRoleStatus#HOLDING}. + */ +public class AutoSkipFailedSummaryAction extends AbstractServerAction { + + /** + * Logger. + */ + private static final Logger LOG = LoggerFactory.getLogger(AutoSkipFailedSummaryAction.class); + + /** + * The standard output template message. + */ + private static final String FAILURE_STD_OUT_TEMPLATE = "There were {0} skipped failure(s) that must be addressed before you can proceed. Please resolve each failure before continuing with the upgrade."; + + /** + * ... + */ + private static final String MIDDLE_ELLIPSIZE_MARKER = "\n\u2026\n"; + + /** + * Used to lookup the {@link UpgradeGroupEntity}. + */ + @Inject + private UpgradeDAO m_upgradeDAO; + + /** + * Used to lookup the tasks that need to be checked for + * {@link HostRoleStatus#SKIPPED_FAILED}. + */ + @Inject + private HostRoleCommandDAO m_hostRoleCommandDAO; + + /** + * Used for writing structured out. + */ + @Inject + private Gson m_gson; + + /** + * A mapping of host -> Map for each failure. + */ + private Map> m_structuredFailures = new HashMap<>(); + + /** + * {@inheritDoc} + */ + @Override + public CommandReport execute(ConcurrentMap requestSharedDataContext) + throws AmbariException, InterruptedException { + + HostRoleCommand hostRoleCommand = getHostRoleCommand(); + long requestId = hostRoleCommand.getRequestId(); + long stageId = hostRoleCommand.getStageId(); + + // use the host role command to get to the parent upgrade group + UpgradeItemEntity upgradeItem = m_upgradeDAO.findUpgradeItemByRequestAndStage(requestId,stageId); + UpgradeGroupEntity upgradeGroup = upgradeItem.getGroupEntity(); + + // find all of the stages in this group + long upgradeGroupId = upgradeGroup.getId(); + UpgradeGroupEntity upgradeGroupEntity = m_upgradeDAO.findUpgradeGroup(upgradeGroupId); + List groupUpgradeItems = upgradeGroupEntity.getItems(); + TreeSet stageIds = new TreeSet<>(); + for (UpgradeItemEntity groupUpgradeItem : groupUpgradeItems) { + stageIds.add(groupUpgradeItem.getStageId()); + } + + // for every stage, find all tasks that have been SKIPPED_FAILED - we use a + // bit of trickery here since within any given request, the stage ID are + // always sequential. This allows us to make a simple query instead of some + // overly complex IN or NESTED SELECT query + long minStageId = stageIds.first(); + long maxStageId = stageIds.last(); + + List skippedTasks = m_hostRoleCommandDAO.findByStatusBetweenStages( + hostRoleCommand.getRequestId(), + HostRoleStatus.SKIPPED_FAILED, minStageId, maxStageId); + + if (skippedTasks.isEmpty()) { + return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", + "There were no skipped failures", null); + } + + StringBuilder buffer = new StringBuilder("The following steps failed and were automatically skipped:\n"); + + for (HostRoleCommandEntity skippedTask : skippedTasks) { + try{ + ServiceComponentHostEventWrapper eventWrapper = new ServiceComponentHostEventWrapper( + skippedTask.getEvent()); + + ServiceComponentHostEvent event = eventWrapper.getEvent(); + + String hostName = skippedTask.getHostName(); + if(null != hostName){ + Map failures = m_structuredFailures.get(hostName); + if( null == failures ){ + failures = new HashMap<>(); + m_structuredFailures.put(hostName, failures); + } + + failures.put("id", skippedTask.getTaskId()); + failures.put("exit_code", skippedTask.getExitcode()); + failures.put("output_log", skippedTask.getOutputLog()); + failures.put("error_log", skippedTask.getErrorLog()); + + String stdOut = StringUtils.abbreviateMiddle(new String(skippedTask.getStdOut()), + MIDDLE_ELLIPSIZE_MARKER, 1000); + + String stderr = StringUtils.abbreviateMiddle(new String(skippedTask.getStdError()), + MIDDLE_ELLIPSIZE_MARKER, 1000); + + failures.put("stdout", stdOut); + failures.put("stderr", stderr); + } + + buffer.append(event.getServiceComponentName()); + if (null != event.getHostName()) { + buffer.append(" on "); + buffer.append(event.getHostName()); + } + + buffer.append(": "); + buffer.append(skippedTask.getCommandDetail()); + buffer.append("\n"); + } catch (Exception exception) { + LOG.warn("Unable to extract failure information for {}", skippedTask); + buffer.append(": "); + buffer.append(skippedTask); + } + } + + String structuredOutput = m_gson.toJson(m_structuredFailures); + String standardOutput = MessageFormat.format(FAILURE_STD_OUT_TEMPLATE, skippedTasks.size()); + String standardError = buffer.toString(); + + return createCommandReport(0, HostRoleStatus.HOLDING, structuredOutput, standardOutput, + standardError); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/c7a714cf/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java index 5e63744..75c04da 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java @@ -439,9 +439,21 @@ public class UpgradeHelper { * List of stages for the group */ public List items = new ArrayList(); - } - + /** + * {@inheritDoc} + */ + @Override + public String toString() { + StringBuilder buffer = new StringBuilder("UpgradeGroupHolder{ "); + buffer.append("name").append(name); + buffer.append(", title=").append(title); + buffer.append(", allowRetry=").append(allowRetry); + buffer.append(", skippable=").append(skippable); + buffer.append("}"); + return buffer.toString(); + } + } /** * Gets a set of Stages resources to aggregate an UpgradeItem with Stage. http://git-wip-us.apache.org/repos/asf/ambari/blob/c7a714cf/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java index 9691292..3e9aa5f 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java @@ -94,6 +94,7 @@ public class UpgradePack { if (null == group.intendedDirection || direction == group.intendedDirection) { checked.add(group); } + } return checked; http://git-wip-us.apache.org/repos/asf/ambari/blob/c7a714cf/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java index cf58511..eff1b13 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java @@ -31,7 +31,6 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.XmlType; import org.apache.ambari.server.stack.HostsType; @@ -59,12 +58,9 @@ public class ClusterGrouping extends Grouping { @XmlElement(name="execute-stage") public List executionStages; - @XmlTransient - private ClusterBuilder m_builder = new ClusterBuilder(); - @Override public ClusterBuilder getBuilder() { - return m_builder; + return new ClusterBuilder(this); } @@ -100,24 +96,39 @@ public class ClusterGrouping extends Grouping { public class ClusterBuilder extends StageWrapperBuilder { + /** + * Constructor. + * + * @param grouping + * the upgrade/downgrade grouping (not {@code null}). + */ + private ClusterBuilder(Grouping grouping) { + super(grouping); + } + @Override public void add(UpgradeContext ctx, HostsType hostsType, String service, boolean clientOnly, ProcessingComponent pc) { // !!! no-op in this case } + /** + * {@inheritDoc} + */ @Override - public List build(UpgradeContext ctx) { + public List build(UpgradeContext upgradeContext, + List stageWrappers) { + if (null == executionStages) { - return Collections.emptyList(); + return stageWrappers; } - List results = new ArrayList(); + List results = new ArrayList(stageWrappers); if (executionStages != null) { for (ExecuteStage execution : executionStages) { - if (null != execution.intendedDirection && - execution.intendedDirection != ctx.getDirection()) { + if (null != execution.intendedDirection + && execution.intendedDirection != upgradeContext.getDirection()) { continue; } @@ -127,7 +138,7 @@ public class ClusterGrouping extends Grouping { switch (task.getType()) { case MANUAL: - wrapper = getManualStageWrapper(ctx, execution); + wrapper = getManualStageWrapper(upgradeContext, execution); break; case SERVER_ACTION: @@ -138,7 +149,7 @@ public class ClusterGrouping extends Grouping { break; case EXECUTE: - wrapper = getExecuteStageWrapper(ctx, execution); + wrapper = getExecuteStageWrapper(upgradeContext, execution); break; default: http://git-wip-us.apache.org/repos/asf/ambari/blob/c7a714cf/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ColocatedGrouping.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ColocatedGrouping.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ColocatedGrouping.java index a8e9c43..2aef43c 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ColocatedGrouping.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ColocatedGrouping.java @@ -54,9 +54,12 @@ public class ColocatedGrouping extends Grouping { public Batch batch; + /** + * {@inheritDoc} + */ @Override public StageWrapperBuilder getBuilder() { - return new MultiHomedBuilder(batch, performServiceCheck); + return new MultiHomedBuilder(this, batch, performServiceCheck); } private static class MultiHomedBuilder extends StageWrapperBuilder { @@ -69,7 +72,9 @@ public class ColocatedGrouping extends Grouping { private Map> finalBatches = new LinkedHashMap>(); - private MultiHomedBuilder(Batch batch, boolean serviceCheck) { + private MultiHomedBuilder(Grouping grouping, Batch batch, boolean serviceCheck) { + super(grouping); + m_batch = batch; m_serviceCheck = serviceCheck; } @@ -143,16 +148,20 @@ public class ColocatedGrouping extends Grouping { } + /** + * {@inheritDoc} + */ @Override - public List build(UpgradeContext ctx) { - List results = new ArrayList(); + public List build(UpgradeContext upgradeContext, + List stageWrappers) { + List results = new ArrayList(stageWrappers); if (LOG.isDebugEnabled()) { LOG.debug("RU initial: {}", initialBatch); LOG.debug("RU final: {}", finalBatches); } - List befores = fromProxies(ctx.getDirection(), initialBatch); + List befores = fromProxies(upgradeContext.getDirection(), initialBatch); results.addAll(befores); if (!befores.isEmpty()) { @@ -160,16 +169,17 @@ public class ColocatedGrouping extends Grouping { ManualTask task = new ManualTask(); task.summary = m_batch.summary; task.message = m_batch.message; - formatFirstBatch(ctx, task, befores); + formatFirstBatch(upgradeContext, task, befores); StageWrapper wrapper = new StageWrapper( StageWrapper.Type.SERVER_SIDE_ACTION, - "Validate Partial " + ctx.getDirection().getText(true), + "Validate Partial " + upgradeContext.getDirection().getText(true), new TaskWrapper(null, null, Collections.emptySet(), task)); + results.add(wrapper); } - results.addAll(fromProxies(ctx.getDirection(), finalBatches)); + results.addAll(fromProxies(upgradeContext.getDirection(), finalBatches)); return results; } http://git-wip-us.apache.org/repos/asf/ambari/blob/c7a714cf/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Grouping.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Grouping.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Grouping.java index a1e1fcd..cd27722 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Grouping.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Grouping.java @@ -65,7 +65,7 @@ public class Grouping { * Gets the default builder. */ public StageWrapperBuilder getBuilder() { - return new DefaultBuilder(performServiceCheck); + return new DefaultBuilder(this, performServiceCheck); } @@ -75,7 +75,8 @@ public class Grouping { private Set m_servicesToCheck = new HashSet(); private boolean m_serviceCheck = true; - private DefaultBuilder(boolean serviceCheck) { + private DefaultBuilder(Grouping grouping, boolean serviceCheck) { + super(grouping); m_serviceCheck = serviceCheck; } @@ -139,23 +140,32 @@ public class Grouping { } } + /** + * {@inheritDoc} + */ @Override - public List build(UpgradeContext ctx) { + public List build(UpgradeContext upgradeContext, + List stageWrappers) { + + // insert all pre-processed stage wrappers first + if (!stageWrappers.isEmpty()) { + m_stages.addAll(0, stageWrappers); + } List tasks = new ArrayList(); List displays = new ArrayList(); for (String service : m_servicesToCheck) { tasks.add(new TaskWrapper( service, "", Collections.emptySet(), new ServiceCheckTask())); - displays.add(ctx.getServiceDisplay(service)); + + displays.add(upgradeContext.getServiceDisplay(service)); } - if (ctx.getDirection().isUpgrade() && m_serviceCheck && m_servicesToCheck.size() > 0) { - StageWrapper wrapper = new StageWrapper( - StageWrapper.Type.SERVICE_CHECK, - "Service Check " + StringUtils.join(displays, ", "), - tasks.toArray(new TaskWrapper[0]) - ); + if (upgradeContext.getDirection().isUpgrade() && m_serviceCheck + && m_servicesToCheck.size() > 0) { + + StageWrapper wrapper = new StageWrapper(StageWrapper.Type.SERVICE_CHECK, + "Service Check " + StringUtils.join(displays, ", "), tasks.toArray(new TaskWrapper[0])); m_stages.add(wrapper); } @@ -168,8 +178,9 @@ public class Grouping { * Group all like-typed tasks together. When they change, create a new type. */ private static List buckets(List tasks) { - if (null == tasks || tasks.isEmpty()) + if (null == tasks || tasks.isEmpty()) { return Collections.emptyList(); + } List holders = new ArrayList(); http://git-wip-us.apache.org/repos/asf/ambari/blob/c7a714cf/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServerActionTask.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServerActionTask.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServerActionTask.java index 7a42c3b..74144b7 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServerActionTask.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServerActionTask.java @@ -17,8 +17,6 @@ */ package org.apache.ambari.server.state.stack.upgrade; -import org.apache.ambari.server.serveraction.upgrades.FinalizeUpgradeAction; - import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; http://git-wip-us.apache.org/repos/asf/ambari/blob/c7a714cf/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServiceCheckGrouping.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServiceCheckGrouping.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServiceCheckGrouping.java index fdf89cc..6061895 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServiceCheckGrouping.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServiceCheckGrouping.java @@ -58,11 +58,12 @@ public class ServiceCheckGrouping extends Grouping { @XmlElement(name="service") private Set excludeServices = new HashSet(); - private ServiceCheckBuilder m_builder = new ServiceCheckBuilder(); - + /** + * {@inheritDoc} + */ @Override public ServiceCheckBuilder getBuilder() { - return m_builder; + return new ServiceCheckBuilder(this); } /** @@ -76,23 +77,40 @@ public class ServiceCheckGrouping extends Grouping { * Used to build stages for service check groupings. */ public class ServiceCheckBuilder extends StageWrapperBuilder { + private Cluster m_cluster; private AmbariMetaInfo m_metaInfo; + /** + * Constructor. + * + * @param grouping + * the upgrade/downgrade grouping (not {@code null}). + */ + protected ServiceCheckBuilder(Grouping grouping) { + super(grouping); + } + /** + * {@inheritDoc} + */ @Override public void add(UpgradeContext ctx, HostsType hostsType, String service, boolean clientOnly, ProcessingComponent pc) { // !!! nothing to do here } + /** + * {@inheritDoc} + */ @Override - public List build(UpgradeContext ctx) { - m_cluster = ctx.getCluster(); - m_metaInfo = ctx.getAmbariMetaInfo(); + public List build(UpgradeContext upgradeContext, + List stageWrappers) { + m_cluster = upgradeContext.getCluster(); + m_metaInfo = upgradeContext.getAmbariMetaInfo(); - List result = new ArrayList(); - if (ctx.getDirection().isDowngrade()) { + List result = new ArrayList(stageWrappers); + if (upgradeContext.getDirection().isDowngrade()) { return result; } @@ -101,28 +119,29 @@ public class ServiceCheckGrouping extends Grouping { Set clusterServices = new LinkedHashSet(serviceMap.keySet()); // create stages for the priorities - for (String service : ServiceCheckGrouping.this.priorityServices) { - if (checkServiceValidity(ctx, service, serviceMap)) { + for (String service : priorityServices) { + if (checkServiceValidity(upgradeContext, service, serviceMap)) { StageWrapper wrapper = new StageWrapper( StageWrapper.Type.SERVICE_CHECK, - "Service Check " + ctx.getServiceDisplay(service), + "Service Check " + upgradeContext.getServiceDisplay(service), new TaskWrapper(service, "", Collections.emptySet(), new ServiceCheckTask())); - result.add(wrapper); + result.add(wrapper); clusterServices.remove(service); } } // create stages for everything else, as long it is valid for (String service : clusterServices) { - if (ServiceCheckGrouping.this.excludeServices.contains(service)) { + if (excludeServices.contains(service)) { continue; } - if (checkServiceValidity(ctx, service, serviceMap)) { + + if (checkServiceValidity(upgradeContext, service, serviceMap)) { StageWrapper wrapper = new StageWrapper( StageWrapper.Type.SERVICE_CHECK, - "Service Check " + ctx.getServiceDisplay(service), + "Service Check " + upgradeContext.getServiceDisplay(service), new TaskWrapper(service, "", Collections.emptySet(), new ServiceCheckTask())); result.add(wrapper); http://git-wip-us.apache.org/repos/asf/ambari/blob/c7a714cf/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StageWrapperBuilder.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StageWrapperBuilder.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StageWrapperBuilder.java index f7b37ab..57cd41f 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StageWrapperBuilder.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StageWrapperBuilder.java @@ -17,9 +17,12 @@ */ package org.apache.ambari.server.state.stack.upgrade; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Set; +import org.apache.ambari.server.serveraction.upgrades.AutoSkipFailedSummaryAction; import org.apache.ambari.server.stack.HostsType; import org.apache.ambari.server.state.UpgradeContext; import org.apache.ambari.server.state.stack.UpgradePack.ProcessingComponent; @@ -30,23 +33,129 @@ import org.apache.ambari.server.state.stack.UpgradePack.ProcessingComponent; public abstract class StageWrapperBuilder { /** + * The message for the task which checks for skipped failures. + */ + private static final String AUTO_SKIPPED_TASK_SUMMARY = "Pauses the upgrade if there were failed steps that were automatically skipped."; + + /** + * The upgrade/downgrade grouping that the builder is for. + */ + protected final Grouping m_grouping; + + /** + * Constructor. + * + * @param grouping + * the upgrade/downgrade grouping (not {@code null}). + */ + protected StageWrapperBuilder(Grouping grouping) { + m_grouping = grouping; + } + + /** * Adds a processing component that will be built into stage wrappers. * - * @param ctx the upgrade context - * @param hostsType the hosts, along with their type - * @param service the service name - * @param clientOnly whether the service is client only, no service checks - * @param pc the ProcessingComponent derived from the upgrade pack + * @param upgradeContext + * the upgrade context + * @param hostsType + * the hosts, along with their type + * @param service + * the service name + * @param clientOnly + * whether the service is client only, no service checks + * @param pc + * the ProcessingComponent derived from the upgrade pack */ - public abstract void add(UpgradeContext ctx, HostsType hostsType, String service, + public abstract void add(UpgradeContext upgradeContext, HostsType hostsType, String service, boolean clientOnly, ProcessingComponent pc); /** - * Builds the stage wrappers. - * @param ctx the upgrade context + * Builds the stage wrappers, including any pre- and post-procesing that needs + * to be performed. + * + * @param upgradeContext + * the upgrade context (not {@code null}). * @return a list of stages, never {@code null} */ - public abstract List build(UpgradeContext ctx); + public final List build(UpgradeContext upgradeContext) { + List stageWrappers = beforeBuild(upgradeContext); + stageWrappers = build(upgradeContext, stageWrappers); + stageWrappers = afterBuild(upgradeContext, stageWrappers); + return stageWrappers; + } + + /** + * Performs any pre-processing that needs to be performed on the list of stage + * wrappers. + * + * @param upgradeContext + * the upgrade context (not {@code null}). + * @return the initial list of stage wrappers, or an empty list (never + * {@code null}). + */ + protected List beforeBuild(UpgradeContext upgradeContext) { + List stageWrappers = new ArrayList<>(100); + return stageWrappers; + } + + /** + * Builds the stage wrappers. + * + * @param upgradeContext + * the upgrade context (not {@code null}). + * @param stageWrappers + * the list of stage wrappers created by + * {@link #beforeBuild(UpgradeContext)}. + * @return the stage wrapper list, (never {@code null}) + */ + public abstract List build(UpgradeContext upgradeContext, + List stageWrappers); + + /** + * Performs any post-processing that needs to be performed on the list of + * stage wrappers. + * + * @param upgradeContext + * the upgrade context (not {@code null}). + * @param stageWrappers + * the list of stage wrappers created by + * {@link #build(UpgradeContext, List)}. + * @return the post-processed list of stage wrappers (never {@code null}) + */ + protected List afterBuild(UpgradeContext upgradeContext, + List stageWrappers) { + + if (stageWrappers.isEmpty()) { + return stageWrappers; + } + + // we only want to insert the auto skip summary if the group is skippable + // and the upgrade context says to auto skip failures + final boolean autoSkipFailures; + if (m_grouping instanceof ServiceCheckGrouping) { + autoSkipFailures = upgradeContext.isServiceCheckFailureAutoSkipped(); + } else { + autoSkipFailures = upgradeContext.isComponentFailureAutoSkipped(); + } + + if (m_grouping.skippable && autoSkipFailures) { + ServerActionTask skippedFailedCheck = new ServerActionTask(); + skippedFailedCheck.implClass = AutoSkipFailedSummaryAction.class.getName(); + skippedFailedCheck.summary = AUTO_SKIPPED_TASK_SUMMARY; + + TaskWrapper skippedFailedTaskWrapper = new TaskWrapper(null, null, + Collections. emptySet(), skippedFailedCheck); + + StageWrapper skippedFailedStageWrapper = new StageWrapper( + StageWrapper.Type.SERVER_SIDE_ACTION, "Verifying Skipped Failures", + skippedFailedTaskWrapper); + + stageWrappers.add(skippedFailedStageWrapper); + } + + return stageWrappers; + } + /** * Consistently formats a string. http://git-wip-us.apache.org/repos/asf/ambari/blob/c7a714cf/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/HostRoleCommandDAOTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/HostRoleCommandDAOTest.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/HostRoleCommandDAOTest.java new file mode 100644 index 0000000..1fded28 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/HostRoleCommandDAOTest.java @@ -0,0 +1,155 @@ +/** + * 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.orm.dao; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.Role; +import org.apache.ambari.server.RoleCommand; +import org.apache.ambari.server.actionmanager.HostRoleStatus; +import org.apache.ambari.server.api.services.AmbariMetaInfo; +import org.apache.ambari.server.orm.GuiceJpaInitializer; +import org.apache.ambari.server.orm.InMemoryDefaultTestModule; +import org.apache.ambari.server.orm.OrmTestHelper; +import org.apache.ambari.server.orm.entities.ClusterEntity; +import org.apache.ambari.server.orm.entities.HostEntity; +import org.apache.ambari.server.orm.entities.HostRoleCommandEntity; +import org.apache.ambari.server.orm.entities.RequestEntity; +import org.apache.ambari.server.orm.entities.StageEntity; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.persist.PersistService; + +import junit.framework.Assert; + +/** + * Tests {@link HostRoleCommandDAO}. + */ +public class HostRoleCommandDAOTest { + + private Injector m_injector; + private ClusterDAO m_clusterDAO; + private StageDAO m_stageDAO; + private HostRoleCommandDAO m_hostRoleCommandDAO; + private HostDAO m_hostDAO; + private RequestDAO m_requestDAO; + + @Before + public void setup() throws Exception { + m_injector = Guice.createInjector(new InMemoryDefaultTestModule()); + m_injector.getInstance(GuiceJpaInitializer.class); + m_injector.getInstance(AmbariMetaInfo.class); + + m_clusterDAO = m_injector.getInstance(ClusterDAO.class); + m_stageDAO = m_injector.getInstance(StageDAO.class); + m_hostRoleCommandDAO = m_injector.getInstance(HostRoleCommandDAO.class); + m_hostDAO = m_injector.getInstance(HostDAO.class); + m_requestDAO = m_injector.getInstance(RequestDAO.class); + } + + @After + public void teardown() throws AmbariException { + m_injector.getInstance(PersistService.class).stop(); + } + + /** + * Tests finding all tasks between a range of stages. + */ + @Test + public void testFindTasksBetweenStages() { + OrmTestHelper helper = m_injector.getInstance(OrmTestHelper.class); + helper.createDefaultData(); + + Long requestId = Long.valueOf(100L); + ClusterEntity clusterEntity = m_clusterDAO.findByName("test_cluster1"); + + RequestEntity requestEntity = new RequestEntity(); + requestEntity.setRequestId(requestId); + requestEntity.setClusterId(clusterEntity.getClusterId()); + requestEntity.setStages(new ArrayList()); + m_requestDAO.create(requestEntity); + + AtomicLong stageId = new AtomicLong(1); + HostEntity host = m_hostDAO.findByName("test_host1"); + host.setHostRoleCommandEntities(new ArrayList()); + + createStage(stageId.getAndIncrement(), 3, host, requestEntity, HostRoleStatus.COMPLETED, false); + createStage(stageId.getAndIncrement(), 2, host, requestEntity, HostRoleStatus.SKIPPED_FAILED, false); + createStage(stageId.getAndIncrement(), 1, host, requestEntity, HostRoleStatus.ABORTED, false); + + List tasks = m_hostRoleCommandDAO.findByStatusBetweenStages(requestId, + HostRoleStatus.SKIPPED_FAILED, 1, 3); + + Assert.assertEquals(2, tasks.size()); + + tasks = m_hostRoleCommandDAO.findByStatusBetweenStages(requestId, HostRoleStatus.SKIPPED_FAILED, 1, 1); + Assert.assertEquals(0, tasks.size()); + } + + /** + * Creates a single stage with the specified number of commands. + * + * @param startStageId + * @param count + * @param hostEntity + * @param requestEntity + * @param status + * @param skipStage + * @return + */ + private void createStage(long startStageId, int count, HostEntity hostEntity, + RequestEntity requestEntity, HostRoleStatus status, boolean skipStage) { + long stageId = startStageId; + + ClusterEntity clusterEntity = m_clusterDAO.findByName("test_cluster1"); + + StageEntity stageEntity = new StageEntity(); + stageEntity.setClusterId(clusterEntity.getClusterId()); + stageEntity.setRequest(requestEntity); + stageEntity.setStageId(stageId); + stageEntity.setHostRoleCommands(new ArrayList()); + stageEntity.setSkippable(skipStage); + m_stageDAO.create(stageEntity); + requestEntity.getStages().add(stageEntity); + + for (int i = 0; i < count; i++) { + HostRoleCommandEntity commandEntity = new HostRoleCommandEntity(); + commandEntity.setRequestId(requestEntity.getRequestId()); + commandEntity.setStageId(stageId); + commandEntity.setRoleCommand(RoleCommand.INSTALL); + commandEntity.setStatus(status); + commandEntity.setRole(Role.DATANODE); + commandEntity.setHostEntity(hostEntity); + commandEntity.setStage(stageEntity); + m_hostRoleCommandDAO.create(commandEntity); + + hostEntity.getHostRoleCommandEntities().add(commandEntity); + hostEntity = m_hostDAO.merge(hostEntity); + + stageEntity.getHostRoleCommands().add(commandEntity); + m_stageDAO.merge(stageEntity); + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/c7a714cf/ambari-server/src/test/java/org/apache/ambari/server/state/stack/upgrade/StageWrapperBuilderTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/stack/upgrade/StageWrapperBuilderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/stack/upgrade/StageWrapperBuilderTest.java new file mode 100644 index 0000000..e2a3995 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/state/stack/upgrade/StageWrapperBuilderTest.java @@ -0,0 +1,162 @@ +/** + * 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.state.stack.upgrade; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.ambari.server.serveraction.upgrades.AutoSkipFailedSummaryAction; +import org.apache.ambari.server.stack.HostsType; +import org.apache.ambari.server.state.UpgradeContext; +import org.apache.ambari.server.state.stack.UpgradePack.ProcessingComponent; +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Test; + +/** + * Tests the {@link StageWrapperBuilder}. + */ +public class StageWrapperBuilderTest { + + + /** + * Tests that the various build methods of a builder are invoked in the + * correct order. + * + * @throws Exception + */ + @Test + public void testBuildOrder() throws Exception { + UpgradeContext upgradeContext = new UpgradeContext(null, null, null, null, Direction.UPGRADE); + MockStageWrapperBuilder builder = new MockStageWrapperBuilder(null); + List stageWrappers = builder.build(upgradeContext); + List invocationOrder = builder.getInvocationOrder(); + + Assert.assertEquals(Integer.valueOf(0), invocationOrder.get(0)); + Assert.assertEquals(Integer.valueOf(1), invocationOrder.get(1)); + Assert.assertEquals(Integer.valueOf(2), invocationOrder.get(2)); + + // nothing happened, so this should be empty + Assert.assertTrue(stageWrappers.isEmpty()); + } + + /** + * Tests that a new task was inserted into the upgrade which will check for + * skipped failures and display a summary. + * + * @throws Exception + */ + @Test + public void testAutoSkipCheckInserted() throws Exception { + UpgradeContext upgradeContext = new UpgradeContext(null, null, null, null, Direction.UPGRADE); + upgradeContext.setAutoSkipComponentFailures(true); + upgradeContext.setAutoSkipServiceCheckFailures(true); + + Grouping grouping = new Grouping(); + grouping.skippable = true; + + MockStageWrapperBuilder builder = new MockStageWrapperBuilder(grouping); + + List mockStageWrappers = new ArrayList<>(); + StageWrapper mockStageWrapper = EasyMock.createNiceMock(StageWrapper.class); + mockStageWrappers.add(mockStageWrapper); + + builder.setMockStageWrappers(mockStageWrappers); + + List stageWrappers = builder.build(upgradeContext); + Assert.assertEquals(2, stageWrappers.size()); + + StageWrapper skipSummaryWrapper = stageWrappers.get(1); + Assert.assertEquals(StageWrapper.Type.SERVER_SIDE_ACTION, skipSummaryWrapper.getType()); + + ServerActionTask task = (ServerActionTask)(skipSummaryWrapper.getTasks().get(0).getTasks().get(0)); + Assert.assertEquals(AutoSkipFailedSummaryAction.class.getName(), task.implClass); + + } + + /** + * A mock {@link StageWrapperBuilder}. + */ + private final class MockStageWrapperBuilder extends StageWrapperBuilder { + + private List m_invocationOrder = new ArrayList<>(); + private List m_stageWrappers = Collections.emptyList(); + + /** + * Constructor. + * + * @param grouping + */ + protected MockStageWrapperBuilder(Grouping grouping) { + super(grouping); + } + + private void setMockStageWrappers(List stageWrappers) { + m_stageWrappers = stageWrappers; + } + + /** + * Gets the invocation order. + * + * @return + */ + private List getInvocationOrder() { + return m_invocationOrder; + } + + /** + * {@inheritDoc} + */ + @Override + public void add(UpgradeContext upgradeContext, HostsType hostsType, String service, + boolean clientOnly, ProcessingComponent pc) { + } + + /** + * {@inheritDoc} + */ + @Override + public List build(UpgradeContext upgradeContext, + List stageWrappers) { + m_invocationOrder.add(1); + return m_stageWrappers; + } + + /** + * {@inheritDoc} + */ + @Override + protected List beforeBuild(UpgradeContext upgradeContext) { + List stageWrappers = super.beforeBuild(upgradeContext); + m_invocationOrder.add(0); + return stageWrappers; + } + + /** + * {@inheritDoc} + */ + @Override + protected List afterBuild(UpgradeContext upgradeContext, + List stageWrappers) { + stageWrappers = super.afterBuild(upgradeContext, stageWrappers); + m_invocationOrder.add(2); + return stageWrappers; + } + } +}