Repository: ambari Updated Branches: refs/heads/trunk a4e72f648 -> 95fb3a16c
AMBARI-16439 - Restarting Upgraded Component During a Paused Upgrade Downgrades Component To Old Version (jonathanhurley) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/95fb3a16 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/95fb3a16 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/95fb3a16 Branch: refs/heads/trunk Commit: 95fb3a16cfaf542b5fe221cecaa3cbeb68528852 Parents: a4e72f6 Author: Jonathan Hurley <jhur...@hortonworks.com> Authored: Tue May 10 19:34:32 2016 -0400 Committer: Jonathan Hurley <jhur...@hortonworks.com> Committed: Wed May 11 13:42:38 2016 -0400 ---------------------------------------------------------------------- .../ambari/server/agent/ExecutionCommand.java | 11 +- .../ambari/server/agent/HeartbeatProcessor.java | 5 +- .../AmbariManagementControllerImpl.java | 9 +- .../listeners/upgrade/StackVersionListener.java | 13 +- .../org/apache/ambari/server/state/Cluster.java | 45 ++- .../server/state/cluster/ClusterImpl.java | 73 ++--- .../upgrade/StackVersionListenerTest.java | 37 +++ .../cluster/ClusterEffectiveVersionTest.java | 283 +++++++++++++++++++ 8 files changed, 412 insertions(+), 64 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/95fb3a16/ambari-server/src/main/java/org/apache/ambari/server/agent/ExecutionCommand.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/ExecutionCommand.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/ExecutionCommand.java index 34a0918..bdb5fb1 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/agent/ExecutionCommand.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/ExecutionCommand.java @@ -374,6 +374,7 @@ public class ExecutionCommand extends AgentCommand { String COMMAND_RETRY_ENABLED = "command_retry_enabled"; String AGENT_STACK_RETRY_ON_UNAVAILABILITY = "agent_stack_retry_on_unavailability"; String AGENT_STACK_RETRY_COUNT = "agent_stack_retry_count"; + /** * Comma separated list of config-types whose tags have be refreshed * at runtime before being executed. If all config-type tags have to be @@ -383,6 +384,7 @@ public class ExecutionCommand extends AgentCommand { String SERVICE_CHECK = "SERVICE_CHECK"; // TODO: is it standard command? maybe add it to RoleCommand enum? String CUSTOM_COMMAND = "custom_command"; + /** * The key indicating that the package_version string is available */ @@ -392,11 +394,14 @@ public class ExecutionCommand extends AgentCommand { * The key indicating that there is an un-finalized upgrade which is suspended. */ String UPGRADE_SUSPENDED = "upgrade_suspended"; + /** - * When installing packages, optionally provide the row id the version is for in - * order to precisely match response data. + * When installing packages, optionally provide the row id the version is + * for in order to precisely match response data. + * <p/> + * The agent will return this value back in its response so the repository + * can be looked up and possibly have its version updated. */ String REPO_VERSION_ID = "repository_version_id"; } - } http://git-wip-us.apache.org/repos/asf/ambari/blob/95fb3a16/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartbeatProcessor.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartbeatProcessor.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartbeatProcessor.java index c587e9f..c6036c2 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartbeatProcessor.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartbeatProcessor.java @@ -39,6 +39,7 @@ import org.apache.ambari.server.ServiceNotFoundException; import org.apache.ambari.server.actionmanager.ActionManager; import org.apache.ambari.server.actionmanager.HostRoleCommand; import org.apache.ambari.server.actionmanager.HostRoleStatus; +import org.apache.ambari.server.agent.ExecutionCommand.KeyNames; import org.apache.ambari.server.api.services.AmbariMetaInfo; import org.apache.ambari.server.controller.MaintenanceStateHelper; import org.apache.ambari.server.events.ActionFinalReportReceivedEvent; @@ -664,7 +665,7 @@ public class HeartbeatProcessor extends AbstractService{ } } - this.heartbeatMonitor.getAgentRequests() + heartbeatMonitor.getAgentRequests() .setExecutionDetailsRequest(hostname, componentName, status.getSendExecCmdDet()); } else { // TODO: What should be done otherwise? @@ -738,7 +739,7 @@ public class HeartbeatProcessor extends AbstractService{ @SerializedName("direction") private Direction upgradeDirection = null; - @SerializedName("repository_version_id") + @SerializedName(KeyNames.REPO_VERSION_ID) private Long repositoryVersionId; } http://git-wip-us.apache.org/repos/asf/ambari/blob/95fb3a16/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java index 6dbceba..7884d02 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java @@ -2207,16 +2207,19 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle hostParams.put(REPO_INFO, repoInfo); hostParams.putAll(getRcaParameters()); + // use the effective cluster version here since this command might happen + // in the context of an upgrade and we should send the repo ID which matches + // the version being send down RepositoryVersionEntity repoVersion = null; - if (null != cluster.getCurrentClusterVersion()) { - repoVersion = cluster.getCurrentClusterVersion().getRepositoryVersion(); + ClusterVersionEntity effectiveClusterVersion = cluster.getEffectiveClusterVersion(); + if (null != effectiveClusterVersion) { + repoVersion = effectiveClusterVersion.getRepositoryVersion(); } else { List<ClusterVersionEntity> list = clusterVersionDAO.findByClusterAndState(cluster.getClusterName(), RepositoryVersionState.INIT); if (1 == list.size()) { repoVersion = list.get(0).getRepositoryVersion(); } - } if (null != repoVersion) { http://git-wip-us.apache.org/repos/asf/ambari/blob/95fb3a16/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListener.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListener.java b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListener.java index b14e9e5..d9599cc 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListener.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListener.java @@ -92,14 +92,19 @@ public class StackVersionListener { m_stackVersionLock.lock(); - if (null != event.getRepositoryVersionId()) { + // if the cluster is upgrading, there's no need to update the repo version - + // it better be right + if (null != event.getRepositoryVersionId() && null == cluster.getUpgradeInProgress()) { // !!! make sure the repo_version record actually has the same version. // This is NOT true when installing a cluster using a public repo where the // exact version is not known in advance. RepositoryVersionEntity rve = repositoryVersionDAO.findByPK(event.getRepositoryVersionId()); - if (null != rve && !rve.getVersion().equals(newVersion)) { - rve.setVersion(newVersion); - repositoryVersionDAO.merge(rve); + if (null != rve) { + String currentRepoVersion = rve.getVersion(); + if (!StringUtils.equals(currentRepoVersion, newVersion)) { + rve.setVersion(newVersion); + repositoryVersionDAO.merge(rve); + } } } http://git-wip-us.apache.org/repos/asf/ambari/blob/95fb3a16/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java index cf2c9aa..303b043 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java @@ -138,9 +138,28 @@ public interface Cluster { ClusterVersionEntity getCurrentClusterVersion(); /** - * If no RU/EU is in progress, get the ClusterVersionEntity object whose state is CURRENT. - * If RU/EU is in progress, based on the direction and desired stack, determine which version to use. - * @return Cluster Version entity to use. + * Gets the current stack version associated with the cluster. + * <ul> + * <li>if there is no upgrade in progress then get the + * {@link ClusterVersionEntity} object whose state is + * {@link RepositoryVersionState#CURRENT}. + * <li>If an upgrade is in progress then based on the direction and the + * desired stack determine which version to use. Assuming upgrading from HDP + * 2.2.0.0-1 to 2.3.0.0-2: + * <ul> + * <li>RU Upgrade: 2.3.0.0-2 (desired stack id) + * <li>RU Downgrade: 2.2.0.0-1 (desired stack id) + * <li>EU Upgrade: while stopping services and before changing desired stack, + * use 2.2.0.0-1, after, use 2.3.0.0-2 + * <li>EU Downgrade: while stopping services and before changing desired + * stack, use 2.3.0.0-2, after, use 2.2.0.0-1 + * </ul> + * </ul> + * + * This method must take into account both a running and a suspended upgrade. + * + * @return the effective cluster stack version given the current upgrading + * conditions of the cluster. */ ClusterVersionEntity getEffectiveClusterVersion() throws AmbariException; @@ -672,10 +691,24 @@ public interface Cluster { boolean isUpgradeSuspended(); /** + * Gets an {@link UpgradeEntity} if there is an upgrade in progress or an + * upgrade that has been suspended. This will first check + * {@link #getUpgradeEntity()} and return that if it is not {@code null}. + * Otherwise, this will perform a search for the most recent upgrade/downgrade + * which has not been completed. + * + * @return an upgrade which will either be in progress or suspended, or + * {@code null} if none. + */ + UpgradeEntity getUpgradeInProgress(); + + /** * Returns the name of the service that the passed config type belongs to. - * @param configType the config type to look up the service by - * @return returns the name of the service that the config type belongs to if there is any - * otherwise returns null. + * + * @param configType + * the config type to look up the service by + * @return returns the name of the service that the config type belongs to if + * there is any otherwise returns null. */ String getServiceByConfigType(String configType); http://git-wip-us.apache.org/repos/asf/ambari/blob/95fb3a16/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java index cd0812b..85ca2c7 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java @@ -322,9 +322,6 @@ public class ClusterImpl implements Cluster { loadServiceConfigTypes(); } - // Load any active stack upgrades. - loadStackUpgrade(); - loadServices(); // register to receive stuff @@ -349,24 +346,6 @@ public class ClusterImpl implements Cluster { } /** - * When a cluster is first loaded, determine if it has a stack upgrade in progress. - */ - private void loadStackUpgrade() { - clusterGlobalLock.writeLock().lock(); - - try { - UpgradeEntity activeUpgrade = getUpgradeInProgress(); - if (activeUpgrade != null) { - setUpgradeEntity(activeUpgrade); - } - } catch (AmbariException e) { - LOG.error("Unable to load active stack upgrade. Error: " + e.getMessage()); - } finally { - clusterGlobalLock.writeLock().unlock(); - } - } - - /** * Construct config type to service name mapping * @throws AmbariException when stack or its part not found */ @@ -1189,17 +1168,27 @@ public class ClusterImpl implements Cluster { } /** - * Get any stack upgrade currently in progress. - * @return + * {@inheritDoc} */ - private UpgradeEntity getUpgradeInProgress() { + @Override + public UpgradeEntity getUpgradeInProgress() { + // first check for an upgrade that's actively running + UpgradeEntity upgradeInProgress = getUpgradeEntity(); + if (null != upgradeInProgress) { + return upgradeInProgress; + } + + // perform a search for any upgrade which shoudl also return upgrades which + // are suspended UpgradeEntity mostRecentUpgrade = upgradeDAO.findLastUpgradeOrDowngradeForCluster(getClusterId()); if (mostRecentUpgrade != null) { - List<HostRoleStatus> UNFINISHED_STATUSES = new ArrayList(); + List<HostRoleStatus> UNFINISHED_STATUSES = new ArrayList<>(); UNFINISHED_STATUSES.add(HostRoleStatus.PENDING); UNFINISHED_STATUSES.add(HostRoleStatus.ABORTED); - List<HostRoleCommandEntity> commands = hostRoleCommandDAO.findByRequestIdAndStatuses(mostRecentUpgrade.getRequestId(), UNFINISHED_STATUSES); + List<HostRoleCommandEntity> commands = hostRoleCommandDAO.findByRequestIdAndStatuses( + mostRecentUpgrade.getRequestId(), UNFINISHED_STATUSES); + if (!commands.isEmpty()) { return mostRecentUpgrade; } @@ -1208,39 +1197,32 @@ public class ClusterImpl implements Cluster { return null; } + /** - * If no RU/EU is in progress, get the ClusterVersionEntity object whose state is CURRENT. - * If RU/EU is in progress, based on the direction and desired stack, determine which version to use. - * Assuming upgrading from HDP 2.2.0.0-1 to 2.3.0.0-2, then - * RU Upgrade: 2.3.0.0-2 (desired stack id) - * RU Downgrade: 2.2.0.0-1 (desired stack id) - * EU Upgrade: while stopping services and before changing desired stack, use 2.2.0.0-1, after, use 2.3.0.0-2 - * EU Downgrade: while stopping services and before changing desired stack, use 2.3.0.0-2, after, use 2.2.0.0-1 - * @return + * {@inheritDoc} */ @Override public ClusterVersionEntity getEffectiveClusterVersion() throws AmbariException { - // This is not reliable. Need to find the last upgrade request. - UpgradeEntity upgradeInProgress = getUpgradeEntity(); - if (upgradeInProgress == null) { + UpgradeEntity upgradeEntity = getUpgradeInProgress(); + if (upgradeEntity == null) { return getCurrentClusterVersion(); } String effectiveVersion = null; - switch (upgradeInProgress.getUpgradeType()) { + switch (upgradeEntity.getUpgradeType()) { case NON_ROLLING: - if (upgradeInProgress.getDirection() == Direction.UPGRADE) { - boolean pastChangingStack = isNonRollingUpgradePastUpgradingStack(upgradeInProgress); - effectiveVersion = pastChangingStack ? upgradeInProgress.getToVersion() : upgradeInProgress.getFromVersion(); + if (upgradeEntity.getDirection() == Direction.UPGRADE) { + boolean pastChangingStack = isNonRollingUpgradePastUpgradingStack(upgradeEntity); + effectiveVersion = pastChangingStack ? upgradeEntity.getToVersion() : upgradeEntity.getFromVersion(); } else { // Should be the lower value during a Downgrade. - effectiveVersion = upgradeInProgress.getToVersion(); + effectiveVersion = upgradeEntity.getToVersion(); } break; case ROLLING: default: // Version will be higher on upgrade and lower on downgrade directions. - effectiveVersion = upgradeInProgress.getToVersion(); + effectiveVersion = upgradeEntity.getToVersion(); break; } @@ -1255,6 +1237,7 @@ public class ClusterImpl implements Cluster { return clusterVersionEntity; } } + return null; } @@ -1305,9 +1288,7 @@ public class ClusterImpl implements Cluster { throw new AmbariException("Could not find current stack version of cluster " + getClusterName()); } - final Set<RepositoryVersionState> validStates = new HashSet<RepositoryVersionState>(){{ - add(RepositoryVersionState.CURRENT); - }}; + final Set<RepositoryVersionState> validStates = Sets.newHashSet(RepositoryVersionState.CURRENT); if (!validStates.contains(desiredState)) { throw new AmbariException("The state must be one of [" + StringUtils.join(validStates, ", ") + "]"); http://git-wip-us.apache.org/repos/asf/ambari/blob/95fb3a16/ambari-server/src/test/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListenerTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListenerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListenerTest.java index 3a8b396..6177e6b 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListenerTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListenerTest.java @@ -37,6 +37,8 @@ import org.easymock.EasyMockSupport; import org.junit.Before; import org.junit.Test; +import junit.framework.Assert; + /** * StackVersionListener tests. */ @@ -258,6 +260,41 @@ public class StackVersionListenerTest extends EasyMockSupport { verifyAll(); } + /** + * Tests that the {@link RepositoryVersionEntity} is not updated if there is + * an upgrade, even if the repo ID is passed back and the versions don't + * match. + * + * @throws Exception + */ + @Test + public void testRepositoryVersionNotSetDuringUpgrade() throws Exception { + // this call will make it seem like there is an upgrade in progress + expect(cluster.getUpgradeInProgress()).andReturn(createNiceMock(UpgradeEntity.class)); + + // create the DAO - nothing will be called on it, so make it strict + RepositoryVersionDAO dao = createStrictMock(RepositoryVersionDAO.class); + + replayAll(); + + // !!! avoid injector for test class + StackVersionListener listener = new StackVersionListener(publisher); + + Field field = StackVersionListener.class.getDeclaredField("repositoryVersionDAO"); + field.setAccessible(true); + field.set(listener, dao); + + HostComponentVersionAdvertisedEvent event = new HostComponentVersionAdvertisedEvent(cluster, + sch, VALID_NEW_VERSION, 1L); + + // make sure that a repo ID will come back + Assert.assertNotNull(event.getRepositoryVersionId()); + + listener.onAmbariEvent(event); + + verifyAll(); + } + private void sendEventAndVerify(String newVersion) { HostComponentVersionAdvertisedEvent event = new HostComponentVersionAdvertisedEvent(cluster, sch, newVersion); StackVersionListener listener = new StackVersionListener(publisher); http://git-wip-us.apache.org/repos/asf/ambari/blob/95fb3a16/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterEffectiveVersionTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterEffectiveVersionTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterEffectiveVersionTest.java new file mode 100644 index 0000000..2aaa2cf --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterEffectiveVersionTest.java @@ -0,0 +1,283 @@ +/** + * 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.cluster; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import javax.persistence.EntityManager; + +import org.apache.ambari.server.actionmanager.ActionManager; +import org.apache.ambari.server.actionmanager.RequestFactory; +import org.apache.ambari.server.actionmanager.StageFactory; +import org.apache.ambari.server.api.services.AmbariMetaInfo; +import org.apache.ambari.server.controller.AbstractRootServiceResponseFactory; +import org.apache.ambari.server.controller.AmbariManagementController; +import org.apache.ambari.server.controller.KerberosHelper; +import org.apache.ambari.server.controller.spi.ClusterController; +import org.apache.ambari.server.events.publishers.AmbariEventPublisher; +import org.apache.ambari.server.orm.DBAccessor; +import org.apache.ambari.server.orm.dao.ClusterDAO; +import org.apache.ambari.server.orm.dao.HostRoleCommandDAO; +import org.apache.ambari.server.orm.entities.ClusterConfigEntity; +import org.apache.ambari.server.orm.entities.ClusterEntity; +import org.apache.ambari.server.orm.entities.ClusterServiceEntity; +import org.apache.ambari.server.orm.entities.ClusterVersionEntity; +import org.apache.ambari.server.orm.entities.RepositoryVersionEntity; +import org.apache.ambari.server.orm.entities.StackEntity; +import org.apache.ambari.server.orm.entities.UpgradeEntity; +import org.apache.ambari.server.scheduler.ExecutionScheduler; +import org.apache.ambari.server.security.authorization.Users; +import org.apache.ambari.server.stack.StackManagerFactory; +import org.apache.ambari.server.stageplanner.RoleGraphFactory; +import org.apache.ambari.server.stageplanner.RoleGraphFactoryImpl; +import org.apache.ambari.server.state.Cluster; +import org.apache.ambari.server.state.Clusters; +import org.apache.ambari.server.state.ConfigFactory; +import org.apache.ambari.server.state.ServiceComponentFactory; +import org.apache.ambari.server.state.ServiceComponentHostFactory; +import org.apache.ambari.server.state.ServiceFactory; +import org.apache.ambari.server.state.ServiceInfo; +import org.apache.ambari.server.state.configgroup.ConfigGroupFactory; +import org.apache.ambari.server.state.scheduler.RequestExecutionFactory; +import org.apache.ambari.server.state.stack.OsFamily; +import org.apache.ambari.server.state.stack.upgrade.Direction; +import org.apache.ambari.server.state.stack.upgrade.UpgradeType; +import org.easymock.EasyMock; +import org.easymock.EasyMockSupport; +import org.eclipse.jetty.server.SessionManager; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.springframework.security.crypto.password.PasswordEncoder; + +import com.google.common.collect.Lists; +import com.google.inject.Binder; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Module; + +import junit.framework.Assert; + +/** + * Tests that cluster effective version is calcualted correctly during upgrades. + */ +@RunWith(value = PowerMockRunner.class) +@PrepareForTest({ ClusterImpl.class }) +public class ClusterEffectiveVersionTest extends EasyMockSupport { + + private Injector m_injector; + private ClusterEntity m_clusterEntity; + private Cluster m_cluster; + + /** + * @throws Exception + */ + @Before + public void setup() throws Exception { + m_injector = Guice.createInjector(new MockModule()); + m_clusterEntity = createNiceMock(ClusterEntity.class); + + expectClusterEntityMocks(); + + AmbariEventPublisher eventPublisher = createNiceMock(AmbariEventPublisher.class); + + replayAll(); + + m_cluster = new ClusterImpl(m_clusterEntity, m_injector, eventPublisher); + + verifyAll(); + } + + /** + * Tests that {@link Cluster#getEffectiveClusterVersion()} returns the + * "current" version when there is no upgrade in progress. + */ + @Test + public void testEffectiveVersionWithNoUpgrade() throws Exception { + Cluster clusterSpy = Mockito.spy(m_cluster); + + Mockito.doReturn(null).when(clusterSpy).getUpgradeInProgress(); + + ClusterVersionEntity currentClusterVersion = new ClusterVersionEntity(); + Mockito.doReturn(currentClusterVersion).when(clusterSpy).getCurrentClusterVersion(); + + ClusterVersionEntity effectiveVersion = clusterSpy.getEffectiveClusterVersion(); + Assert.assertEquals(currentClusterVersion, effectiveVersion); + } + + /** + * Tests that {@link Cluster#getEffectiveClusterVersion()} returns the target + * version in an active rolling upgrade. + */ + @Test + public void testEffectiveVersionWithActiveRollingUpgrade() throws Exception { + resetAll(); + expectClusterEntityMocks(); + + Cluster clusterSpy = Mockito.spy(m_cluster); + + UpgradeEntity upgradeEntity = createNiceMock(UpgradeEntity.class); + EasyMock.expect(upgradeEntity.getUpgradeType()).andReturn(UpgradeType.ROLLING).atLeastOnce(); + EasyMock.expect(upgradeEntity.getFromVersion()).andReturn("2.3.0.0-1234").anyTimes(); + EasyMock.expect(upgradeEntity.getToVersion()).andReturn("2.4.0.0-1234").atLeastOnce(); + + RepositoryVersionEntity repositoryVersionEntity = createNiceMock(RepositoryVersionEntity.class); + EasyMock.expect(repositoryVersionEntity.getVersion()).andReturn("2.4.0.0-1234").atLeastOnce(); + + ClusterVersionEntity clusterVersionUpgradingTo = createNiceMock(ClusterVersionEntity.class); + EasyMock.expect(clusterVersionUpgradingTo.getRepositoryVersion()).andReturn( + repositoryVersionEntity).atLeastOnce(); + + List<ClusterVersionEntity> clusterVersionEntities = Lists.newArrayList(clusterVersionUpgradingTo); + EasyMock.expect(m_clusterEntity.getClusterVersionEntities()).andReturn(clusterVersionEntities).atLeastOnce(); + + replayAll(); + + Mockito.doReturn(upgradeEntity).when(clusterSpy).getUpgradeInProgress(); + + // this shouldn't be returned since there is an upgrade in progress + ClusterVersionEntity currentClusterVersion = new ClusterVersionEntity(); + Mockito.doReturn(currentClusterVersion).when(clusterSpy).getCurrentClusterVersion(); + + ClusterVersionEntity effectiveVersion = clusterSpy.getEffectiveClusterVersion(); + Assert.assertEquals(clusterVersionUpgradingTo, effectiveVersion); + + verifyAll(); + } + + /** + * Tests that {@link Cluster#getEffectiveClusterVersion()} returns the target + * version in an active rolling upgrade. + */ + @Test + public void testEffectiveVersionWithActiveExpressDowngrade() throws Exception { + resetAll(); + expectClusterEntityMocks(); + + Cluster clusterSpy = Mockito.spy(m_cluster); + + // from/to are switched on downgrade + UpgradeEntity upgradeEntity = createNiceMock(UpgradeEntity.class); + EasyMock.expect(upgradeEntity.getUpgradeType()).andReturn(UpgradeType.NON_ROLLING).atLeastOnce(); + EasyMock.expect(upgradeEntity.getToVersion()).andReturn("2.3.0.0-1234").atLeastOnce(); + EasyMock.expect(upgradeEntity.getFromVersion()).andReturn("2.4.0.0-1234").anyTimes(); + EasyMock.expect(upgradeEntity.getDirection()).andReturn(Direction.DOWNGRADE).atLeastOnce(); + + RepositoryVersionEntity repositoryVersionEntity = createNiceMock(RepositoryVersionEntity.class); + EasyMock.expect(repositoryVersionEntity.getVersion()).andReturn("2.3.0.0-1234").atLeastOnce(); + + ClusterVersionEntity clusterVersionUpgradingTo = createNiceMock(ClusterVersionEntity.class); + EasyMock.expect(clusterVersionUpgradingTo.getRepositoryVersion()).andReturn( + repositoryVersionEntity).atLeastOnce(); + + List<ClusterVersionEntity> clusterVersionEntities = Lists.newArrayList(clusterVersionUpgradingTo); + EasyMock.expect(m_clusterEntity.getClusterVersionEntities()).andReturn(clusterVersionEntities).atLeastOnce(); + + replayAll(); + + Mockito.doReturn(upgradeEntity).when(clusterSpy).getUpgradeInProgress(); + + // this shouldn't be returned since there is an upgrade in progress + ClusterVersionEntity currentClusterVersion = new ClusterVersionEntity(); + Mockito.doReturn(currentClusterVersion).when(clusterSpy).getCurrentClusterVersion(); + + ClusterVersionEntity effectiveVersion = clusterSpy.getEffectiveClusterVersion(); + Assert.assertEquals(clusterVersionUpgradingTo, effectiveVersion); + + verifyAll(); + } + + /** + * Sets the expectations on the {@link ClusterEntity} mock. + */ + private void expectClusterEntityMocks() { + ClusterDAO clusterDAO = m_injector.getInstance(ClusterDAO.class); + StackEntity stackEntity = createNiceMock(StackEntity.class); + + EasyMock.expect(clusterDAO.findById(1L)).andReturn(m_clusterEntity).anyTimes(); + + EasyMock.expect(stackEntity.getStackId()).andReturn(1L).anyTimes(); + EasyMock.expect(stackEntity.getStackName()).andReturn("HDP").anyTimes(); + EasyMock.expect(stackEntity.getStackVersion()).andReturn("2.3").anyTimes(); + + EasyMock.expect(m_clusterEntity.getClusterId()).andReturn(1L).anyTimes(); + EasyMock.expect(m_clusterEntity.getClusterName()).andReturn("c1").anyTimes(); + EasyMock.expect(m_clusterEntity.getDesiredStack()).andReturn(stackEntity).anyTimes(); + EasyMock.expect(m_clusterEntity.getClusterServiceEntities()).andReturn( + new ArrayList<ClusterServiceEntity>()).anyTimes(); + EasyMock.expect(m_clusterEntity.getClusterConfigEntities()).andReturn( + new ArrayList<ClusterConfigEntity>()).anyTimes(); + } + + /** + * + */ + private class MockModule implements Module { + /** + * + */ + @Override + public void configure(Binder binder) { + binder.bind(Clusters.class).toInstance(EasyMock.createNiceMock(Clusters.class)); + binder.bind(OsFamily.class).toInstance(EasyMock.createNiceMock(OsFamily.class)); + binder.bind(DBAccessor.class).toInstance(EasyMock.createNiceMock(DBAccessor.class)); + binder.bind(EntityManager.class).toInstance(EasyMock.createNiceMock(EntityManager.class)); + binder.bind(ActionManager.class).toInstance(EasyMock.createNiceMock(ActionManager.class)); + binder.bind(HostRoleCommandDAO.class).toInstance(EasyMock.createNiceMock(HostRoleCommandDAO.class)); + binder.bind(AmbariManagementController.class).toInstance(EasyMock.createNiceMock(AmbariManagementController.class)); + binder.bind(ClusterController.class).toInstance(EasyMock.createNiceMock(ClusterController.class)); + binder.bind(StackManagerFactory.class).toInstance(EasyMock.createNiceMock(StackManagerFactory.class)); + binder.bind(SessionManager.class).toInstance(EasyMock.createNiceMock(SessionManager.class)); + binder.bind(RequestExecutionFactory.class).toInstance(EasyMock.createNiceMock(RequestExecutionFactory.class)); + binder.bind(ExecutionScheduler.class).toInstance(EasyMock.createNiceMock(ExecutionScheduler.class)); + binder.bind(RequestFactory.class).toInstance(EasyMock.createNiceMock(RequestFactory.class)); + binder.bind(StageFactory.class).toInstance(EasyMock.createNiceMock(StageFactory.class)); + binder.bind(RoleGraphFactory.class).toInstance(EasyMock.createNiceMock(RoleGraphFactoryImpl.class)); + binder.bind(AbstractRootServiceResponseFactory.class).toInstance(EasyMock.createNiceMock(AbstractRootServiceResponseFactory.class)); + binder.bind(ConfigFactory.class).toInstance(EasyMock.createNiceMock(ConfigFactory.class)); + binder.bind(ConfigGroupFactory.class).toInstance(EasyMock.createNiceMock(ConfigGroupFactory.class)); + binder.bind(ServiceFactory.class).toInstance(EasyMock.createNiceMock(ServiceFactory.class)); + binder.bind(ServiceComponentFactory.class).toInstance(EasyMock.createNiceMock(ServiceComponentFactory.class)); + binder.bind(ServiceComponentHostFactory.class).toInstance(EasyMock.createNiceMock(ServiceComponentHostFactory.class)); + binder.bind(PasswordEncoder.class).toInstance(EasyMock.createNiceMock(PasswordEncoder.class)); + binder.bind(KerberosHelper.class).toInstance(EasyMock.createNiceMock(KerberosHelper.class)); + binder.bind(Users.class).toInstance(EasyMock.createNiceMock(Users.class)); + + + try { + AmbariMetaInfo ambariMetaInfo = EasyMock.createNiceMock(AmbariMetaInfo.class); + EasyMock.expect( + ambariMetaInfo.getServices(EasyMock.anyString(), EasyMock.anyString())).andReturn( + new HashMap<String, ServiceInfo>()); + + EasyMock.replay(ambariMetaInfo); + + binder.bind(AmbariMetaInfo.class).toInstance(ambariMetaInfo); + } catch (Exception exception) { + Assert.fail(exception.toString()); + } + + binder.bind(ClusterDAO.class).toInstance(createNiceMock(ClusterDAO.class)); + } + } +}