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 2016/02/01 22:33:57 UTC

ambari git commit: AMBARI-14867. Use entities for parent/child repository versions (ncole)

Repository: ambari
Updated Branches:
  refs/heads/branch-dev-patch-upgrade 73aee31ef -> c84da2493


AMBARI-14867. Use entities for parent/child repository versions (ncole)


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

Branch: refs/heads/branch-dev-patch-upgrade
Commit: c84da24939b691951244840e07526a0ed9dee30a
Parents: 73aee31
Author: Nate Cole <nc...@hortonworks.com>
Authored: Mon Feb 1 12:46:26 2016 -0500
Committer: Nate Cole <nc...@hortonworks.com>
Committed: Mon Feb 1 16:33:19 2016 -0500

----------------------------------------------------------------------
 .../server/configuration/Configuration.java     |  83 +++++++---
 .../RepositoryVersionResourceProvider.java      |  14 ++
 .../VersionDefinitionResourceProvider.java      | 149 ++++++++++++++---
 .../orm/entities/RepositoryVersionEntity.java   |  30 ++++
 .../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 +
 .../Ambari-DDL-Postgres-EMBEDDED-CREATE.sql     |   1 +
 .../resources/Ambari-DDL-SQLAnywhere-CREATE.sql |   1 +
 .../resources/Ambari-DDL-SQLServer-CREATE.sql   |   1 +
 .../RepositoryVersionResourceProviderTest.java  |  60 -------
 .../VersionDefinitionResourceProviderTest.java  | 160 +++++++++++++++++++
 .../version_definition_resource_provider.xml    |  13 +-
 14 files changed, 398 insertions(+), 118 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/c84da249/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
index d1453a0..55eea7a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
@@ -17,11 +17,20 @@
  */
 package org.apache.ambari.server.configuration;
 
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.cert.CertificateException;
+import java.security.interfaces.RSAPublicKey;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
 
 import org.apache.ambari.annotations.Experimental;
 import org.apache.ambari.annotations.ExperimentalFeature;
@@ -34,32 +43,23 @@ import org.apache.ambari.server.orm.entities.StageEntity;
 import org.apache.ambari.server.security.ClientSecurityType;
 import org.apache.ambari.server.security.authorization.LdapServerProperties;
 import org.apache.ambari.server.security.authorization.jwt.JwtAuthenticationProperties;
+import org.apache.ambari.server.security.encryption.CertificateUtils;
 import org.apache.ambari.server.security.encryption.CredentialProvider;
 import org.apache.ambari.server.state.stack.OsFamily;
-import org.apache.ambari.server.security.encryption.CertificateUtils;
 import org.apache.ambari.server.utils.Parallel;
 import org.apache.ambari.server.utils.ShellCommandUtil;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang.RandomStringUtils;
 import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.math.NumberUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Properties;
-
-import java.security.cert.CertificateException;
-import java.security.interfaces.RSAPublicKey;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
 
 
 /**
@@ -545,6 +545,26 @@ public class Configuration {
   public static final String VIEWS_HTTP_X_XSS_PROTECTION_HEADER_VALUE_KEY = "views.http.x-xss-protection";
   public static final String VIEWS_HTTP_X_XSS_PROTECTION_HEADER_VALUE_DEFAULT = "1; mode=block";
 
+  /*
+   * Version Definition URL
+   */
+  /**
+   * The connection timeout for reading version definitions.
+   */
+  private static final String VERSION_DEFINITION_CONNECT_TIMEOUT = "server.version_definition.connect.timeout.millis";
+  /**
+   * Default connect timeout for reading version definitions.
+   */
+  private static final int VERSION_DEFINITION_CONNECT_TIMEOUT_DEFAULT = 5000;
+  /**
+   * The read timeout for reading version definitions.
+   */
+  private static final String VERSION_DEFINITION_READ_TIMEOUT = "server.version_definition.read.timeout.millis";
+  /**
+   * Default read timeout for reading version definitions.
+   */
+  private static final int VERSION_DEFINITION_READ_TIMEOUT_DEFAULT = 5000;
+
   private static final Logger LOG = LoggerFactory.getLogger(
       Configuration.class);
 
@@ -1929,7 +1949,7 @@ public class Configuration {
 
   /**
    * Get property-providers' thread pool core size.
-   * 
+   *
    * @return the property-providers' thread pool core size
    */
   public int getPropertyProvidersThreadPoolCoreSize() {
@@ -1939,7 +1959,7 @@ public class Configuration {
 
   /**
    * Get property-providers' thread pool max size.
-   * 
+   *
    * @return the property-providers' thread pool max size
    */
   public int getPropertyProvidersThreadPoolMaxSize() {
@@ -2472,4 +2492,21 @@ public class Configuration {
     }
     return attempts;
   }
+
+  /**
+   * @return the connect timeout used when loading a version definition URL.
+   */
+  public int getVersionDefinitionConnectTimeout() {
+    return NumberUtils.toInt(
+        properties.getProperty(VERSION_DEFINITION_CONNECT_TIMEOUT),
+            VERSION_DEFINITION_CONNECT_TIMEOUT_DEFAULT);
+  }
+  /**
+   * @return the read timeout used when loading a version definition URL
+   */
+  public int getVersionDefinitionReadTimeout() {
+    return NumberUtils.toInt(
+        properties.getProperty(VERSION_DEFINITION_READ_TIMEOUT),
+            VERSION_DEFINITION_READ_TIMEOUT_DEFAULT);
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/c84da249/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RepositoryVersionResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RepositoryVersionResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RepositoryVersionResourceProvider.java
index 873733d..87731c4 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RepositoryVersionResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RepositoryVersionResourceProvider.java
@@ -96,6 +96,9 @@ public class RepositoryVersionResourceProvider extends AbstractAuthorizedResourc
   public static final String REPOSITORY_VERSION_RELEASE_COMPATIBLE_WITH        = "RepositoryVersions/release/compatible_with";
   public static final String REPOSITORY_VERSION_AVAILABLE_SERVICES             = "RepositoryVersions/services";
 
+  public static final String REPOSITORY_VERSION_PARENT_ID                      = "RepositoryVersions/parent_id";
+  public static final String REPOSITORY_VERSION_HAS_CHILDREN                   = "RepositoryVersions/has_children";
+
   @SuppressWarnings("serial")
   private static Set<String> pkPropertyIds = new HashSet<String>() {
     {
@@ -116,6 +119,8 @@ public class RepositoryVersionResourceProvider extends AbstractAuthorizedResourc
       REPOSITORY_VERSION_RELEASE_COMPATIBLE_WITH,
       REPOSITORY_VERSION_RELEASE_NOTES,
       REPOSITORY_VERSION_RELEASE_VERSION,
+      REPOSITORY_VERSION_PARENT_ID,
+      REPOSITORY_VERSION_HAS_CHILDREN,
       REPOSITORY_VERSION_AVAILABLE_SERVICES);
 
   @SuppressWarnings("serial")
@@ -157,6 +162,7 @@ public class RepositoryVersionResourceProvider extends AbstractAuthorizedResourc
    */
   public RepositoryVersionResourceProvider() {
     super(propertyIds, keyPropertyIds);
+
     setRequiredCreateAuthorizations(EnumSet.of(RoleAuthorization.AMBARI_MANAGE_STACK_VERSIONS));
     setRequiredDeleteAuthorizations(EnumSet.of(RoleAuthorization.AMBARI_MANAGE_STACK_VERSIONS));
     setRequiredUpdateAuthorizations(EnumSet.of(RoleAuthorization.AMBARI_MANAGE_STACK_VERSIONS, RoleAuthorization.AMBARI_EDIT_STACK_REPOS));
@@ -226,6 +232,7 @@ public class RepositoryVersionResourceProvider extends AbstractAuthorizedResourc
     final Set<String> requestedIds = getRequestPropertyIds(request, predicate);
     final Set<Map<String, Object>> propertyMaps = getPropertyMaps(predicate);
 
+
     List<RepositoryVersionEntity> requestedEntities = new ArrayList<RepositoryVersionEntity>();
     for (Map<String, Object> propertyMap: propertyMaps) {
       final StackId stackId = getStackInformationFromUrl(propertyMap);
@@ -240,6 +247,7 @@ public class RepositoryVersionResourceProvider extends AbstractAuthorizedResourc
           throw new SystemException("Repository version should have numerical id");
         }
         final RepositoryVersionEntity entity = repositoryVersionDAO.findByPK(id);
+
         if (entity == null) {
           throw new NoSuchResourceException("There is no repository version with id " + id);
         } else {
@@ -258,6 +266,12 @@ public class RepositoryVersionResourceProvider extends AbstractAuthorizedResourc
       setResourceProperty(resource, REPOSITORY_VERSION_REPOSITORY_VERSION_PROPERTY_ID, entity.getVersion(), requestedIds);
       setResourceProperty(resource, REPOSITORY_VERSION_TYPE_PROPERTY_ID, entity.getType(), requestedIds);
 
+      setResourceProperty(resource, REPOSITORY_VERSION_PARENT_ID, entity.getParentId(), requestedIds);
+
+      List<RepositoryVersionEntity> children = entity.getChildren();
+      setResourceProperty(resource, REPOSITORY_VERSION_HAS_CHILDREN,
+          null != children && !children.isEmpty(), requestedIds);
+
       final VersionDefinitionXml xml;
 
       try {

http://git-wip-us.apache.org/repos/asf/ambari/blob/c84da249/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/VersionDefinitionResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/VersionDefinitionResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/VersionDefinitionResourceProvider.java
index 4b0d3cc..3533293 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/VersionDefinitionResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/VersionDefinitionResourceProvider.java
@@ -17,7 +17,9 @@
  */
 package org.apache.ambari.server.controller.internal;
 
-import java.net.URL;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashMap;
@@ -29,6 +31,8 @@ import java.util.Set;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.StaticallyInject;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
+import org.apache.ambari.server.configuration.ComponentSSLConfiguration;
+import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
 import org.apache.ambari.server.controller.spi.NoSuchResourceException;
 import org.apache.ambari.server.controller.spi.Predicate;
@@ -44,11 +48,13 @@ import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
 import org.apache.ambari.server.orm.entities.StackEntity;
 import org.apache.ambari.server.security.authorization.ResourceType;
 import org.apache.ambari.server.security.authorization.RoleAuthorization;
+import org.apache.ambari.server.state.RepositoryType;
 import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.StackInfo;
 import org.apache.ambari.server.state.repository.VersionDefinitionXml;
 import org.apache.ambari.server.state.stack.upgrade.RepositoryVersionHelper;
 import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
 
 import com.google.common.collect.Sets;
 import com.google.inject.Inject;
@@ -67,6 +73,7 @@ public class VersionDefinitionResourceProvider extends AbstractAuthorizedResourc
   public static final String VERSION_DEF_STACK_NAME                  = "VersionDefinition/stack_name";
   public static final String VERSION_DEF_STACK_VERSION               = "VersionDefinition/stack_version";
 
+
   protected static final String VERSION_DEF_TYPE_PROPERTY_ID         = "VersionDefinition/type";
   protected static final String VERSION_DEF_DEFINITION_URL           = "VersionDefinition/version_url";
   protected static final String VERSION_DEF_FULL_VERSION             = "VersionDefinition/repository_version";
@@ -74,7 +81,7 @@ public class VersionDefinitionResourceProvider extends AbstractAuthorizedResourc
   protected static final String VERSION_DEF_RELEASE_BUILD            = "VersionDefinition/release/build";
   protected static final String VERSION_DEF_RELEASE_NOTES            = "VersionDefinition/release/notes";
   protected static final String VERSION_DEF_RELEASE_COMPATIBLE_WITH  = "VersionDefinition/release/compatible_with";
-    protected static final String VERSION_DEF_AVAILABLE_SERVICES     = "VersionDefinition/services";
+  protected static final String VERSION_DEF_AVAILABLE_SERVICES       = "VersionDefinition/services";
 
   @Inject
   private static RepositoryVersionDAO s_repoVersionDAO;
@@ -88,6 +95,9 @@ public class VersionDefinitionResourceProvider extends AbstractAuthorizedResourc
   @Inject
   private static StackDAO s_stackDAO;
 
+  @Inject
+  private static Configuration s_configuration;
+
   /**
    * Key property ids
    */
@@ -141,12 +151,13 @@ public class VersionDefinitionResourceProvider extends AbstractAuthorizedResourc
     Set<Map<String, Object>> requestProperties = request.getProperties();
 
     if (requestProperties.size() > 1) {
-      throw new SystemException("Cannot process more than one file per request");
+      throw new IllegalArgumentException("Cannot process more than one file per request");
     }
 
     final Map<String, Object> properties = requestProperties.iterator().next();
+
     if (!properties.containsKey(VERSION_DEF_DEFINITION_URL)) {
-      throw new SystemException(String.format("%s is required", VERSION_DEF_DEFINITION_URL));
+      throw new IllegalArgumentException(String.format("%s is required", VERSION_DEF_DEFINITION_URL));
     }
 
     RepositoryVersionEntity entity = createResources(new Command<RepositoryVersionEntity>() {
@@ -155,11 +166,15 @@ public class VersionDefinitionResourceProvider extends AbstractAuthorizedResourc
 
         String definitionUrl = (String) properties.get(VERSION_DEF_DEFINITION_URL);
 
-        RepositoryVersionEntity entity = toRepositoryVersionEntity(definitionUrl);
+        XmlHolder holder = loadXml(definitionUrl);
+
+        RepositoryVersionEntity entity = toRepositoryVersionEntity(holder);
 
         RepositoryVersionResourceProvider.validateRepositoryVersion(s_repoVersionDAO,
             s_metaInfo.get(), entity);
 
+        checkForParent(holder, entity);
+
         s_repoVersionDAO.create(entity);
 
         return entity;
@@ -220,6 +235,62 @@ public class VersionDefinitionResourceProvider extends AbstractAuthorizedResourc
     throw new SystemException("Cannot delete Version Definitions");
   }
 
+  /**
+   * In the case of a patch, check if there is a parent repo.
+   * @param entity the entity to check
+   */
+  private void checkForParent(XmlHolder holder, RepositoryVersionEntity entity) throws AmbariException {
+    if (entity.getType() != RepositoryType.PATCH) {
+      return;
+    }
+
+    List<RepositoryVersionEntity> entities = s_repoVersionDAO.findByStack(entity.getStackId());
+    if (entities.isEmpty()) {
+      throw new AmbariException(String.format("Patch %s was uploaded, but there are no repositories for %s",
+          entity.getVersion(), entity.getStackId().toString()));
+    }
+
+    List<RepositoryVersionEntity> matching = new ArrayList<>();
+
+    boolean emptyCompatible = StringUtils.isBlank(holder.xml.release.compatibleWith);
+
+    for (RepositoryVersionEntity candidate : entities) {
+      String baseVersion = candidate.getVersion();
+      if (baseVersion.lastIndexOf('-') > -1) {
+        baseVersion = baseVersion.substring(0,  baseVersion.lastIndexOf('-'));
+      }
+
+      if (emptyCompatible) {
+        if (baseVersion.equals(holder.xml.release.version)) {
+          matching.add(candidate);
+        }
+      } else {
+        if (baseVersion.matches(holder.xml.release.compatibleWith)) {
+          matching.add(candidate);
+        }
+      }
+    }
+
+    if (matching.isEmpty()) {
+      String format = "No versions matched pattern %s";
+
+      throw new AmbariException(String.format(format,
+          emptyCompatible ? holder.xml.release.version : holder.xml.release.compatibleWith));
+    } else if (matching.size() > 1) {
+      Set<String> versions= new HashSet<>();
+      for (RepositoryVersionEntity match : matching) {
+        versions.add(match.getVersion());
+      }
+
+      throw new AmbariException(String.format("More than one repository matches patch %s: %s",
+          entity.getVersion(), StringUtils.join(versions, ", ")));
+    }
+
+    RepositoryVersionEntity parent = matching.get(0);
+
+    entity.setParent(parent);
+  }
+
   @Override
   protected Set<String> getPKPropertyIds() {
     return PK_PROPERTY_IDS;
@@ -230,45 +301,63 @@ public class VersionDefinitionResourceProvider extends AbstractAuthorizedResourc
     return ResourceType.AMBARI;
   }
 
-  /**
-   * Transforms a XML version defintion to an entity
-   *
-   * @param definitionUrl the String URL for loading
-   * @return constructed entity
-   * @throws AmbariException if some properties are missing or json has incorrect structure
-   */
-  protected RepositoryVersionEntity toRepositoryVersionEntity(String definitionUrl) throws AmbariException {
-    final VersionDefinitionXml xml;
-    final String xmlString;
+  private XmlHolder loadXml(String definitionUrl) throws AmbariException {
+    XmlHolder holder = new XmlHolder();
+    holder.url = definitionUrl;
+
+    int connectTimeout = s_configuration.getVersionDefinitionConnectTimeout();
+    int readTimeout = s_configuration.getVersionDefinitionReadTimeout();
+
     try {
-      URL url = new URL(definitionUrl);
+      URI uri = new URI(definitionUrl);
+      InputStream stream = null;
 
-      xmlString = IOUtils.toString(url.openStream(), "UTF-8");
+      if (uri.getScheme().equalsIgnoreCase("file")) {
+        stream = uri.toURL().openStream();
+      } else {
+        URLStreamProvider provider = new URLStreamProvider(connectTimeout, readTimeout,
+            ComponentSSLConfiguration.instance());
 
-      xml = VersionDefinitionXml.load(xmlString);
+        stream = provider.readFrom(definitionUrl);
+      }
+
+      holder.xmlString = IOUtils.toString(stream, "UTF-8");
+      holder.xml = VersionDefinitionXml.load(holder.xmlString);
     } catch (Exception e) {
       String err = String.format("Could not load url from %s.  %s",
           definitionUrl, e.getMessage());
       throw new AmbariException(err, e);
     }
 
+    return holder;
+  }
+
+  /**
+   * Transforms a XML version defintion to an entity
+   *
+   * @param definitionUrl the String URL for loading
+   * @return constructed entity
+   * @throws AmbariException if some properties are missing or json has incorrect structure
+   */
+  protected RepositoryVersionEntity toRepositoryVersionEntity(XmlHolder holder) throws AmbariException {
+
     // !!! TODO validate parsed object graph
 
     RepositoryVersionEntity entity = new RepositoryVersionEntity();
 
-    StackId stackId = new StackId(xml.release.stackId);
+    StackId stackId = new StackId(holder.xml.release.stackId);
 
     StackEntity stackEntity = s_stackDAO.find(stackId.getStackName(), stackId.getStackVersion());
 
     entity.setStack(stackEntity);
     entity.setOperatingSystems(s_repoVersionHelper.get().serializeOperatingSystems(
-        xml.repositoryInfo.getRepositories()));
-    entity.setVersion(xml.release.getFullVersion());
-    entity.setDisplayName(stackId, xml.release);
-    entity.setType(xml.release.repositoryType);
-    entity.setVersionUrl(definitionUrl);
-    entity.setVersionXml(xmlString);
-    entity.setVersionXsd(xml.xsdLocation);
+        holder.xml.repositoryInfo.getRepositories()));
+    entity.setVersion(holder.xml.release.getFullVersion());
+    entity.setDisplayName(stackId, holder.xml.release);
+    entity.setType(holder.xml.release.repositoryType);
+    entity.setVersionUrl(holder.url);
+    entity.setVersionXml(holder.xmlString);
+    entity.setVersionXsd(holder.xml.xsdLocation);
 
     return entity;
   }
@@ -327,5 +416,13 @@ public class VersionDefinitionResourceProvider extends AbstractAuthorizedResourc
     return resource;
   }
 
+  /**
+   * Convenience class to hold the xml String representation, the url, and the parsed object.
+   */
+  private static class XmlHolder {
+    String url = null;
+    String xmlString = null;
+    VersionDefinitionXml xml = null;
+  }
 
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/c84da249/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 fa2f905..4af4216 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
@@ -33,6 +33,7 @@ import javax.persistence.GenerationType;
 import javax.persistence.Id;
 import javax.persistence.JoinColumn;
 import javax.persistence.Lob;
+import javax.persistence.ManyToOne;
 import javax.persistence.NamedQueries;
 import javax.persistence.NamedQuery;
 import javax.persistence.OneToMany;
@@ -124,6 +125,13 @@ public class RepositoryVersionEntity {
   @Column(name="version_xsd", insertable = true, updatable = true)
   private String versionXsd;
 
+  @ManyToOne
+  @JoinColumn(name = "parent_id")
+  private RepositoryVersionEntity parent;
+
+  @OneToMany(mappedBy = "parent")
+  private List<RepositoryVersionEntity> children;
+
   // ----- RepositoryVersionEntity -------------------------------------------------------
 
   public RepositoryVersionEntity() {
@@ -389,4 +397,26 @@ public class RepositoryVersionEntity {
     return false;
   }
 
+  /**
+   * @param parent
+   */
+  public void setParent(RepositoryVersionEntity entity) {
+    parent = entity;
+    parent.children.add(this);
+  }
+
+  /**
+   * @return the repositories that are denoted children
+   */
+  public List<RepositoryVersionEntity> getChildren() {
+    return children;
+  }
+
+  /**
+   * @return the parentId, or {@code null} if the entity is already a parent
+   */
+  public Long getParentId() {
+    return null == parent ? null : parent.getId();
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/c84da249/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 a43211c..d873122 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql
@@ -554,6 +554,7 @@ CREATE TABLE repo_version (
   version_url VARCHAR(1024),
   version_xml CLOB,
   version_xsd VARCHAR(512),
+  parent_id BIGINT,
   PRIMARY KEY(repo_version_id)
 );
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/c84da249/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 1941331..705436c 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
@@ -562,6 +562,7 @@ CREATE TABLE repo_version (
   version_url VARCHAR(1024),
   version_xml MEDIUMTEXT,
   version_xsd VARCHAR(512),
+  parent_id BIGINT,
   PRIMARY KEY(repo_version_id)
 );
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/c84da249/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 75d2a4b..10785b9 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
@@ -551,6 +551,7 @@ CREATE TABLE repo_version (
   version_url VARCHAR(1024),
   version_xml CLOB,
   version_xsd VARCHAR(512),
+  parent_id NUMBER(19),
   PRIMARY KEY(repo_version_id)
 );
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/c84da249/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 1a6c9c6..54f5404 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
@@ -555,6 +555,7 @@ CREATE TABLE repo_version (
   version_url VARCHAR(1024),
   version_xml VARCHAR TEXT,
   version_xsd VARCHAR(512),
+  parent_id BIGINT,
   PRIMARY KEY(repo_version_id)
 );
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/c84da249/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
index efaa7a9..f0302f5 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
@@ -624,6 +624,7 @@ CREATE TABLE ambari.repo_version (
   version_url VARCHAR(1024),
   version_xml TEXT,
   version_xsd VARCHAR(512),
+  parent_id BIGINT,
   PRIMARY KEY(repo_version_id)
 );
 GRANT ALL PRIVILEGES ON TABLE ambari.repo_version TO :username;

http://git-wip-us.apache.org/repos/asf/ambari/blob/c84da249/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 267a1f0..fba786f 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
@@ -552,6 +552,7 @@ CREATE TABLE repo_version (
   version_url VARCHAR(1024),
   version_xml TEXT,
   version_xsd VARCHAR(512),
+  parent_id NUMERIC(19),
   PRIMARY KEY(repo_version_id)
 );
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/c84da249/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 828a3a3..a5c60c7 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
@@ -656,6 +656,7 @@ CREATE TABLE repo_version (
   version_url VARCHAR(1024),
   version_xml VARCHAR(MAX),
   version_xsd VARCHAR(512),
+  parent_id BIGINT,
   PRIMARY KEY CLUSTERED (repo_version_id)
   );
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/c84da249/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RepositoryVersionResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RepositoryVersionResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RepositoryVersionResourceProviderTest.java
index f3cf954..e031fc8 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RepositoryVersionResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RepositoryVersionResourceProviderTest.java
@@ -18,7 +18,6 @@
 
 package org.apache.ambari.server.controller.internal;
 
-import java.io.File;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -33,8 +32,6 @@ import org.apache.ambari.server.controller.ResourceProviderFactory;
 import org.apache.ambari.server.controller.predicate.AndPredicate;
 import org.apache.ambari.server.controller.spi.Predicate;
 import org.apache.ambari.server.controller.spi.Request;
-import org.apache.ambari.server.controller.spi.RequestStatus;
-import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.controller.spi.ResourceProvider;
 import org.apache.ambari.server.controller.utilities.PredicateBuilder;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
@@ -277,64 +274,7 @@ public class RepositoryVersionResourceProviderTest {
     Assert.assertEquals(1, provider.getResources(getRequest, new AndPredicate(predicateStackName, predicateStackVersion)).size());
   }
 
-  @Test
-  public void testCreateResourcesWithUrl() throws Exception {
-    Authentication authentication = TestAuthenticationFactory.createAdministrator();
-    SecurityContextHolder.getContext().setAuthentication(authentication);
-
-    File file = new File("src/test/resources/version_definition_resource_provider.xml");
-
-    final ResourceProvider versionProvider = new VersionDefinitionResourceProvider();
-    final ResourceProvider provider = injector.getInstance(ResourceProviderFactory.class).getRepositoryVersionResourceProvider();
 
-    final Set<Map<String, Object>> propertySet = new LinkedHashSet<Map<String, Object>>();
-    final Map<String, Object> properties = new LinkedHashMap<String, Object>();
-    properties.put(VersionDefinitionResourceProvider.VERSION_DEF_DEFINITION_URL, file.toURI().toURL().toString());
-    propertySet.add(properties);
-
-    final Predicate predicateStackName = new PredicateBuilder().property(RepositoryVersionResourceProvider.REPOSITORY_VERSION_STACK_NAME_PROPERTY_ID).equals("HDP").toPredicate();
-    final Predicate predicateStackVersion = new PredicateBuilder().property(RepositoryVersionResourceProvider.REPOSITORY_VERSION_STACK_VERSION_PROPERTY_ID).equals("1.1").toPredicate();
-    Request getRequest = PropertyHelper.getReadRequest(RepositoryVersionResourceProvider.REPOSITORY_VERSION_DISPLAY_NAME_PROPERTY_ID);
-    Assert.assertEquals(0, provider.getResources(getRequest, new AndPredicate(predicateStackName, predicateStackVersion)).size());
-
-    final Request createRequest = PropertyHelper.getCreateRequest(propertySet, null);
-    RequestStatus status = versionProvider.createResources(createRequest);
-    Assert.assertEquals(1, status.getAssociatedResources().size());
-
-    getRequest = PropertyHelper.getReadRequest("VersionDefinition");
-    Set<Resource> results = versionProvider.getResources(getRequest, null);
-    Assert.assertEquals(1, results.size());
-
-    results = provider.getResources(getRequest, new AndPredicate(predicateStackName, predicateStackVersion));
-    Assert.assertEquals(1, results.size());
-
-    getRequest = PropertyHelper.getReadRequest(
-        RepositoryVersionResourceProvider.REPOSITORY_VERSION_DISPLAY_NAME_PROPERTY_ID,
-        RepositoryVersionResourceProvider.REPOSITORY_VERSION_ID_PROPERTY_ID,
-        RepositoryVersionResourceProvider.REPOSITORY_VERSION_REPOSITORY_VERSION_PROPERTY_ID,
-        RepositoryVersionResourceProvider.REPOSITORY_VERSION_STACK_NAME_PROPERTY_ID,
-        RepositoryVersionResourceProvider.REPOSITORY_VERSION_STACK_VERSION_PROPERTY_ID,
-        "RepositoryVersions/release",
-        "RepositoryVersions/services",
-        RepositoryVersionResourceProvider.SUBRESOURCE_OPERATING_SYSTEMS_PROPERTY_ID,
-        RepositoryVersionResourceProvider.SUBRESOURCE_REPOSITORIES_PROPERTY_ID);
-
-    results = provider.getResources(getRequest, new AndPredicate(predicateStackName, predicateStackVersion));
-    Assert.assertEquals(1, results.size());
-
-    Resource r = results.iterator().next();
-    Map<String, Map<String, Object>> map = r.getPropertiesMap();
-    Assert.assertTrue(map.containsKey("RepositoryVersions"));
-
-    Map<String, Object> vals = map.get("RepositoryVersions");
-    Assert.assertEquals("1.1.7.1-1234", vals.get("repository_version"));
-
-    Assert.assertTrue(map.containsKey("RepositoryVersions/release"));
-    vals = map.get("RepositoryVersions/release");
-    Assert.assertEquals("1234", vals.get("build"));
-    Assert.assertEquals("2.3.4.[1-9]", vals.get("compatible_with"));
-    Assert.assertEquals("http://docs.hortonworks.com/HDPDocuments/HDP2/HDP-2.3.4/", vals.get("notes"));
-  }
 
 
   @Test

http://git-wip-us.apache.org/repos/asf/ambari/blob/c84da249/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/VersionDefinitionResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/VersionDefinitionResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/VersionDefinitionResourceProviderTest.java
new file mode 100644
index 0000000..dbac1b4
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/VersionDefinitionResourceProviderTest.java
@@ -0,0 +1,160 @@
+/**
+ * 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.controller.internal;
+
+import java.io.File;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ambari.server.api.services.AmbariMetaInfo;
+import org.apache.ambari.server.controller.ResourceProviderFactory;
+import org.apache.ambari.server.controller.predicate.AndPredicate;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.RequestStatus;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.ResourceProvider;
+import org.apache.ambari.server.controller.utilities.PredicateBuilder;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.orm.GuiceJpaInitializer;
+import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
+import org.apache.ambari.server.orm.dao.RepositoryVersionDAO;
+import org.apache.ambari.server.orm.dao.StackDAO;
+import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
+import org.apache.ambari.server.orm.entities.StackEntity;
+import org.apache.ambari.server.security.TestAuthenticationFactory;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.persist.PersistService;
+
+/**
+ * Tests the VersionDefinitionResourceProvider class
+ */
+public class VersionDefinitionResourceProviderTest {
+  private Injector injector;
+
+  @Before
+  public void before() throws Exception {
+    injector = Guice.createInjector(new InMemoryDefaultTestModule());
+    injector.getInstance(GuiceJpaInitializer.class);
+
+    AmbariMetaInfo ami = injector.getInstance(AmbariMetaInfo.class);
+    ami.init();
+
+    StackDAO stackDao = injector.getInstance(StackDAO.class);
+    StackEntity stack = stackDao.find("HDP", "2.2.0");
+
+    RepositoryVersionEntity entity = new RepositoryVersionEntity();
+    entity.setStack(stack);
+    entity.setDisplayName("2.2.0.0");
+    entity.setVersion("2.3.4.4-1234");
+
+    RepositoryVersionDAO dao = injector.getInstance(RepositoryVersionDAO.class);
+    dao.create(entity);
+
+  }
+
+  @After
+  public void after() throws Exception {
+    injector.getInstance(PersistService.class).stop();
+  }
+
+  @Test
+  public void testWithParent() throws Exception {
+    Authentication authentication = TestAuthenticationFactory.createAdministrator();
+    SecurityContextHolder.getContext().setAuthentication(authentication);
+
+    File file = new File("src/test/resources/version_definition_resource_provider.xml");
+
+    final ResourceProvider versionProvider = new VersionDefinitionResourceProvider();
+    final ResourceProvider provider = injector.getInstance(ResourceProviderFactory.class)
+        .getRepositoryVersionResourceProvider();
+
+    final Set<Map<String, Object>> propertySet = new LinkedHashSet<Map<String, Object>>();
+    final Map<String, Object> properties = new LinkedHashMap<String, Object>();
+    properties.put(VersionDefinitionResourceProvider.VERSION_DEF_DEFINITION_URL,
+        file.toURI().toURL().toString());
+    propertySet.add(properties);
+
+
+    final Request createRequest = PropertyHelper.getCreateRequest(propertySet, null);
+    RequestStatus status = versionProvider.createResources(createRequest);
+    Assert.assertEquals(1, status.getAssociatedResources().size());
+
+    Request getRequest = PropertyHelper.getReadRequest("VersionDefinition");
+    Set<Resource> results = versionProvider.getResources(getRequest, null);
+    Assert.assertEquals(1, results.size());
+
+    final Predicate predicateStackName = new PredicateBuilder().property(RepositoryVersionResourceProvider.REPOSITORY_VERSION_STACK_NAME_PROPERTY_ID).equals("HDP").toPredicate();
+    final Predicate predicateStackVersion = new PredicateBuilder().property(RepositoryVersionResourceProvider.REPOSITORY_VERSION_STACK_VERSION_PROPERTY_ID).equals("2.2.0").toPredicate();
+
+    results = provider.getResources(getRequest,
+        new AndPredicate(predicateStackName, predicateStackVersion));
+    Assert.assertEquals(1, results.size());
+
+    getRequest = PropertyHelper.getReadRequest(
+        RepositoryVersionResourceProvider.REPOSITORY_VERSION_DISPLAY_NAME_PROPERTY_ID,
+        RepositoryVersionResourceProvider.REPOSITORY_VERSION_ID_PROPERTY_ID,
+        RepositoryVersionResourceProvider.REPOSITORY_VERSION_REPOSITORY_VERSION_PROPERTY_ID,
+        RepositoryVersionResourceProvider.REPOSITORY_VERSION_STACK_NAME_PROPERTY_ID,
+        RepositoryVersionResourceProvider.REPOSITORY_VERSION_STACK_VERSION_PROPERTY_ID,
+        RepositoryVersionResourceProvider.SUBRESOURCE_OPERATING_SYSTEMS_PROPERTY_ID,
+        RepositoryVersionResourceProvider.SUBRESOURCE_REPOSITORIES_PROPERTY_ID,
+        "RepositoryVersions/release", "RepositoryVersions/services",
+        "RepositoryVersions/has_children", "RepositoryVersions/parent_id");
+
+    results = provider.getResources(getRequest,
+        new AndPredicate(predicateStackName, predicateStackVersion));
+    Assert.assertEquals(2, results.size());
+
+    Resource r = null;
+    for (Resource result : results) {
+      if (result.getPropertyValue("RepositoryVersions/repository_version").equals("2.2.0.8-5678")) {
+        r = result;
+        break;
+      }
+    }
+
+    Assert.assertNotNull(r);
+    Map<String, Map<String, Object>> map = r.getPropertiesMap();
+    Assert.assertTrue(map.containsKey("RepositoryVersions"));
+
+    Map<String, Object> vals = map.get("RepositoryVersions");
+
+    Assert.assertEquals("2.2.0.8-5678", vals.get("repository_version"));
+    Assert.assertNotNull(vals.get("parent_id"));
+    Assert.assertEquals(Boolean.FALSE, vals.get("has_children"));
+
+
+    Assert.assertTrue(map.containsKey("RepositoryVersions/release"));
+    vals = map.get("RepositoryVersions/release");
+    Assert.assertEquals("5678", vals.get("build"));
+    Assert.assertEquals("2.3.4.[1-9]", vals.get("compatible_with"));
+    Assert.assertEquals("http://docs.hortonworks.com/HDPDocuments/HDP2/HDP-2.3.4/",
+        vals.get("notes"));
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/c84da249/ambari-server/src/test/resources/version_definition_resource_provider.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/resources/version_definition_resource_provider.xml b/ambari-server/src/test/resources/version_definition_resource_provider.xml
index 735f91d..55cd1c1 100644
--- a/ambari-server/src/test/resources/version_definition_resource_provider.xml
+++ b/ambari-server/src/test/resources/version_definition_resource_provider.xml
@@ -21,9 +21,9 @@
   
   <release>
     <type>PATCH</type>
-    <stack-id>HDP-1.1</stack-id>
-    <version>1.1.7.1</version>
-    <build>1234</build>
+    <stack-id>HDP-2.2.0</stack-id>
+    <version>2.2.0.8</version>
+    <build>5678</build>
     <compatible-with>2.3.4.[1-9]</compatible-with>
     <release-notes>http://docs.hortonworks.com/HDPDocuments/HDP2/HDP-2.3.4/</release-notes>
   </release>
@@ -54,17 +54,12 @@
         <reponame>HDP-UTILS</reponame>
       </repo>
     </os>
-    <os family="redhat7">
+    <os family="suse11">
       <repo>
         <baseurl>http://public-repo-1.hortonworks.com/HDP/centos7/2.x/updates/2.3.0.0</baseurl>
         <repoid>HDP-2.3</repoid>
         <reponame>HDP</reponame>
       </repo>
-      <repo>
-        <baseurl>http://public-repo-1.hortonworks.com/HDP-UTILS-1.1.0.20/repos/centos7</baseurl>
-        <repoid>HDP-UTILS-1.1.0.20</repoid>
-        <reponame>HDP-UTILS</reponame>
-      </repo>
     </os>
 
   </repository-info>