You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by nc...@apache.org on 2017/04/27 20:20:08 UTC

[21/50] [abbrv] ambari git commit: AMBARI-19381. Heartbeat version transitions must be update Component alongside Cluster (ncole)

AMBARI-19381. Heartbeat version transitions must be update Component alongside Cluster (ncole)


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

Branch: refs/heads/trunk
Commit: cd245c00abaa5720675324a383e691434eabcf32
Parents: b1bb289
Author: Nate Cole <nc...@hortonworks.com>
Authored: Thu Jan 5 05:01:03 2017 -0500
Committer: Nate Cole <nc...@hortonworks.com>
Committed: Thu Jan 5 11:49:52 2017 -0500

----------------------------------------------------------------------
 .../server/checks/AbstractCheckDescriptor.java  |   2 +-
 .../controller/ServiceComponentResponse.java    |  37 +++--
 .../internal/ComponentResourceProvider.java     |  11 +-
 .../internal/UpgradeResourceProvider.java       |   2 +-
 .../listeners/upgrade/StackVersionListener.java |  22 +--
 .../server/orm/dao/HostComponentStateDAO.java   |  20 +++
 .../dao/ServiceComponentDesiredStateDAO.java    |  16 ++
 .../orm/entities/HostComponentStateEntity.java  |   7 +-
 .../orm/entities/RepositoryVersionEntity.java   |   2 -
 .../ServiceComponentDesiredStateEntity.java     |  19 +++
 .../entities/ServiceComponentVersionEntity.java |  26 ++-
 .../ambari/server/state/ServiceComponent.java   |  15 ++
 .../server/state/ServiceComponentImpl.java      | 166 ++++++++++++++++++-
 .../state/stack/upgrade/ClusterGrouping.java    |   3 -
 .../server/upgrade/UpgradeCatalog300.java       |  15 +-
 .../main/resources/Ambari-DDL-Derby-CREATE.sql  |   1 +
 .../main/resources/Ambari-DDL-MySQL-CREATE.sql  |   1 +
 .../main/resources/Ambari-DDL-Oracle-CREATE.sql |   1 +
 .../resources/Ambari-DDL-Postgres-CREATE.sql    |   1 +
 .../resources/Ambari-DDL-SQLAnywhere-CREATE.sql |   1 +
 .../resources/Ambari-DDL-SQLServer-CREATE.sql   |   1 +
 .../src/main/resources/properties.json          |   6 +-
 .../checks/AbstractCheckDescriptorTest.java     |   2 -
 .../internal/ComponentResourceProviderTest.java |  28 +++-
 .../controller/internal/RequestImplTest.java    |   4 +-
 .../apache/ambari/server/orm/OrmTestHelper.java |   1 +
 .../server/state/ServiceComponentTest.java      |  72 ++++++++
 .../server/upgrade/UpgradeCatalog300Test.java   |  48 +++---
 28 files changed, 443 insertions(+), 87 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/main/java/org/apache/ambari/server/checks/AbstractCheckDescriptor.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/checks/AbstractCheckDescriptor.java b/ambari-server/src/main/java/org/apache/ambari/server/checks/AbstractCheckDescriptor.java
index 0046ec6..3a6c45e 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/checks/AbstractCheckDescriptor.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/checks/AbstractCheckDescriptor.java
@@ -43,8 +43,8 @@ import org.apache.ambari.server.state.stack.PrereqCheckType;
 import org.apache.ambari.server.state.stack.PrerequisiteCheck;
 import org.apache.ambari.server.state.stack.UpgradePack;
 import org.apache.ambari.server.state.stack.upgrade.RepositoryVersionHelper;
-import org.apache.commons.collections.CollectionUtils;
 import org.apache.ambari.server.state.stack.upgrade.UpgradeType;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/main/java/org/apache/ambari/server/controller/ServiceComponentResponse.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/ServiceComponentResponse.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/ServiceComponentResponse.java
index 9dbda20..16f724f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/ServiceComponentResponse.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/ServiceComponentResponse.java
@@ -21,27 +21,22 @@ package org.apache.ambari.server.controller;
 
 import java.util.Map;
 
+import org.apache.ambari.server.state.RepositoryVersionState;
+
 public class ServiceComponentResponse {
 
   private Long clusterId; // REF
-
   private String clusterName; // REF
-
   private String serviceName;
-
   private String componentName;
-
   private String displayName;
-
   private String desiredStackVersion;
-
   private String desiredState;
-
   private String category;
-
-  Map<String, Integer> serviceComponentStateCount;
-
+  private Map<String, Integer> serviceComponentStateCount;
   private boolean recoveryEnabled;
+  private String desiredVersion;
+  private RepositoryVersionState repoState;
 
   public ServiceComponentResponse(Long clusterId, String clusterName,
                                   String serviceName,
@@ -50,8 +45,9 @@ public class ServiceComponentResponse {
                                   String desiredState,
                                   Map<String, Integer> serviceComponentStateCount,
                                   boolean recoveryEnabled,
-                                  String displayName) {
-    super();
+                                  String displayName,
+                                  String desiredVersion,
+                                  RepositoryVersionState repoState) {
     this.clusterId = clusterId;
     this.clusterName = clusterName;
     this.serviceName = serviceName;
@@ -61,6 +57,8 @@ public class ServiceComponentResponse {
     this.desiredState = desiredState;
     this.serviceComponentStateCount = serviceComponentStateCount;
     this.recoveryEnabled = recoveryEnabled;
+    this.desiredVersion = desiredVersion;
+    this.repoState = repoState;
   }
 
   /**
@@ -196,6 +194,21 @@ public class ServiceComponentResponse {
     this.recoveryEnabled = recoveryEnabled;
   }
 
+  /**
+   * @return the desired version of the component
+   */
+  public String getDesiredVersion() {
+    return desiredVersion;
+  }
+
+  /**
+   * @return the state of the repository against the desired version
+   */
+  public RepositoryVersionState getRepositoryState() {
+    return repoState;
+  }
+
+
   @Override
   public boolean equals(Object o) {
     if (this == o) return true;

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ComponentResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ComponentResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ComponentResourceProvider.java
index 65cfcaa..ff8d0be 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ComponentResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ComponentResourceProvider.java
@@ -18,7 +18,6 @@
 package org.apache.ambari.server.controller.internal;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.EnumSet;
 import java.util.HashMap;
@@ -64,6 +63,7 @@ import org.apache.ambari.server.state.State;
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.Validate;
 
+import com.google.common.collect.Sets;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
 import com.google.inject.persist.Transactional;
@@ -89,17 +89,18 @@ public class ComponentResourceProvider extends AbstractControllerResourceProvide
   protected static final String COMPONENT_UNKNOWN_COUNT_PROPERTY_ID   = "ServiceComponentInfo/unknown_count";
   protected static final String COMPONENT_INSTALL_FAILED_COUNT_PROPERTY_ID = "ServiceComponentInfo/install_failed_count";
   protected static final String COMPONENT_RECOVERY_ENABLED_ID         = "ServiceComponentInfo/recovery_enabled";
+  protected static final String COMPONENT_DESIRED_VERSION             = "ServiceComponentInfo/desired_version";
+  protected static final String COMPONENT_REPOSITORY_STATE            = "ServiceComponentInfo/repository_state";
 
   private static final String TRUE = "true";
 
   //Parameters from the predicate
   private static final String QUERY_PARAMETERS_RUN_SMOKE_TEST_ID = "params/run_smoke_test";
 
-  private static Set<String> pkPropertyIds =
-      new HashSet<>(Arrays.asList(new String[]{
+  private static Set<String> pkPropertyIds = Sets.newHashSet(
           COMPONENT_CLUSTER_NAME_PROPERTY_ID,
           COMPONENT_SERVICE_NAME_PROPERTY_ID,
-          COMPONENT_COMPONENT_NAME_PROPERTY_ID}));
+          COMPONENT_COMPONENT_NAME_PROPERTY_ID);
 
   private MaintenanceStateHelper maintenanceStateHelper;
 
@@ -188,6 +189,8 @@ public class ComponentResourceProvider extends AbstractControllerResourceProvide
       setResourceProperty(resource, COMPONENT_INIT_COUNT_PROPERTY_ID, response.getServiceComponentStateCount().get("initCount"), requestedIds);
       setResourceProperty(resource, COMPONENT_UNKNOWN_COUNT_PROPERTY_ID, response.getServiceComponentStateCount().get("unknownCount"), requestedIds);
       setResourceProperty(resource, COMPONENT_RECOVERY_ENABLED_ID, String.valueOf(response.isRecoveryEnabled()), requestedIds);
+      setResourceProperty(resource, COMPONENT_DESIRED_VERSION, response.getDesiredVersion(), requestedIds);
+      setResourceProperty(resource, COMPONENT_REPOSITORY_STATE, response.getRepositoryState(), requestedIds);
 
       resources.add(resource);
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
index 7c90cab..71162e4 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
@@ -92,8 +92,8 @@ import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Config;
 import org.apache.ambari.server.state.ConfigHelper;
 import org.apache.ambari.server.state.DesiredConfig;
-import org.apache.ambari.server.state.RepositoryType;
 import org.apache.ambari.server.state.Host;
+import org.apache.ambari.server.state.RepositoryType;
 import org.apache.ambari.server.state.Service;
 import org.apache.ambari.server.state.ServiceComponent;
 import org.apache.ambari.server.state.ServiceInfo;

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/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 f5a5b0c..a29929d 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
@@ -17,9 +17,6 @@
  */
 package org.apache.ambari.server.events.listeners.upgrade;
 
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.EagerSingleton;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
@@ -37,7 +34,6 @@ import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.eventbus.AllowConcurrentEvents;
 import com.google.common.eventbus.Subscribe;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -58,13 +54,7 @@ public class StackVersionListener {
    * Logger.
    */
   private final static Logger LOG = LoggerFactory.getLogger(StackVersionListener.class);
-  private static final String UNKNOWN_VERSION = State.UNKNOWN.toString();
-
-  /**
-   * Used to prevent multiple threads from trying to create host alerts
-   * simultaneously.
-   */
-  private Lock m_stackVersionLock = new ReentrantLock();
+  public static final String UNKNOWN_VERSION = State.UNKNOWN.toString();
 
   @Inject
   private RepositoryVersionDAO repositoryVersionDAO;
@@ -83,7 +73,6 @@ public class StackVersionListener {
   }
 
   @Subscribe
-  @AllowConcurrentEvents
   public void onAmbariEvent(HostComponentVersionAdvertisedEvent event) {
     LOG.debug("Received event {}", event);
 
@@ -96,8 +85,6 @@ public class StackVersionListener {
       return;
     }
 
-    m_stackVersionLock.lock();
-
     // 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()) {
@@ -120,6 +107,7 @@ public class StackVersionListener {
       ComponentInfo componentInfo = metaInfo.getComponent(cluster.getDesiredStackVersion().getStackName(),
       cluster.getDesiredStackVersion().getStackVersion(), sch.getServiceName(), sch.getServiceComponentName());
       ServiceComponent sc = cluster.getService(sch.getServiceName()).getServiceComponent(sch.getServiceComponentName());
+
       if (componentInfo.isVersionAdvertised() && StringUtils.isNotBlank(newVersion)
           && !UNKNOWN_VERSION.equalsIgnoreCase(newVersion)) {
         processComponentAdvertisedVersion(cluster, sch, newVersion, sc);
@@ -135,12 +123,11 @@ public class StackVersionListener {
           processComponentAdvertisedVersion(cluster, sch, newVersion, sc);
         }
       }
+
     } catch (Exception e) {
       LOG.error(
           "Unable to propagate version for ServiceHostComponent on component: {}, host: {}. Error: {}",
           sch.getServiceComponentName(), sch.getHostName(), e.getMessage());
-    } finally {
-      m_stackVersionLock.unlock();
     }
   }
 
@@ -158,6 +145,7 @@ public class StackVersionListener {
     if (StringUtils.isBlank(newVersion)) {
       return;
     }
+
     String previousVersion = sch.getVersion();
     if (previousVersion == null || UNKNOWN_VERSION.equalsIgnoreCase(previousVersion)) {
       // value may be "UNKNOWN" when upgrading from older Ambari versions
@@ -168,6 +156,8 @@ public class StackVersionListener {
     } else if (!StringUtils.equals(previousVersion, newVersion)) {
       processComponentVersionChange(cluster, sc, sch, newVersion);
     }
+
+    sc.updateRepositoryState(newVersion);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostComponentStateDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostComponentStateDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostComponentStateDAO.java
index cc7b503..6174912 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostComponentStateDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostComponentStateDAO.java
@@ -207,4 +207,24 @@ public class HostComponentStateDAO {
 
     em.clear();
   }
+
+  /**
+   * @param serviceName
+   * @param componentName
+   * @param version
+   * @return a list of host components whose version that does NOT match the give version
+   */
+  @RequiresSession
+  public List<HostComponentStateEntity> findByServiceAndComponentAndNotVersion(String serviceName,
+      String componentName, String version) {
+
+    final TypedQuery<HostComponentStateEntity> query = entityManagerProvider.get().createNamedQuery(
+        "HostComponentStateEntity.findByServiceAndComponentAndNotVersion", HostComponentStateEntity.class);
+
+    query.setParameter("serviceName", serviceName);
+    query.setParameter("componentName", componentName);
+    query.setParameter("version", version);
+
+    return daoUtils.selectList(query);
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ServiceComponentDesiredStateDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ServiceComponentDesiredStateDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ServiceComponentDesiredStateDAO.java
index 987e44f..11e41b6 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ServiceComponentDesiredStateDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ServiceComponentDesiredStateDAO.java
@@ -199,4 +199,20 @@ public class ServiceComponentDesiredStateDAO {
     return daoUtils.selectList(query);
   }
 
+  @RequiresSession
+  public ServiceComponentVersionEntity findVersion(long clusterId, String serviceName,
+      String componentName, String version) {
+
+    EntityManager entityManager = entityManagerProvider.get();
+    TypedQuery<ServiceComponentVersionEntity> query = entityManager.createNamedQuery(
+        "ServiceComponentVersionEntity.findByComponentAndVersion", ServiceComponentVersionEntity.class);
+
+    query.setParameter("clusterId", clusterId);
+    query.setParameter("serviceName", serviceName);
+    query.setParameter("componentName", componentName);
+    query.setParameter("repoVersion", version);
+
+    return daoUtils.selectSingle(query);
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostComponentStateEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostComponentStateEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostComponentStateEntity.java
index 9d35e2a..0b3d8ce 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostComponentStateEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostComponentStateEntity.java
@@ -67,7 +67,12 @@ import com.google.common.base.Objects;
         query = "SELECT hcs from HostComponentStateEntity hcs WHERE hcs.serviceName=:serviceName AND hcs.componentName=:componentName AND hcs.hostEntity.hostName=:hostName"),
     @NamedQuery(
         name = "HostComponentStateEntity.findByIndex",
-        query = "SELECT hcs from HostComponentStateEntity hcs WHERE hcs.clusterId=:clusterId AND hcs.serviceName=:serviceName AND hcs.componentName=:componentName AND hcs.hostId=:hostId") })
+        query = "SELECT hcs from HostComponentStateEntity hcs WHERE hcs.clusterId=:clusterId AND hcs.serviceName=:serviceName AND hcs.componentName=:componentName AND hcs.hostId=:hostId"),
+    @NamedQuery(
+        name = "HostComponentStateEntity.findByServiceAndComponentAndNotVersion",
+        query = "SELECT hcs from HostComponentStateEntity hcs WHERE hcs.serviceName=:serviceName AND hcs.componentName=:componentName AND hcs.version != :version")
+})
+
 public class HostComponentStateEntity {
 
   @Id

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RepositoryVersionEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RepositoryVersionEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RepositoryVersionEntity.java
index 210de62..d3705f3 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RepositoryVersionEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RepositoryVersionEntity.java
@@ -17,8 +17,6 @@
  */
 package org.apache.ambari.server.orm.entities;
 
-import static java.util.Arrays.asList;
-
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceComponentDesiredStateEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceComponentDesiredStateEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceComponentDesiredStateEntity.java
index 9b93517..17fd323 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceComponentDesiredStateEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceComponentDesiredStateEntity.java
@@ -40,6 +40,7 @@ import javax.persistence.Table;
 import javax.persistence.TableGenerator;
 import javax.persistence.UniqueConstraint;
 
+import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.State;
 
 @Entity
@@ -84,6 +85,10 @@ public class ServiceComponentDesiredStateEntity {
   @Column(name = "recovery_enabled", nullable = false, insertable = true, updatable = true)
   private Integer recoveryEnabled = 0;
 
+  @Column(name = "repo_state", nullable = false, insertable = true, updatable = true)
+  @Enumerated(EnumType.STRING)
+  private RepositoryVersionState repoState = RepositoryVersionState.INIT;
+
   /**
    * Unidirectional one-to-one association to {@link StackEntity}
    */
@@ -297,4 +302,18 @@ public class ServiceComponentDesiredStateEntity {
     this.hostComponentDesiredStateEntities = hostComponentDesiredStateEntities;
   }
 
+  /**
+   * @param state the repository state for {@link #getDesiredVersion()}
+   */
+  public void setRepositoryState(RepositoryVersionState state) {
+    repoState = state;
+  }
+
+  /**
+   * @return the state of the repository for {@link #getDesiredVersion()}
+   */
+  public RepositoryVersionState getRepositoryState() {
+    return repoState;
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceComponentVersionEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceComponentVersionEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceComponentVersionEntity.java
index 5085d18..f0b9660 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceComponentVersionEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceComponentVersionEntity.java
@@ -49,9 +49,22 @@ import org.apache.ambari.server.state.RepositoryVersionState;
     valueColumnName = "sequence_value",
     pkColumnValue = "servicecomponent_version_id_seq",
     initialValue = 0)
-@NamedQueries({ @NamedQuery(
+@NamedQueries({
+  @NamedQuery(
     name = "ServiceComponentVersionEntity.findByComponent",
-    query = "SELECT version FROM ServiceComponentVersionEntity version WHERE version.m_serviceComponentDesiredStateEntity.clusterId = :clusterId AND version.m_serviceComponentDesiredStateEntity.serviceName = :serviceName AND version.m_serviceComponentDesiredStateEntity.componentName = :componentName") })
+    query = "SELECT version FROM ServiceComponentVersionEntity version WHERE " +
+      "version.m_serviceComponentDesiredStateEntity.clusterId = :clusterId AND " +
+      "version.m_serviceComponentDesiredStateEntity.serviceName = :serviceName AND " +
+      "version.m_serviceComponentDesiredStateEntity.componentName = :componentName"),
+  @NamedQuery(
+    name = "ServiceComponentVersionEntity.findByComponentAndVersion",
+    query = "SELECT version FROM ServiceComponentVersionEntity version WHERE " +
+        "version.m_serviceComponentDesiredStateEntity.clusterId = :clusterId AND " +
+        "version.m_serviceComponentDesiredStateEntity.serviceName = :serviceName AND " +
+        "version.m_serviceComponentDesiredStateEntity.componentName = :componentName AND " +
+        "version.m_repositoryVersion.version = :repoVersion")
+})
+
 public class ServiceComponentVersionEntity {
 
   @Id
@@ -66,7 +79,7 @@ public class ServiceComponentVersionEntity {
   private ServiceComponentDesiredStateEntity m_serviceComponentDesiredStateEntity;
 
   @ManyToOne
-  @JoinColumn(name = "repo_version_id", referencedColumnName = "repo_version_id", nullable = false)
+  @JoinColumn(name  = "repo_version_id", referencedColumnName = "repo_version_id", nullable = false)
   private RepositoryVersionEntity m_repositoryVersion;
 
   @Column(name = "state", nullable = false, insertable = true, updatable = true)
@@ -99,6 +112,13 @@ public class ServiceComponentVersionEntity {
   }
 
   /**
+   * @return the repository
+   */
+  public RepositoryVersionEntity getRepositoryVersion() {
+    return m_repositoryVersion;
+  }
+
+  /**
    * @return the id
    */
   public long getId() {

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponent.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponent.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponent.java
index e93ab9a..b5b6821 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponent.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponent.java
@@ -97,4 +97,19 @@ public interface ServiceComponent {
       String hostName) throws AmbariException;
 
   void delete() throws AmbariException;
+
+  /**
+   * This method computes the state of the repository that's associated with the desired
+   * version.  It is used, for example, when a host component reports its version and the
+   * state can be in flux.
+   *
+   * @param reportedVersion
+   * @throws AmbariException
+   */
+  void updateRepositoryState(String reportedVersion) throws AmbariException;
+
+  /**
+   * @return the repository state for the desired version
+   */
+  RepositoryVersionState getRepositoryState();
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentImpl.java
index 236091b..122f57c 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentImpl.java
@@ -19,6 +19,7 @@
 package org.apache.ambari.server.state;
 
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.concurrent.ConcurrentHashMap;
@@ -32,21 +33,31 @@ import org.apache.ambari.server.ServiceComponentHostNotFoundException;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.controller.ServiceComponentResponse;
 import org.apache.ambari.server.events.ServiceComponentRecoveryChangedEvent;
+import org.apache.ambari.server.events.listeners.upgrade.StackVersionListener;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
 import org.apache.ambari.server.orm.dao.ClusterServiceDAO;
 import org.apache.ambari.server.orm.dao.HostComponentDesiredStateDAO;
+import org.apache.ambari.server.orm.dao.HostComponentStateDAO;
+import org.apache.ambari.server.orm.dao.RepositoryVersionDAO;
 import org.apache.ambari.server.orm.dao.ServiceComponentDesiredStateDAO;
 import org.apache.ambari.server.orm.dao.StackDAO;
 import org.apache.ambari.server.orm.entities.ClusterServiceEntity;
 import org.apache.ambari.server.orm.entities.ClusterServiceEntityPK;
 import org.apache.ambari.server.orm.entities.HostComponentDesiredStateEntity;
 import org.apache.ambari.server.orm.entities.HostComponentStateEntity;
+import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
 import org.apache.ambari.server.orm.entities.ServiceComponentDesiredStateEntity;
+import org.apache.ambari.server.orm.entities.ServiceComponentVersionEntity;
 import org.apache.ambari.server.orm.entities.StackEntity;
 import org.apache.ambari.server.state.cluster.ClusterImpl;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Function;
+import com.google.common.collect.Maps;
+import com.google.inject.Inject;
 import com.google.inject.ProvisionException;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
@@ -86,6 +97,12 @@ public class ServiceComponentImpl implements ServiceComponent {
    */
   private final StackDAO stackDAO;
 
+  @Inject
+  private RepositoryVersionDAO repoVersionDAO;
+
+  @Inject
+  private HostComponentStateDAO hostComponentDAO;
+
   @AssistedInject
   public ServiceComponentImpl(@Assisted Service service, @Assisted String componentName,
       AmbariMetaInfo ambariMetaInfo,
@@ -121,6 +138,7 @@ public class ServiceComponentImpl implements ServiceComponent {
     desiredStateEntityId = desiredStateEntity.getId();
   }
 
+  @Override
   public void updateComponentInfo() throws AmbariException {
     StackId stackId = service.getDesiredStackVersion();
     try {
@@ -427,7 +445,8 @@ public class ServiceComponentImpl implements ServiceComponent {
     ServiceComponentResponse r = new ServiceComponentResponse(getClusterId(),
         cluster.getClusterName(), service.getName(), getName(),
         getDesiredStackVersion().getStackId(), getDesiredState().toString(),
-        getServiceComponentStateCount(), isRecoveryEnabled(), displayName);
+        getServiceComponentStateCount(), isRecoveryEnabled(), displayName,
+        getDesiredVersion(), getRepositoryState());
     return r;
   }
 
@@ -571,6 +590,151 @@ public class ServiceComponentImpl implements ServiceComponent {
   }
 
 
+  /**
+   * Follows this version logic:
+   * <table border="1">
+   *   <tr>
+   *     <th>DB hostcomponent1</th>
+   *     <th>DB hostcomponentN</th>
+   *     <th>DB desired</th>
+   *     <th>New desired</th>
+   *     <th>Repo State</th>
+   *   </tr>
+   *   <tr>
+   *     <td>v1</td>
+   *     <td>v1</td>
+   *     <td>UNKNOWN</td>
+   *     <td>v1</td>
+   *     <td>CURRENT</td>
+   *   </tr>
+   *   <tr>
+   *     <td>v1</td>
+   *     <td>v2</td>
+   *     <td>UNKNOWN</td>
+   *     <td>UNKNOWN</td>
+   *     <td>OUT_OF_SYNC</td>
+   *   </tr>
+   *   <tr>
+   *     <td>v1</td>
+   *     <td>v2</td>
+   *     <td>v2</td>
+   *     <td>v2 (no change)</td>
+   *     <td>OUT_OF_SYNC</td>
+   *   </tr>
+   *   <tr>
+   *     <td>v2</td>
+   *     <td>v2</td>
+   *     <td>v1</td>
+   *     <td>v1 (no change)</td>
+   *     <td>OUT_OF_SYNC</td>
+   *   </tr>
+   *   <tr>
+   *     <td>v2</td>
+   *     <td>v2</td>
+   *     <td>v2</td>
+   *     <td>v2 (no change)</td>
+   *     <td>CURRENT</td>
+   *   </tr>
+   * </table>
+   */
+  @Override
+  @Transactional
+  public void updateRepositoryState(String reportedVersion) throws AmbariException {
+
+    ServiceComponentDesiredStateEntity component = serviceComponentDesiredStateDAO.findById(
+        desiredStateEntityId);
+
+    List<ServiceComponentVersionEntity> componentVersions = serviceComponentDesiredStateDAO.findVersions(
+        getClusterId(), getServiceName(), getName());
+
+    // per component, this list should be small, so iterating here isn't a big deal
+    Map<String, ServiceComponentVersionEntity> map = new HashMap<>(Maps.uniqueIndex(componentVersions,
+        new Function<ServiceComponentVersionEntity, String>() {
+          @Override
+          public String apply(ServiceComponentVersionEntity input) {
+            return input.getRepositoryVersion().getVersion();
+          }
+      }));
+
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Existing versions for {}/{}/{}: {}",
+          getClusterName(), getServiceName(), getName(), map.keySet());
+    }
+
+    ServiceComponentVersionEntity componentVersion = map.get(reportedVersion);
+
+    if (null == componentVersion) {
+      RepositoryVersionEntity repoVersion = repoVersionDAO.findByStackAndVersion(
+          getDesiredStackVersion(), reportedVersion);
+
+      if (null != repoVersion) {
+        componentVersion = new ServiceComponentVersionEntity();
+        componentVersion.setRepositoryVersion(repoVersion);
+        componentVersion.setState(RepositoryVersionState.INSTALLED);
+        componentVersion.setUserName("auto-reported");
+
+        // since we've never seen this version before, mark the component as CURRENT
+        component.setRepositoryState(RepositoryVersionState.CURRENT);
+        component.addVersion(componentVersion);
+
+        component = serviceComponentDesiredStateDAO.merge(component);
+
+        map.put(reportedVersion, componentVersion);
+
+      } else {
+        LOG.warn("There is no repository available for stack {}, version {}",
+            getDesiredStackVersion(), reportedVersion);
+      }
+    }
+
+    if (MapUtils.isNotEmpty(map)) {
+      String desiredVersion = component.getDesiredVersion();
+
+      List<HostComponentStateEntity> hostComponents = hostComponentDAO.findByServiceAndComponentAndNotVersion(
+          component.getServiceName(), component.getComponentName(), reportedVersion);
+
+      LOG.debug("{}/{} reportedVersion={}, desiredVersion={}, non-matching desired count={}, repo_state={}",
+          component.getServiceName(), component.getComponentName(), reportedVersion,
+          desiredVersion, hostComponents.size(), component.getRepositoryState());
+
+      // !!! if we are unknown, that means it's never been set.  Try to determine it.
+      if (StackVersionListener.UNKNOWN_VERSION.equals(desiredVersion)) {
+        if (CollectionUtils.isEmpty(hostComponents)) {
+          // all host components are the same version as reported
+          component.setDesiredVersion(reportedVersion);
+          component.setRepositoryState(RepositoryVersionState.CURRENT);
+        } else {
+          // desired is UNKNOWN and there's a mix of versions in the host components
+          component.setRepositoryState(RepositoryVersionState.OUT_OF_SYNC);
+        }
+      } else {
+        if (!reportedVersion.equals(desiredVersion)) {
+          component.setRepositoryState(RepositoryVersionState.OUT_OF_SYNC);
+        } else if (CollectionUtils.isEmpty(hostComponents)) {
+          component.setRepositoryState(RepositoryVersionState.CURRENT);
+        }
+      }
+
+      component = serviceComponentDesiredStateDAO.merge(component);
+    }
+  }
+
+  @Override
+  public RepositoryVersionState getRepositoryState() {
+    ServiceComponentDesiredStateEntity component = serviceComponentDesiredStateDAO.findById(
+        desiredStateEntityId);
+
+    if (null != component) {
+      return component.getRepositoryState();
+    } else {
+      LOG.warn("Cannot retrieve repository state on component that may have been deleted: service {}, component {}",
+          service != null ? service.getName() : null, componentName);
+
+      return null;
+    }
+  }
+
+
   private int getSCHCountByState(State state) {
     int count = 0;
     for (ServiceComponentHost sch : hostComponents.values()) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java
index 7d35d2e..35c5efb 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java
@@ -46,9 +46,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Objects;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonPrimitive;
 
 /**
  * Used to represent cluster-based operations.

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog300.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog300.java b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog300.java
index d6bbbac..3190390 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog300.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog300.java
@@ -19,8 +19,6 @@ package org.apache.ambari.server.upgrade;
 
 
 import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Map;
 
 import org.apache.ambari.server.AmbariException;
@@ -30,6 +28,7 @@ import org.apache.ambari.server.orm.dao.DaoUtils;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Config;
+import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -87,6 +86,7 @@ public class UpgradeCatalog300 extends AbstractUpgradeCatalog {
    */
   @Override
   protected void executeDDLUpdates() throws AmbariException, SQLException {
+    addServiceComponentColumn();
   }
 
   /**
@@ -125,4 +125,15 @@ public class UpgradeCatalog300 extends AbstractUpgradeCatalog {
     }
 
   }
+
+  /**
+   * Updates the {@code servicecomponentdesiredstate} table.
+   *
+   * @throws SQLException
+   */
+  protected void addServiceComponentColumn() throws SQLException {
+    dbAccessor.addColumn(UpgradeCatalog250.COMPONENT_TABLE,
+        new DBColumnInfo("repo_state", String.class, 255, RepositoryVersionState.INIT.name(), false));
+
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql
index b79c945..a64a861 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql
@@ -191,6 +191,7 @@ CREATE TABLE servicecomponentdesiredstate (
   desired_version VARCHAR(255) NOT NULL DEFAULT 'UNKNOWN',
   service_name VARCHAR(255) NOT NULL,
   recovery_enabled SMALLINT NOT NULL DEFAULT 0,
+  repo_state VARCHAR(255) NOT NULL DEFAULT 'INIT',
   CONSTRAINT pk_sc_desiredstate PRIMARY KEY (id),
   CONSTRAINT UQ_scdesiredstate_name UNIQUE(component_name, service_name, cluster_id),
   CONSTRAINT FK_scds_desired_stack_id FOREIGN KEY (desired_stack_id) REFERENCES stack(stack_id),

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
index 1c502bc..55ec5ed 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
@@ -201,6 +201,7 @@ CREATE TABLE servicecomponentdesiredstate (
   desired_state VARCHAR(255) NOT NULL,
   service_name VARCHAR(100) NOT NULL,
   recovery_enabled SMALLINT NOT NULL DEFAULT 0,
+  repo_state VARCHAR(255) NOT NULL DEFAULT 'INIT',
   CONSTRAINT pk_sc_desiredstate PRIMARY KEY (id),
   CONSTRAINT UQ_scdesiredstate_name UNIQUE(component_name, service_name, cluster_id),
   CONSTRAINT FK_scds_desired_stack_id FOREIGN KEY (desired_stack_id) REFERENCES stack(stack_id),

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
index c6d4ad0..a4a87aa 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
@@ -182,6 +182,7 @@ CREATE TABLE servicecomponentdesiredstate (
   desired_version VARCHAR(255) DEFAULT 'UNKNOWN' NOT NULL,
   service_name VARCHAR2(255) NOT NULL,
   recovery_enabled SMALLINT DEFAULT 0 NOT NULL,
+  repo_state VARCHAR2(255) DEFAULT 'INIT' NOT NULL,
   CONSTRAINT pk_sc_desiredstate PRIMARY KEY (id),
   CONSTRAINT UQ_scdesiredstate_name UNIQUE(component_name, service_name, cluster_id),
   CONSTRAINT FK_scds_desired_stack_id FOREIGN KEY (desired_stack_id) REFERENCES stack(stack_id),

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
index 1be87bb..36587c7 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
@@ -191,6 +191,7 @@ CREATE TABLE servicecomponentdesiredstate (
   desired_state VARCHAR(255) NOT NULL,
   service_name VARCHAR(255) NOT NULL,
   recovery_enabled SMALLINT NOT NULL DEFAULT 0,
+  repo_state VARCHAR(255) NOT NULL DEFAULT 'INIT',
   CONSTRAINT pk_sc_desiredstate PRIMARY KEY (id),
   CONSTRAINT UQ_scdesiredstate_name UNIQUE(component_name, service_name, cluster_id),
   CONSTRAINT FK_scds_desired_stack_id FOREIGN KEY (desired_stack_id) REFERENCES stack(stack_id),

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
index abe48e8..5f7d98d 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
@@ -180,6 +180,7 @@ CREATE TABLE servicecomponentdesiredstate (
   desired_state VARCHAR(255) NOT NULL,
   service_name VARCHAR(255) NOT NULL,
   recovery_enabled SMALLINT NOT NULL DEFAULT 0,
+  repo_state VARCHAR(255) NOT NULL DEFAULT 'INIT',
   CONSTRAINT pk_sc_desiredstate PRIMARY KEY (id),
   CONSTRAINT UQ_scdesiredstate_name UNIQUE(component_name, service_name, cluster_id),
   CONSTRAINT FK_scds_desired_stack_id FOREIGN KEY (desired_stack_id) REFERENCES stack(stack_id),

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
index 169a464..7fbe93b 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
@@ -204,6 +204,7 @@ CREATE TABLE servicecomponentdesiredstate (
   desired_state VARCHAR(255) NOT NULL,
   service_name VARCHAR(255) NOT NULL,
   recovery_enabled SMALLINT NOT NULL DEFAULT 0,
+  repo_state VARCHAR(255) NOT NULL DEFAULT 'INIT',
   CONSTRAINT pk_sc_desiredstate PRIMARY KEY (id),
   CONSTRAINT UQ_scdesiredstate_name UNIQUE(component_name, service_name, cluster_id),
   CONSTRAINT FK_scds_desired_stack_id FOREIGN KEY (desired_stack_id) REFERENCES stack(stack_id),

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/main/resources/properties.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/properties.json b/ambari-server/src/main/resources/properties.json
index b7e0988..fc74381 100644
--- a/ambari-server/src/main/resources/properties.json
+++ b/ambari-server/src/main/resources/properties.json
@@ -59,8 +59,8 @@
         "ServiceComponentInfo/cluster_name",
         "ServiceComponentInfo/display_name",
         "ServiceComponentInfo/state",
-        "ServiceComponents/display_name",
-        "ServiceComponents/description",
+        "ServiceComponentInfo/display_name",
+        "ServiceComponentInfo/description",
         "ServiceComponentInfo/category",
         "ServiceComponentInfo/total_count",
         "ServiceComponentInfo/started_count",
@@ -69,6 +69,8 @@
         "ServiceComponentInfo/init_count",
         "ServiceComponentInfo/unknown_count",
         "ServiceComponentInfo/recovery_enabled",
+        "ServiceComponentInfo/desired_version",
+        "ServiceComponentInfo/repository_state",
         "params/run_smoke_test",
         "_"
     ],

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/test/java/org/apache/ambari/server/checks/AbstractCheckDescriptorTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/checks/AbstractCheckDescriptorTest.java b/ambari-server/src/test/java/org/apache/ambari/server/checks/AbstractCheckDescriptorTest.java
index 7758924..be9ab96 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/checks/AbstractCheckDescriptorTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/checks/AbstractCheckDescriptorTest.java
@@ -49,8 +49,6 @@ import org.junit.Test;
 
 import com.google.inject.Provider;
 
-import junit.framework.Assert;
-
 /**
  * Unit tests for AbstractCheckDescriptor
  */

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ComponentResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ComponentResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ComponentResourceProviderTest.java
index 3529bef..3fd38f9 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ComponentResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ComponentResourceProviderTest.java
@@ -68,6 +68,7 @@ import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.ComponentInfo;
 import org.apache.ambari.server.state.MaintenanceState;
+import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.Service;
 import org.apache.ambari.server.state.ServiceComponent;
 import org.apache.ambari.server.state.ServiceComponentFactory;
@@ -243,13 +244,13 @@ public class ComponentResourceProviderTest {
 
     expect(serviceComponent1.convertToResponse()).andReturn(
       new ServiceComponentResponse(100L, "Cluster100", "Service100", "Component100", null, "", serviceComponentStateCountMap,
-              true /* recovery enabled */, "Component100 Client"));
+              true /* recovery enabled */, "Component100 Client", null, null));
     expect(serviceComponent2.convertToResponse()).andReturn(
       new ServiceComponentResponse(100L, "Cluster100", "Service100", "Component101", null, "", serviceComponentStateCountMap,
-              false /* recovery not enabled */, "Component101 Client"));
+              false /* recovery not enabled */, "Component101 Client", null, null));
     expect(serviceComponent3.convertToResponse()).andReturn(
       new ServiceComponentResponse(100L, "Cluster100", "Service100", "Component102", null, "", serviceComponentStateCountMap,
-              true /* recovery enabled */, "Component102 Client"));
+              true /* recovery enabled */, "Component102 Client", "1.1", RepositoryVersionState.CURRENT));
 
     expect(ambariMetaInfo.getComponent(null, null, null, "Component100")).andReturn(componentInfo1);
     expect(ambariMetaInfo.getComponent(null, null, null, "Component101")).andReturn(componentInfo2);
@@ -282,6 +283,8 @@ public class ComponentResourceProviderTest {
     propertyIds.add(ComponentResourceProvider.COMPONENT_INIT_COUNT_PROPERTY_ID);
     propertyIds.add(ComponentResourceProvider.COMPONENT_UNKNOWN_COUNT_PROPERTY_ID);
     propertyIds.add(ComponentResourceProvider.COMPONENT_RECOVERY_ENABLED_ID);
+    propertyIds.add(ComponentResourceProvider.COMPONENT_DESIRED_VERSION);
+    propertyIds.add(ComponentResourceProvider.COMPONENT_REPOSITORY_STATE);
 
     Predicate predicate = new PredicateBuilder()
       .property(ComponentResourceProvider.COMPONENT_CLUSTER_NAME_PROPERTY_ID)
@@ -314,6 +317,17 @@ public class ComponentResourceProviderTest {
           ComponentResourceProvider.COMPONENT_UNKNOWN_COUNT_PROPERTY_ID));
       Assert.assertEquals(String.valueOf(true), resource.getPropertyValue(
         ComponentResourceProvider.COMPONENT_RECOVERY_ENABLED_ID));
+
+      if (resource.getPropertyValue(
+          ComponentResourceProvider.COMPONENT_COMPONENT_NAME_PROPERTY_ID).equals("Component102")) {
+        Assert.assertNotNull(resource.getPropertyValue(ComponentResourceProvider.COMPONENT_REPOSITORY_STATE));
+        Assert.assertNotNull(resource.getPropertyValue(ComponentResourceProvider.COMPONENT_DESIRED_VERSION));
+        Assert.assertEquals(RepositoryVersionState.CURRENT, resource.getPropertyValue(ComponentResourceProvider.COMPONENT_REPOSITORY_STATE));
+        Assert.assertEquals("1.1", resource.getPropertyValue(ComponentResourceProvider.COMPONENT_DESIRED_VERSION));
+      } else {
+        Assert.assertNull(resource.getPropertyValue(ComponentResourceProvider.COMPONENT_REPOSITORY_STATE));
+        Assert.assertNull(resource.getPropertyValue(ComponentResourceProvider.COMPONENT_DESIRED_VERSION));
+      }
     }
 
     // verify
@@ -405,13 +419,13 @@ public class ComponentResourceProviderTest {
 
     expect(serviceComponent1.convertToResponse()).andReturn(
       new ServiceComponentResponse(100L, "Cluster100", "Service100", "Component101", null, "", serviceComponentStateCountMap,
-              false /* recovery not enabled */, "Component101 Client"));
+              false /* recovery not enabled */, "Component101 Client", null, null));
     expect(serviceComponent2.convertToResponse()).andReturn(
       new ServiceComponentResponse(100L, "Cluster100", "Service100", "Component102", null, "", serviceComponentStateCountMap,
-              false /* recovery not enabled */, "Component102 Client"));
+              false /* recovery not enabled */, "Component102 Client", null, null));
     expect(serviceComponent3.convertToResponse()).andReturn(
       new ServiceComponentResponse(100L, "Cluster100", "Service100", "Component103", null, "", serviceComponentStateCountMap,
-              false /* recovery not enabled */, "Component103 Client"));
+              false /* recovery not enabled */, "Component103 Client", null, null));
     expect(serviceComponent1.getDesiredState()).andReturn(State.INSTALLED).anyTimes();
     expect(serviceComponent2.getDesiredState()).andReturn(State.INSTALLED).anyTimes();
     expect(serviceComponent3.getDesiredState()).andReturn(State.INSTALLED).anyTimes();
@@ -721,7 +735,7 @@ public class ComponentResourceProviderTest {
 
     expect(serviceComponent1.convertToResponse()).andReturn(
         new ServiceComponentResponse(100L, "Cluster100", "Service100", "Component101", null, "", serviceComponentStateCountMap,
-            false /* recovery not enabled */, "Component101 Client"));
+            false /* recovery not enabled */, "Component101 Client", null, null));
     expect(serviceComponent1.getDesiredState()).andReturn(State.INSTALLED).anyTimes();
 
     expect(serviceComponentHost.getState()).andReturn(State.INSTALLED).anyTimes();

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RequestImplTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RequestImplTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RequestImplTest.java
index fb508ea..4aacf91 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RequestImplTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RequestImplTest.java
@@ -128,8 +128,8 @@ public class RequestImplTest {
     Assert.assertTrue(validPropertyIds.contains("ServiceComponentInfo/component_name"));
     Assert.assertTrue(validPropertyIds.contains("ServiceComponentInfo/cluster_name"));
     Assert.assertTrue(validPropertyIds.contains("ServiceComponentInfo/state"));
-    Assert.assertTrue(validPropertyIds.contains("ServiceComponents/display_name"));
-    Assert.assertTrue(validPropertyIds.contains("ServiceComponents/description"));
+    Assert.assertTrue(validPropertyIds.contains("ServiceComponentInfo/display_name"));
+    Assert.assertTrue(validPropertyIds.contains("ServiceComponentInfo/description"));
     Assert.assertTrue(validPropertyIds.contains("params/run_smoke_test"));
 
     request = PropertyHelper.getReadRequest(PropertyHelper.getPropertyIds(Resource.Type.Action));

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/test/java/org/apache/ambari/server/orm/OrmTestHelper.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/OrmTestHelper.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/OrmTestHelper.java
index ead0a2d..57af8a6 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/orm/OrmTestHelper.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/OrmTestHelper.java
@@ -622,6 +622,7 @@ public class OrmTestHelper {
         repositoryVersion = repositoryVersionDAO.create(stackEntity, version,
             String.valueOf(System.currentTimeMillis()), "");
       } catch (Exception ex) {
+        ex.printStackTrace();
         Assert.fail(MessageFormat.format("Unable to create Repo Version for Stack {0} and version {1}",
             stackEntity.getStackName() + "-" + stackEntity.getStackVersion(), version));
       }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/test/java/org/apache/ambari/server/state/ServiceComponentTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/ServiceComponentTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/ServiceComponentTest.java
index 0f615ee..a7f8ae3 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/ServiceComponentTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/ServiceComponentTest.java
@@ -29,6 +29,7 @@ import java.util.Map;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.controller.ServiceComponentResponse;
+import org.apache.ambari.server.events.listeners.upgrade.StackVersionListener;
 import org.apache.ambari.server.orm.GuiceJpaInitializer;
 import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
 import org.apache.ambari.server.orm.OrmTestHelper;
@@ -634,6 +635,77 @@ public class ServiceComponentTest {
     assertEquals(0, list.size());
   }
 
+
+  @Test
+  public void testUpdateStates() throws Exception {
+    ServiceComponentDesiredStateDAO serviceComponentDesiredStateDAO = injector.getInstance(
+        ServiceComponentDesiredStateDAO.class);
+
+    String componentName = "NAMENODE";
+
+    ServiceComponent component = serviceComponentFactory.createNew(service, componentName);
+    component.setDesiredStackVersion(new StackId("HDP-2.2.0"));
+    service.addServiceComponent(component);
+
+    ServiceComponent sc = service.getServiceComponent(componentName);
+    Assert.assertNotNull(sc);
+
+    ServiceComponentDesiredStateEntity entity = serviceComponentDesiredStateDAO.findByName(cluster.getClusterId(), serviceName, componentName);
+
+    helper.getOrCreateRepositoryVersion(component.getDesiredStackVersion(), "2.2.0.1");
+    helper.getOrCreateRepositoryVersion(component.getDesiredStackVersion(), "2.2.0.2");
+
+    addHostToCluster("h1", clusterName);
+    addHostToCluster("h2", clusterName);
+
+    sc.setDesiredState(State.INSTALLED);
+    Assert.assertEquals(State.INSTALLED, sc.getDesiredState());
+
+    ServiceComponentHost sch1 = sc.addServiceComponentHost("h1");
+    ServiceComponentHost sch2 = sc.addServiceComponentHost("h2");
+
+    // !!! case 1: component desired is UNKNOWN, mix of h-c versions
+    sc.setDesiredVersion(StackVersionListener.UNKNOWN_VERSION);
+    sch1.setVersion("2.2.0.1");
+    sch2.setVersion("2.2.0.2");
+    sc.updateRepositoryState("2.2.0.2");
+    entity = serviceComponentDesiredStateDAO.findByName(cluster.getClusterId(), serviceName, componentName);
+    assertEquals(RepositoryVersionState.OUT_OF_SYNC, entity.getRepositoryState());
+
+    // !!! case 2: component desired is UNKNOWN, all h-c same version
+    sc.setDesiredVersion(StackVersionListener.UNKNOWN_VERSION);
+    sch1.setVersion("2.2.0.1");
+    sch2.setVersion("2.2.0.1");
+    sc.updateRepositoryState("2.2.0.1");
+    entity = serviceComponentDesiredStateDAO.findByName(cluster.getClusterId(), serviceName, componentName);
+    assertEquals(RepositoryVersionState.CURRENT, entity.getRepositoryState());
+
+    // !!! case 3: component desired is known, any component reports different version
+    sc.setDesiredVersion("2.2.0.1");
+    sch1.setVersion("2.2.0.1");
+    sch2.setVersion("2.2.0.2");
+    sc.updateRepositoryState("2.2.0.2");
+    entity = serviceComponentDesiredStateDAO.findByName(cluster.getClusterId(), serviceName, componentName);
+    assertEquals(RepositoryVersionState.OUT_OF_SYNC, entity.getRepositoryState());
+
+    // !!! case 4: component desired is known, component reports same as desired, mix of h-c versions
+    sc.setDesiredVersion("2.2.0.1");
+    sch1.setVersion("2.2.0.1");
+    sch2.setVersion("2.2.0.2");
+    sc.updateRepositoryState("2.2.0.1");
+    entity = serviceComponentDesiredStateDAO.findByName(cluster.getClusterId(), serviceName, componentName);
+    assertEquals(RepositoryVersionState.OUT_OF_SYNC, entity.getRepositoryState());
+
+    // !!! case 5: component desired is known, component reports same as desired, all h-c the same
+    sc.setDesiredVersion("2.2.0.1");
+    sch1.setVersion("2.2.0.1");
+    sch2.setVersion("2.2.0.1");
+    sc.updateRepositoryState("2.2.0.1");
+    entity = serviceComponentDesiredStateDAO.findByName(cluster.getClusterId(), serviceName, componentName);
+    assertEquals(RepositoryVersionState.CURRENT, entity.getRepositoryState());
+  }
+
+
   /**
    * Creates an upgrade entity, asserting it was created correctly.
    *

http://git-wip-us.apache.org/repos/asf/ambari/blob/cd245c00/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog300Test.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog300Test.java b/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog300Test.java
index 36afa03..f5e0bfe 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog300Test.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog300Test.java
@@ -17,39 +17,13 @@
  */
 package org.apache.ambari.server.upgrade;
 
-import static org.easymock.EasyMock.anyObject;
-import static org.easymock.EasyMock.capture;
 import static org.easymock.EasyMock.createMockBuilder;
-import static org.easymock.EasyMock.createNiceMock;
-import static org.easymock.EasyMock.createStrictMock;
-import static org.easymock.EasyMock.eq;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.newCapture;
 import static org.easymock.EasyMock.replay;
 import static org.easymock.EasyMock.verify;
 
 import java.lang.reflect.Method;
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.Statement;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.persistence.EntityManager;
-
-import org.apache.ambari.server.configuration.Configuration;
-import org.apache.ambari.server.orm.DBAccessor;
-import org.apache.ambari.server.state.stack.OsFamily;
-import org.easymock.Capture;
-import org.junit.Assert;
-import org.junit.Test;
-import org.springframework.security.crypto.password.PasswordEncoder;
 
-import com.google.inject.Binder;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.google.inject.Module;
+import org.junit.Test;
 
 public class UpgradeCatalog300Test {
 
@@ -67,7 +41,6 @@ public class UpgradeCatalog300Test {
     upgradeCatalog300.addNewConfigurationsFromXml();
     upgradeCatalog300.showHcatDeletedUserMessage();
 
-
     replay(upgradeCatalog300);
 
     upgradeCatalog300.executeDMLUpdates();
@@ -75,4 +48,23 @@ public class UpgradeCatalog300Test {
     verify(upgradeCatalog300);
   }
 
+  @Test
+  public void testExecuteDDLUpdates() throws Exception {
+
+    Method addServiceComponentColumn = UpgradeCatalog300.class
+        .getDeclaredMethod("addServiceComponentColumn");
+
+    UpgradeCatalog300 upgradeCatalog300 = createMockBuilder(UpgradeCatalog300.class)
+        .addMockedMethod(addServiceComponentColumn)
+        .createMock();
+
+    upgradeCatalog300.addServiceComponentColumn();
+
+    replay(upgradeCatalog300);
+
+    upgradeCatalog300.executeDDLUpdates();
+
+    verify(upgradeCatalog300);
+  }
+
 }