You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by al...@apache.org on 2014/11/12 20:11:47 UTC

ambari git commit: AMBARI-8271. Create DAO and Unit Tests for HostVersion (alejandro)

Repository: ambari
Updated Branches:
  refs/heads/trunk eb4eb8a02 -> 5567b34ee


AMBARI-8271. Create DAO and Unit Tests for HostVersion (alejandro)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/5567b34e
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/5567b34e
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/5567b34e

Branch: refs/heads/trunk
Commit: 5567b34ee2d4c08742d899941df833f6a30a94c0
Parents: eb4eb8a
Author: Alejandro Fernandez <af...@hortonworks.com>
Authored: Wed Nov 12 11:07:57 2014 -0800
Committer: Alejandro Fernandez <af...@hortonworks.com>
Committed: Wed Nov 12 11:08:17 2014 -0800

----------------------------------------------------------------------
 .../ambari/server/orm/dao/HostVersionDAO.java   | 161 +++++++++++
 .../server/orm/entities/ClusterEntity.java      |   8 +
 .../orm/entities/ClusterVersionEntity.java      |   4 -
 .../ambari/server/orm/entities/HostEntity.java  |   7 +-
 .../server/orm/entities/HostVersionEntity.java  |  53 +++-
 .../ambari/server/state/HostVersionState.java   |  49 ----
 .../server/orm/dao/HostVersionDAOTest.java      | 289 +++++++++++++++++++
 7 files changed, 512 insertions(+), 59 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/5567b34e/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostVersionDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostVersionDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostVersionDAO.java
new file mode 100644
index 0000000..8d147a1
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostVersionDAO.java
@@ -0,0 +1,161 @@
+/**
+ * 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 com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import com.google.inject.persist.Transactional;
+import org.apache.ambari.server.orm.RequiresSession;
+import org.apache.ambari.server.orm.entities.HostVersionEntity;
+import org.apache.ambari.server.state.UpgradeState;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import java.util.List;
+
+/**
+ * The {@link org.apache.ambari.server.orm.dao.HostVersionDAO} class manages the {@link org.apache.ambari.server.orm.entities.HostVersionEntity}
+ * instances associated with a host. Each host can have multiple stack versions in {@link org.apache.ambari.server.state.UpgradeState#NONE}
+ * which are installed, exactly one stack version that is either {@link org.apache.ambari.server.state.UpgradeState#PENDING} or
+ * {@link org.apache.ambari.server.state.UpgradeState#IN_PROGRESS}.
+ */
+@Singleton
+public class HostVersionDAO {
+  @Inject
+  Provider<EntityManager> entityManagerProvider;
+  @Inject
+  DaoUtils daoUtils;
+
+  /**
+   * Get the object with the given id.
+   *
+   * @param id Primary key id
+   * @return Return the object with the given primary key
+   */
+  @RequiresSession
+  public HostVersionEntity findByPK(long id) {
+    return entityManagerProvider.get().find(HostVersionEntity.class, id);
+  }
+
+  /**
+   * Retrieve all of the host versions for the given cluster name, stack name, and stack version.
+   *
+   * @param clusterName Cluster name
+   * @param stack Stack name (e.g., HDP)
+   * @param version Stack version (e.g., 2.2.0.1-995)
+   * @return Return all of the host versions that match the criteria.
+   */
+  @RequiresSession
+  public List<HostVersionEntity> findByClusterStackAndVersion(String clusterName, String stack, String version) {
+    final TypedQuery<HostVersionEntity> query = entityManagerProvider.get().createNamedQuery("hostVersionByClusterAndStackAndVersion", HostVersionEntity.class);
+    query.setParameter("clusterName", clusterName);
+    query.setParameter("stack", stack);
+    query.setParameter("version", version);
+
+    return daoUtils.selectList(query);
+  }
+
+  /**
+   * Retrieve all of the host versions for the given cluster name and host name.
+   *
+   * @param clusterName Cluster name
+   * @param hostName FQDN of host
+   * @return Return all of the host versions that match the criteria.
+   */
+  @RequiresSession
+  public List<HostVersionEntity> findByClusterAndHost(String  clusterName, String hostName) {
+    final TypedQuery<HostVersionEntity> query = entityManagerProvider.get()
+        .createNamedQuery("hostVersionByClusterAndHostname", HostVersionEntity.class);
+    query.setParameter("clusterName", clusterName);
+    query.setParameter("hostName", hostName);
+
+    return daoUtils.selectList(query);
+  }
+
+  /**
+   * Retrieve all of the host versions for the given cluster name, host name, and state.
+   *
+   * @param clusterName Cluster name
+   * @param hostName FQDN of host
+   * @param state Upgrade state
+   * @return Return all of the host versions that match the criteria.
+   */
+  @RequiresSession
+  public List<HostVersionEntity> findByClusterHostAndState(String  clusterName, String hostName, UpgradeState state) {
+    final TypedQuery<HostVersionEntity> query = entityManagerProvider.get()
+        .createNamedQuery("hostVersionByClusterHostnameAndState", HostVersionEntity.class);
+    query.setParameter("clusterName", clusterName);
+    query.setParameter("hostName", hostName);
+    query.setParameter("state", state);
+
+    return daoUtils.selectList(query);
+  }
+
+  /**
+   * Retrieve the single host version for the given cluster, stack name, stack version, and host name.
+   *
+   * @param clusterName Cluster name
+   * @param stack Stack name (e.g., HDP)
+   * @param version Stack version (e.g., 2.2.0.1-995)
+   * @param hostName FQDN of host
+   * @return Returns the single host version that matches the criteria.
+   */
+  @RequiresSession
+  public HostVersionEntity findByClusterStackVersionAndHost(String clusterName, String stack, String version, String hostName) {
+    final TypedQuery<HostVersionEntity> query = entityManagerProvider.get()
+        .createNamedQuery("hostVersionByClusterStackVersionAndHostname", HostVersionEntity.class);
+    query.setParameter("clusterName", clusterName);
+    query.setParameter("stack", stack);
+    query.setParameter("version", version);
+    query.setParameter("hostName", hostName);
+
+    return daoUtils.selectSingle(query);
+  }
+
+  @RequiresSession
+  public List<HostVersionEntity> findAll() {
+    return daoUtils.selectAll(entityManagerProvider.get(), HostVersionEntity.class);
+  }
+
+  @Transactional
+  public void refresh(HostVersionEntity hostVersionEntity) {
+    entityManagerProvider.get().refresh(hostVersionEntity);
+  }
+
+  @Transactional
+  public void create(HostVersionEntity hostVersionEntity) {
+    entityManagerProvider.get().persist(hostVersionEntity);
+  }
+
+  @Transactional
+  public HostVersionEntity merge(HostVersionEntity hostVersionEntity) {
+    return entityManagerProvider.get().merge(hostVersionEntity);
+  }
+
+  @Transactional
+  public void remove(HostVersionEntity hostVersionEntity) {
+    entityManagerProvider.get().remove(merge(hostVersionEntity));
+  }
+
+  @Transactional
+  public void removeByPK(long id) {
+    remove(findByPK(id));
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/5567b34e/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterEntity.java
index 69a2a9d..889a775 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterEntity.java
@@ -20,6 +20,7 @@ package org.apache.ambari.server.orm.entities;
 
 import static org.apache.commons.lang.StringUtils.defaultString;
 
+import java.util.ArrayList;
 import java.util.Collection;
 
 import javax.persistence.Basic;
@@ -294,6 +295,13 @@ public class ClusterEntity {
 
   public void setClusterVersionEntities(Collection<ClusterVersionEntity> clusterVersionEntities) { this.clusterVersionEntities = clusterVersionEntities; }
 
+  public void addClusterVersionEntity(ClusterVersionEntity clusterVersionEntity) {
+    if (this.clusterVersionEntities == null) {
+      this.clusterVersionEntities = new ArrayList<ClusterVersionEntity>();
+    }
+    this.clusterVersionEntities.add(clusterVersionEntity);
+  }
+
   /**
    * Get the admin resource entity.
    *

http://git-wip-us.apache.org/repos/asf/ambari/blob/5567b34e/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterVersionEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterVersionEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterVersionEntity.java
index 46476da..a7d9ab1 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterVersionEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterVersionEntity.java
@@ -34,10 +34,6 @@ import javax.persistence.NamedQuery;
 import javax.persistence.NamedQueries;
 import javax.persistence.TableGenerator;
 import javax.persistence.Table;
-import javax.persistence.Temporal;
-import javax.persistence.TemporalType;
-
-import java.util.Date;
 
 import static org.apache.commons.lang.StringUtils.defaultString;
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/5567b34e/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostEntity.java
index 8f850e0..df22de1 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostEntity.java
@@ -36,7 +36,7 @@ import static org.apache.commons.lang.StringUtils.defaultString;
 
 @javax.persistence.Table(name = "hosts")
 @Entity
-public class HostEntity {
+public class HostEntity implements Comparable<HostEntity> {
 
   @Id
   @Column(name = "host_name", nullable = false, insertable = true, updatable = true)
@@ -261,6 +261,11 @@ public class HostEntity {
     return hostName.hashCode();
   }
 
+  @Override
+  public int compareTo(HostEntity other) {
+    return this.hostName.compareTo(other.hostName);
+  }
+
   public Collection<HostComponentDesiredStateEntity> getHostComponentDesiredStateEntities() {
     return hostComponentDesiredStateEntities;
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/5567b34e/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostVersionEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostVersionEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostVersionEntity.java
index 6bad11b..5b1b4f8 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostVersionEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostVersionEntity.java
@@ -18,7 +18,7 @@
 
 package org.apache.ambari.server.orm.entities;
 
-import org.apache.ambari.server.state.HostVersionState;
+import org.apache.ambari.server.state.UpgradeState;
 
 import javax.persistence.Basic;
 import javax.persistence.Column;
@@ -30,8 +30,10 @@ import javax.persistence.GenerationType;
 import javax.persistence.Id;
 import javax.persistence.JoinColumn;
 import javax.persistence.ManyToOne;
-import javax.persistence.Table;
+import javax.persistence.NamedQuery;
+import javax.persistence.NamedQueries;
 import javax.persistence.TableGenerator;
+import javax.persistence.Table;
 
 import static org.apache.commons.lang.StringUtils.defaultString;
 
@@ -43,6 +45,24 @@ import static org.apache.commons.lang.StringUtils.defaultString;
     , initialValue = 0
     , allocationSize = 1
 )
+@NamedQueries({
+    @NamedQuery(name = "hostVersionByClusterAndStackAndVersion", query =
+        "SELECT hostVersion FROM HostVersionEntity hostVersion JOIN hostVersion.hostEntity host JOIN ClusterEntity cluster " +
+            "WHERE cluster.clusterName=:clusterName AND hostVersion.stack=:stack AND hostVersion.version=:version"),
+
+    @NamedQuery(name = "hostVersionByClusterAndHostname", query =
+        "SELECT hostVersion FROM HostVersionEntity hostVersion JOIN hostVersion.hostEntity host JOIN ClusterEntity cluster " +
+            "WHERE cluster.clusterName=:clusterName AND hostVersion.hostName=:hostName"),
+
+    @NamedQuery(name = "hostVersionByClusterHostnameAndState", query =
+        "SELECT hostVersion FROM HostVersionEntity hostVersion JOIN hostVersion.hostEntity host JOIN ClusterEntity cluster " +
+            "WHERE cluster.clusterName=:clusterName AND hostVersion.hostName=:hostName AND hostVersion.state=:state"),
+
+    @NamedQuery(name = "hostVersionByClusterStackVersionAndHostname", query =
+        "SELECT hostVersion FROM HostVersionEntity hostVersion JOIN hostVersion.hostEntity host JOIN ClusterEntity cluster " +
+            "WHERE cluster.clusterName=:clusterName AND hostVersion.stack=:stack AND hostVersion.version=:version AND " +
+            "hostVersion.hostName=:hostName"),
+})
 public class HostVersionEntity {
 
   @Id
@@ -67,7 +87,30 @@ public class HostVersionEntity {
 
   @Column(name = "state", nullable = false, insertable = true, updatable = true)
   @Enumerated(value = EnumType.STRING)
-  private HostVersionState state = HostVersionState.CURRENT;
+  private UpgradeState state = UpgradeState.NONE;
+
+  /**
+   * Empty constructor is needed by unit tests.
+   */
+  public HostVersionEntity() {
+  }
+
+  public HostVersionEntity(String hostName, String stack, String version, UpgradeState state) {
+    this.hostName = hostName;
+    this.stack = stack;
+    this.version = version;
+    this.state = state;
+  }
+
+  /**
+   * This constructor is mainly used by the unit tests in order to construct an object without the id.
+   */
+  public HostVersionEntity(HostVersionEntity other) {
+    this.hostName = other.hostName;
+    this.stack = other.stack;
+    this.version = other.version;
+    this.state = other.state;
+  }
 
   public Long getId() {
     return id;
@@ -109,11 +152,11 @@ public class HostVersionEntity {
     this.version = version;
   }
 
-  public HostVersionState getState() {
+  public UpgradeState getState() {
     return state;
   }
 
-  public void setState(HostVersionState state) {
+  public void setState(UpgradeState state) {
     this.state = state;
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/5567b34e/ambari-server/src/main/java/org/apache/ambari/server/state/HostVersionState.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/HostVersionState.java b/ambari-server/src/main/java/org/apache/ambari/server/state/HostVersionState.java
deleted file mode 100644
index b0a0ee2..0000000
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/HostVersionState.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * 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;
-
-/**
- * There must be exactly one stack version that is in the CURRENT state.
- * There may be 0 or more stack versions in an INSTALLED state.
- * Installing a new stack version on a host transitions from NOT_INSTALLED to CURRENT once the upgrade finishes.
- * The operation to transition a new stack version on the host from the NOT_INSTALLED state to CURRENT must be atomic
- * and change the existing stack version on the host from CURRENT to INSTALLED.
- *
- * <pre>
- * Start states: CURRENT, NOT_INSTALLED
- * Allowed Transitions:
- * NOT_INSTALLED -> CURRENT
- * CURRENT -> INSTALLED
- * INSTALLED -> CURRENT
- * </pre>
- */
-public enum HostVersionState {
-  /**
-   * Stack version that is installed on this host for all of its components but is not the active stack version on the host.
-   */
-  INSTALLED,
-  /**
-   * Stack version that is installed on this host for all of its components and is the active stack version on the host.
-   */
-  CURRENT,
-  /**
-   * Stack version that remains to be installed on this host, potentially because it is in the process of being installed.
-   */
-  NOT_INSTALLED;
-}

http://git-wip-us.apache.org/repos/asf/ambari/blob/5567b34e/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/HostVersionDAOTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/HostVersionDAOTest.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/HostVersionDAOTest.java
new file mode 100644
index 0000000..93f78b2
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/HostVersionDAOTest.java
@@ -0,0 +1,289 @@
+/**
+ * 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 com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.persist.PersistService;
+import org.apache.ambari.server.orm.GuiceJpaInitializer;
+import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
+import org.apache.ambari.server.orm.entities.*;
+import org.apache.ambari.server.state.ClusterVersionState;
+import org.apache.ambari.server.state.UpgradeState;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+
+/**
+ * {@link org.apache.ambari.server.orm.dao.HostVersionDAO} unit tests.
+ */
+public class HostVersionDAOTest {
+
+  private static Injector injector;
+  private ResourceTypeDAO resourceTypeDAO;
+  private ClusterDAO clusterDAO;
+  private ClusterVersionDAO clusterVersionDAO;
+  private HostDAO hostDAO;
+  private HostVersionDAO hostVersionDAO;
+
+  @Before
+  public void before() {
+    injector = Guice.createInjector(new InMemoryDefaultTestModule());
+    injector.getInstance(GuiceJpaInitializer.class);
+
+    resourceTypeDAO = injector.getInstance(ResourceTypeDAO.class);
+    clusterDAO = injector.getInstance(ClusterDAO.class);
+    clusterVersionDAO = injector.getInstance(ClusterVersionDAO.class);
+    hostDAO = injector.getInstance(HostDAO.class);
+    hostVersionDAO = injector.getInstance(HostVersionDAO.class);
+
+    createDefaultData();
+  }
+
+  /**
+   * Helper function to bootstrap some basic data about clusters, cluster version, host, and host versions.
+   */
+  private void createDefaultData() {
+    // Create the cluster
+    ResourceTypeEntity resourceTypeEntity = new ResourceTypeEntity();
+    resourceTypeEntity.setId(ResourceTypeEntity.CLUSTER_RESOURCE_TYPE);
+    resourceTypeEntity.setName(ResourceTypeEntity.CLUSTER_RESOURCE_TYPE_NAME);
+    resourceTypeEntity = resourceTypeDAO.merge(resourceTypeEntity);
+
+    ResourceEntity resourceEntity = new ResourceEntity();
+    resourceEntity.setResourceType(resourceTypeEntity);
+
+    ClusterEntity clusterEntity = new ClusterEntity();
+    clusterEntity.setClusterName("test_cluster1");
+    clusterEntity.setClusterInfo("test_cluster_info1");
+    clusterEntity.setResource(resourceEntity);
+    clusterDAO.create(clusterEntity);
+
+    // Create the Cluster Version and link it to the cluster
+    ClusterVersionEntity clusterVersionEntity = new ClusterVersionEntity(clusterEntity, "HDP", "2.2.0.0-995", ClusterVersionState.CURRENT, System.currentTimeMillis(), System.currentTimeMillis(), "admin");
+    List<ClusterVersionEntity> clusterVersionEntities = new ArrayList<ClusterVersionEntity>();
+    clusterVersionEntities.add(clusterVersionEntity);
+    clusterEntity.setClusterVersionEntities(clusterVersionEntities);
+
+    clusterVersionDAO.create(clusterVersionEntity);
+    clusterDAO.merge(clusterEntity);
+
+    // Create the hosts
+    HostEntity host1 = new HostEntity();
+    HostEntity host2 = new HostEntity();
+    HostEntity host3 = new HostEntity();
+
+    host1.setHostName("test_host1");
+    host2.setHostName("test_host2");
+    host3.setHostName("test_host3");
+    host1.setIpv4("192.168.0.1");
+    host2.setIpv4("192.168.0.2");
+    host3.setIpv4("192.168.0.3");
+
+    List<HostEntity> hostEntities = new ArrayList<HostEntity>();
+    hostEntities.add(host1);
+    hostEntities.add(host2);
+    hostEntities.add(host3);
+
+    clusterEntity.setHostEntities(hostEntities);
+    // Both sides of relation should be set when modifying in runtime
+    host1.setClusterEntities(Arrays.asList(clusterEntity));
+    host2.setClusterEntities(Arrays.asList(clusterEntity));
+    host2.setClusterEntities(Arrays.asList(clusterEntity));
+
+    hostDAO.create(host1);
+    hostDAO.create(host2);
+    hostDAO.create(host3);
+    clusterDAO.merge(clusterEntity);
+
+    // Create the Host Versions
+    HostVersionEntity hostVersionEntity1 = new HostVersionEntity(host1.getHostName(), clusterVersionEntity.getStack(), clusterVersionEntity.getVersion(), UpgradeState.NONE);
+    HostVersionEntity hostVersionEntity2 = new HostVersionEntity(host2.getHostName(), clusterVersionEntity.getStack(), clusterVersionEntity.getVersion(), UpgradeState.NONE);
+    HostVersionEntity hostVersionEntity3 = new HostVersionEntity(host3.getHostName(), clusterVersionEntity.getStack(), clusterVersionEntity.getVersion(), UpgradeState.NONE);
+    hostVersionEntity1.setHostEntity(host1);
+    hostVersionEntity2.setHostEntity(host2);
+    hostVersionEntity3.setHostEntity(host3);
+
+    hostVersionDAO.create(hostVersionEntity1);
+    hostVersionDAO.create(hostVersionEntity2);
+    hostVersionDAO.create(hostVersionEntity3);
+  }
+
+  /**
+   * Helper function to bootstrap additional data on top of the default data.
+   */
+  private void addMoreVersions() {
+    ClusterEntity clusterEntity = clusterDAO.findByName("test_cluster1");
+
+    // Create another Cluster Version and mark the old one as INSTALLED
+    if (clusterEntity.getClusterVersionEntities() != null && clusterEntity.getClusterVersionEntities().size() > 0) {
+      ClusterVersionEntity installedClusterVersion = clusterVersionDAO.findByClusterAndStateCurrent(clusterEntity.getClusterName());
+      installedClusterVersion.setState(ClusterVersionState.INSTALLED);
+      clusterVersionDAO.merge(installedClusterVersion);
+    } else {
+      Assert.fail("Cluster is expected to have at least one cluster version");
+    }
+
+    ClusterVersionEntity newClusterVersionEntity = new ClusterVersionEntity(clusterEntity, "HDP", "2.2.0.1-996", ClusterVersionState.CURRENT, System.currentTimeMillis(), System.currentTimeMillis(), "admin");
+    clusterEntity.addClusterVersionEntity(newClusterVersionEntity);
+    clusterVersionDAO.create(newClusterVersionEntity);
+
+    HostEntity[] hostEntities = clusterEntity.getHostEntities().toArray(new HostEntity[clusterEntity.getHostEntities().size()]);
+    // Must sort by host name in ascending order to ensure that state is accurately set later on.
+    Arrays.sort(hostEntities);
+
+    // For each of the hosts, add a host version
+    for (HostEntity host : hostEntities) {
+      HostVersionEntity hostVersionEntity = new HostVersionEntity(host.getHostName(), "HDP", "2.2.0.1-996", UpgradeState.NONE);
+      hostVersionEntity.setHostEntity(host);
+      hostVersionDAO.create(hostVersionEntity);
+    }
+
+    // For each of the hosts, add one more host version
+    for (int i = 0; i < hostEntities.length; i++) {
+      UpgradeState desiredState = UpgradeState.NONE;
+      if (i % 3 == 0) {
+        desiredState = UpgradeState.PENDING;
+      }
+      if (i % 3 == 1) {
+        desiredState = UpgradeState.IN_PROGRESS;
+      }
+      if (i % 3 == 2) {
+        desiredState = UpgradeState.FAILED;
+      }
+
+      HostVersionEntity hostVersionEntity = new HostVersionEntity(hostEntities[i].getHostName(), "HDP", "2.2.1.0-500", desiredState);
+      hostVersionEntity.setHostEntity(hostEntities[i]);
+      hostVersionDAO.create(hostVersionEntity);
+    }
+  }
+
+  /**
+   * Test the {@link HostVersionDAO#findAll()} method.
+   */
+  @Test
+  public void testFindAll() {
+    Assert.assertEquals(3, hostVersionDAO.findAll().size());
+  }
+
+  /**
+   * Test the {@link HostVersionDAO#findByClusterStackAndVersion(String, String, String)} method.
+   */
+  @Test
+  public void testFindByClusterStackAndVersion() {
+    Assert.assertEquals(3, hostVersionDAO.findByClusterStackAndVersion("test_cluster1", "HDP", "2.2.0.0-995").size());
+    Assert.assertEquals(3, hostVersionDAO.findAll().size());
+
+    addMoreVersions();
+
+    Assert.assertEquals(3, hostVersionDAO.findByClusterStackAndVersion("test_cluster1", "HDP", "2.2.0.1-996").size());
+    Assert.assertEquals(3, hostVersionDAO.findByClusterStackAndVersion("test_cluster1", "HDP", "2.2.1.0-500").size());
+    Assert.assertEquals(9, hostVersionDAO.findAll().size());
+  }
+
+  /**
+   * Test the {@link HostVersionDAO#findByClusterAndHost(String, String)} method.
+   */
+  @Test
+  public void testFindByClusterAndHost() {
+    Assert.assertEquals(1, hostVersionDAO.findByClusterAndHost("test_cluster1", "test_host1").size());
+    Assert.assertEquals(1, hostVersionDAO.findByClusterAndHost("test_cluster1", "test_host2").size());
+    Assert.assertEquals(1, hostVersionDAO.findByClusterAndHost("test_cluster1", "test_host3").size());
+
+    addMoreVersions();
+
+    Assert.assertEquals(3, hostVersionDAO.findByClusterAndHost("test_cluster1", "test_host1").size());
+    Assert.assertEquals(3, hostVersionDAO.findByClusterAndHost("test_cluster1", "test_host2").size());
+    Assert.assertEquals(3, hostVersionDAO.findByClusterAndHost("test_cluster1", "test_host3").size());
+  }
+
+  /**
+   * Test the {@link HostVersionDAO#findByClusterHostAndState(String, String, org.apache.ambari.server.state.UpgradeState)} method.
+   */
+  @Test
+  public void testFindByClusterHostAndState() {
+    Assert.assertEquals(1, hostVersionDAO.findByClusterHostAndState("test_cluster1", "test_host1", UpgradeState.NONE).size());
+    Assert.assertEquals(0, hostVersionDAO.findByClusterHostAndState("test_cluster1", "test_host1", UpgradeState.PENDING).size());
+    Assert.assertEquals(0, hostVersionDAO.findByClusterHostAndState("test_cluster1", "test_host2", UpgradeState.IN_PROGRESS).size());
+    Assert.assertEquals(0, hostVersionDAO.findByClusterHostAndState("test_cluster1", "test_host3", UpgradeState.FAILED).size());
+
+    addMoreVersions();
+
+    Assert.assertEquals(2, hostVersionDAO.findByClusterHostAndState("test_cluster1", "test_host1", UpgradeState.NONE).size());
+    Assert.assertEquals(2, hostVersionDAO.findByClusterHostAndState("test_cluster1", "test_host2", UpgradeState.NONE).size());
+    Assert.assertEquals(2, hostVersionDAO.findByClusterHostAndState("test_cluster1", "test_host3", UpgradeState.NONE).size());
+
+    Assert.assertEquals(1, hostVersionDAO.findByClusterHostAndState("test_cluster1", "test_host1", UpgradeState.PENDING).size());
+    Assert.assertEquals(1, hostVersionDAO.findByClusterHostAndState("test_cluster1", "test_host2", UpgradeState.IN_PROGRESS).size());
+    Assert.assertEquals(1, hostVersionDAO.findByClusterHostAndState("test_cluster1", "test_host3", UpgradeState.FAILED).size());
+  }
+
+  /**
+   * Test the {@link HostVersionDAO#findByClusterStackVersionAndHost(String, String, String, String)} method.
+   */
+  @Test
+  public void testFindByClusterStackVersionAndHost() {
+    HostVersionEntity hostVersionEntity1 = new HostVersionEntity("test_host1", "HDP", "2.2.0.0-995", UpgradeState.NONE);
+    hostVersionEntity1.setId(1L);
+    HostVersionEntity hostVersionEntity2 = new HostVersionEntity("test_host2", "HDP", "2.2.0.0-995", UpgradeState.NONE);
+    hostVersionEntity2.setId(2L);
+    HostVersionEntity hostVersionEntity3 = new HostVersionEntity("test_host3", "HDP", "2.2.0.0-995", UpgradeState.NONE);
+    hostVersionEntity3.setId(3L);
+
+    Assert.assertEquals(hostVersionEntity1, hostVersionDAO.findByClusterStackVersionAndHost("test_cluster1", "HDP", "2.2.0.0-995", "test_host1"));
+    Assert.assertEquals(hostVersionEntity2, hostVersionDAO.findByClusterStackVersionAndHost("test_cluster1", "HDP", "2.2.0.0-995", "test_host2"));
+    Assert.assertEquals(hostVersionEntity3, hostVersionDAO.findByClusterStackVersionAndHost("test_cluster1", "HDP", "2.2.0.0-995", "test_host3"));
+
+    // Test non-existent objects
+    Assert.assertEquals(null, hostVersionDAO.findByClusterStackVersionAndHost("non_existent_cluster", "HDP", "2.2.0.0-995", "test_host3"));
+    Assert.assertEquals(null, hostVersionDAO.findByClusterStackVersionAndHost("test_cluster1", "non_existent_stack", "2.2.0.0-995", "test_host3"));
+    Assert.assertEquals(null, hostVersionDAO.findByClusterStackVersionAndHost("test_cluster1", "HDP", "non_existent_version", "test_host3"));
+    Assert.assertEquals(null, hostVersionDAO.findByClusterStackVersionAndHost("test_cluster1", "HDP", "non_existent_version", "non_existent_host"));
+
+    addMoreVersions();
+
+    // Expected
+    HostVersionEntity hostVersionEntity1LastExpected = new HostVersionEntity("test_host1", "HDP", "2.2.1.0-500", UpgradeState.PENDING);
+    HostVersionEntity hostVersionEntity2LastExpected = new HostVersionEntity("test_host2", "HDP", "2.2.1.0-500", UpgradeState.IN_PROGRESS);
+    HostVersionEntity hostVersionEntity3LastExpected = new HostVersionEntity("test_host3", "HDP", "2.2.1.0-500", UpgradeState.FAILED);
+
+    // Actual
+    HostVersionEntity hostVersionEntity1LastActual = hostVersionDAO.findByClusterStackVersionAndHost("test_cluster1", "HDP", "2.2.1.0-500", "test_host1");
+    HostVersionEntity hostVersionEntity2LastActual = hostVersionDAO.findByClusterStackVersionAndHost("test_cluster1", "HDP", "2.2.1.0-500", "test_host2");
+    HostVersionEntity hostVersionEntity3LastActual = hostVersionDAO.findByClusterStackVersionAndHost("test_cluster1", "HDP", "2.2.1.0-500", "test_host3");
+
+    // Trying to Mock the actual objects to override the getId() method will not work because the class that mockito creates
+    // is still a Mockito wrapper. Instead, take advantage of an overloaded constructor that ignores the Id.
+    Assert.assertEquals(hostVersionEntity1LastExpected, new HostVersionEntity(hostVersionEntity1LastActual));
+    Assert.assertEquals(hostVersionEntity2LastExpected, new HostVersionEntity(hostVersionEntity2LastActual));
+    Assert.assertEquals(hostVersionEntity3LastExpected, new HostVersionEntity(hostVersionEntity3LastActual));
+  }
+
+  @After
+  public void after() {
+    injector.getInstance(PersistService.class).stop();
+    injector = null;
+  }
+}
\ No newline at end of file