You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by jo...@apache.org on 2016/05/11 17:43:08 UTC
ambari git commit: AMBARI-16439 - Restarting Upgraded Component
During a Paused Upgrade Downgrades Component To Old Version (jonathanhurley)
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 <jh...@hortonworks.com>
Authored: Tue May 10 19:34:32 2016 -0400
Committer: Jonathan Hurley <jh...@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));
+ }
+ }
+}