You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ds...@apache.org on 2018/01/29 09:50:11 UTC

[ambari] branch branch-feature-AMBARI-14714 updated: [AMBARI-22845] Update service metainfo schema (#194)

This is an automated email from the ASF dual-hosted git repository.

dsen pushed a commit to branch branch-feature-AMBARI-14714
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/branch-feature-AMBARI-14714 by this push:
     new 137d82e  [AMBARI-22845] Update service metainfo schema (#194)
137d82e is described below

commit 137d82ebfb8db273706c23a352803a8ed671a09b
Author: Dmitry Sen <ds...@apache.org>
AuthorDate: Mon Jan 29 11:50:08 2018 +0200

    [AMBARI-22845] Update service metainfo schema (#194)
    
    * AMBARI-22845 Update service metainfo schema (dsen)
    
    * AMBARI-22845 Update service metainfo schema - changes according to review (dsen)
---
 .../controller/StackServiceComponentResponse.java  | 25 +++++++
 .../server/controller/StackServiceResponse.java    | 12 ++++
 .../StackServiceComponentResourceProvider.java     |  7 ++
 .../internal/StackServiceResourceProvider.java     | 15 +++--
 .../apache/ambari/server/stack/ServiceModule.java  | 22 ++++++-
 .../apache/ambari/server/stack/StackModule.java    |  9 +++
 .../apache/ambari/server/state/ComponentInfo.java  | 18 ++++-
 .../ambari/server/state/ServiceCategory.java       | 39 +++++++++++
 .../apache/ambari/server/state/ServiceInfo.java    | 17 +++++
 ambari-server/src/main/resources/properties.json   |  2 +
 .../server/api/services/AmbariMetaInfoTest.java    | 25 +++++++
 .../ambari/server/stack/ComponentModuleTest.java   | 27 ++++++++
 .../ambari/server/stack/ServiceModuleTest.java     | 77 ++++++++++++++++++++++
 .../ambari/server/state/ServiceInfoTest.java       | 60 +++++++++++++++++
 .../stacks/HDP/2.0.6.1/services/HBASE/metainfo.xml |  1 +
 .../stacks/HDP/2.0.6/services/HBASE/metainfo.xml   |  3 +-
 16 files changed, 352 insertions(+), 7 deletions(-)

diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/StackServiceComponentResponse.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/StackServiceComponentResponse.java
index 8797f03..cf0d60f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/StackServiceComponentResponse.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/StackServiceComponentResponse.java
@@ -59,6 +59,11 @@ public class StackServiceComponentResponse {
   private String componentDisplayName;
 
   /**
+   * component version
+   */
+  private String componentVersion;
+
+  /**
    * component category
    */
   private String componentCategory;
@@ -132,6 +137,7 @@ public class StackServiceComponentResponse {
     componentName = component.getName();
     componentDisplayName = component.getDisplayName();
     componentCategory = component.getCategory();
+    componentVersion = component.getVersion();
     isClient = component.isClient();
     isMaster = component.isMaster();
     cardinality = component.getCardinality();
@@ -322,6 +328,25 @@ public class StackServiceComponentResponse {
   }
 
   /**
+   * Get component version.
+   *
+   * @return component version
+   */
+  @ApiModelProperty(name = "component_version")
+  public String getComponentVersion() {
+    return componentVersion;
+  }
+
+  /**
+   * Set the component version.
+   *
+   * @param componentVersion  component version
+   */
+  public void setComponentVersion(String componentVersion) {
+    this.componentVersion = componentVersion;
+  }
+
+  /**
    * Determine whether the component is a client component.
    *
    * @return whether the component is a client component
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/StackServiceResponse.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/StackServiceResponse.java
index 6c3af87..e02c91c 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/StackServiceResponse.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/StackServiceResponse.java
@@ -27,6 +27,7 @@ import java.util.Set;
 
 import org.apache.ambari.server.state.CustomCommandDefinition;
 import org.apache.ambari.server.state.RequiredService;
+import org.apache.ambari.server.state.ServiceCategory;
 import org.apache.ambari.server.state.ServiceInfo;
 
 import io.swagger.annotations.ApiModelProperty;
@@ -37,6 +38,7 @@ public class StackServiceResponse {
   private String stackVersion;
   private String serviceName;
   private String serviceType;
+  private ServiceCategory serviceCategory;
   private String serviceDisplayName;
   private String userName;
   private String comments;
@@ -89,6 +91,7 @@ public class StackServiceResponse {
   public StackServiceResponse(ServiceInfo service) {
     serviceName = service.getName();
     serviceType = service.getServiceType();
+    serviceCategory = service.getCategory();
     serviceDisplayName = service.getDisplayName();
     userName = null;
     comments = service.getComment();
@@ -162,6 +165,15 @@ public class StackServiceResponse {
     this.serviceType = serviceType;
   }
 
+  @ApiModelProperty(name = "service_category")
+  public ServiceCategory getServiceCategory() {
+    return serviceCategory;
+  }
+
+  public void setServiceCategory(ServiceCategory serviceCategory) {
+    this.serviceCategory = serviceCategory;
+  }
+
   @ApiModelProperty(name = "display_name")
   public String getServiceDisplayName() {
     return serviceDisplayName;
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackServiceComponentResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackServiceComponentResourceProvider.java
index 933dcb7..8110439 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackServiceComponentResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackServiceComponentResourceProvider.java
@@ -62,6 +62,9 @@ public class StackServiceComponentResourceProvider extends
   private static final String COMPONENT_CATEGORY_PROPERTY_ID = PropertyHelper.getPropertyId(
       "StackServiceComponents", "component_category");
 
+  private static final String COMPONENT_VERSION_PROPERTY_ID = PropertyHelper.getPropertyId(
+      "StackServiceComponents", "component_version");
+
   private static final String IS_CLIENT_PROPERTY_ID = PropertyHelper.getPropertyId(
       "StackServiceComponents", "is_client");
 
@@ -128,6 +131,7 @@ public class StackServiceComponentResourceProvider extends
       COMPONENT_NAME_PROPERTY_ID,
       COMPONENT_DISPLAY_NAME_PROPERTY_ID,
       COMPONENT_CATEGORY_PROPERTY_ID,
+      COMPONENT_VERSION_PROPERTY_ID,
       IS_CLIENT_PROPERTY_ID,
       IS_MASTER_PROPERTY_ID,
       CARDINALITY_ID,
@@ -195,6 +199,9 @@ public class StackServiceComponentResourceProvider extends
       setResourceProperty(resource, COMPONENT_CATEGORY_PROPERTY_ID,
           response.getComponentCategory(), requestedIds);
 
+      setResourceProperty(resource, COMPONENT_VERSION_PROPERTY_ID,
+          response.getComponentVersion(), requestedIds);
+
       setResourceProperty(resource, IS_CLIENT_PROPERTY_ID,
           response.isClient(), requestedIds);
 
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackServiceResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackServiceResourceProvider.java
index a4bf32b..24c411e 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackServiceResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackServiceResourceProvider.java
@@ -54,6 +54,9 @@ public class StackServiceResourceProvider extends ReadOnlyResourceProvider {
   protected static final String SERVICE_TYPE_PROPERTY_ID = PropertyHelper.getPropertyId(
 		  "StackServices", "service_type");
 
+  protected static final String SERVICE_CATEGORY_PROPERTY_ID = PropertyHelper.getPropertyId(
+		  "StackServices", "service_category");
+
   public static final String STACK_NAME_PROPERTY_ID = PropertyHelper.getPropertyId(
       "StackServices", "stack_name");
 
@@ -117,6 +120,7 @@ public class StackServiceResourceProvider extends ReadOnlyResourceProvider {
   private static Set<String> propertyIds = Sets.newHashSet(
       SERVICE_NAME_PROPERTY_ID,
       SERVICE_TYPE_PROPERTY_ID,
+      SERVICE_CATEGORY_PROPERTY_ID,
       STACK_NAME_PROPERTY_ID,
       STACK_VERSION_PROPERTY_ID,
       SERVICE_DISPLAY_NAME_PROPERTY_ID,
@@ -185,11 +189,14 @@ public class StackServiceResourceProvider extends ReadOnlyResourceProvider {
     setResourceProperty(resource, STACK_NAME_PROPERTY_ID,
         response.getStackName(), requestedIds);
 
-      setResourceProperty(resource, SERVICE_NAME_PROPERTY_ID,
-          response.getServiceName(), requestedIds);
+    setResourceProperty(resource, SERVICE_NAME_PROPERTY_ID,
+        response.getServiceName(), requestedIds);
+
+    setResourceProperty(resource, SERVICE_TYPE_PROPERTY_ID,
+        response.getServiceType(), requestedIds);
 
-      setResourceProperty(resource, SERVICE_TYPE_PROPERTY_ID,
-		  response.getServiceType(), requestedIds);
+    setResourceProperty(resource, SERVICE_CATEGORY_PROPERTY_ID,
+        response.getServiceCategory(), requestedIds);
 
     setResourceProperty(resource, STACK_VERSION_PROPERTY_ID,
         response.getStackVersion(), requestedIds);
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceModule.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceModule.java
index ea992e1..1d870a9 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceModule.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceModule.java
@@ -35,6 +35,7 @@ import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.state.ComponentInfo;
 import org.apache.ambari.server.state.CustomCommandDefinition;
 import org.apache.ambari.server.state.QuickLinksConfigurationInfo;
+import org.apache.ambari.server.state.ServiceCategory;
 import org.apache.ambari.server.state.ServiceInfo;
 import org.apache.ambari.server.state.ServicePropertyInfo;
 import org.apache.ambari.server.state.ThemeInfo;
@@ -209,6 +210,10 @@ public class ServiceModule extends BaseModule<ServiceModule, ServiceInfo> implem
       ServiceInfo.ServiceAdvisorType serviceAdvisorType = parent.getServiceAdvisorType();
       serviceInfo.setServiceAdvisorType(serviceAdvisorType == null ? ServiceInfo.ServiceAdvisorType.PYTHON : serviceAdvisorType);
     }
+    if (serviceInfo.getCategory() == null) {
+      ServiceCategory category = parent.getCategory();
+      serviceInfo.setCategory(category == null ? ServiceCategory.LEGACY : category);
+    }
     if (serviceInfo.getVersion() == null) {
       serviceInfo.setVersion(parent.getVersion());
     }
@@ -602,11 +607,26 @@ public class ServiceModule extends BaseModule<ServiceModule, ServiceInfo> implem
       ServiceModule parent, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices, Map<String, ExtensionModule> extensions)
       throws AmbariException {
     serviceInfo.getComponents().clear();
+
+    //component version should not inherit from parent service
+    //make deep copy of all the parent components and set component versions to null
+    //TODO get rid of this when we move to only standalone stack definitions
+    Map<String, ComponentModule> parentComponentsCopy = new HashMap<>();
+    parent.componentModules.values().forEach(componentModule -> parentComponentsCopy.put(componentModule.getId(),
+        new ComponentModule(new ComponentInfo(componentModule.getModuleInfo()))));
+    //set nulls to component versions
+    parentComponentsCopy.values().forEach(componentModule -> componentModule.getModuleInfo().setVersion(null));
+
     Collection<ComponentModule> mergedModules = mergeChildModules(
-        allStacks, commonServices, extensions, componentModules, parent.componentModules);
+        allStacks, commonServices, extensions, componentModules, parentComponentsCopy);
     componentModules.clear();
     for (ComponentModule module : mergedModules) {
       if (!module.isDeleted()){
+        //if the version is not defined for component, set the service version
+        if (module.getModuleInfo().getVersion() == null) {
+          module.getModuleInfo().setVersion(serviceInfo.getVersion());
+        }
+
         componentModules.put(module.getId(), module);
         serviceInfo.getComponents().add(module.getModuleInfo());
       }
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/StackModule.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/StackModule.java
index b57715d..f576a34 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/StackModule.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/StackModule.java
@@ -40,6 +40,7 @@ import org.apache.ambari.server.state.PropertyDependencyInfo;
 import org.apache.ambari.server.state.PropertyInfo;
 import org.apache.ambari.server.state.RefreshCommand;
 import org.apache.ambari.server.state.RepositoryInfo;
+import org.apache.ambari.server.state.ServiceCategory;
 import org.apache.ambari.server.state.ServiceInfo;
 import org.apache.ambari.server.state.StackInfo;
 import org.apache.ambari.server.state.stack.ConfigUpgradePack;
@@ -353,6 +354,14 @@ public class StackModule extends BaseModule<StackModule, StackInfo> implements V
         mergeServiceWithExplicitParent(service, parent, allStacks, commonServices, extensions);
       }else {
         serviceInfo.setServiceAdvisorType(ServiceInfo.ServiceAdvisorType.PYTHON);
+        if (serviceInfo.getCategory() == null) {
+          serviceInfo.setCategory(ServiceCategory.LEGACY);
+        }
+        for (ComponentInfo componentInfo : serviceInfo.getComponents()) {
+          if (componentInfo.getVersion() == null) {
+            componentInfo.setVersion(serviceInfo.getVersion());
+          }
+        }
       }
     }
   }
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ComponentInfo.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ComponentInfo.java
index 351f865..1e89f40 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ComponentInfo.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ComponentInfo.java
@@ -38,7 +38,11 @@ public class ComponentInfo {
   private String category;
   private boolean deleted;
   private String cardinality;
-  
+
+  //Should inherit the service version if component version is not defined
+  //Doesn't inherit version from parent component
+  private String version;
+
   @XmlElement(name="versionAdvertised")
   private Boolean versionAdvertisedField;
   
@@ -162,6 +166,8 @@ public class ComponentInfo {
   public ComponentInfo(ComponentInfo prototype) {
     name = prototype.name;
     category = prototype.category;
+    version = prototype.version;
+    displayName = prototype.displayName;
     deleted = prototype.deleted;
     cardinality = prototype.cardinality;
     versionAdvertisedField = prototype.versionAdvertisedField;
@@ -200,6 +206,14 @@ public class ComponentInfo {
     this.displayName = displayName;
   }
 
+  public String getVersion() {
+    return version;
+  }
+
+  public void setVersion(String version) {
+    this.version = version;
+  }
+
   public String getCategory() {
     return category;
   }
@@ -460,6 +474,7 @@ public class ComponentInfo {
     ComponentInfo that = (ComponentInfo) o;
 
     if (deleted != that.deleted) return false;
+    if (version != that.version) return false;
     if (autoDeploy != null ? !autoDeploy.equals(that.autoDeploy) : that.autoDeploy != null) return false;
     if (cardinality != null ? !cardinality.equals(that.cardinality) : that.cardinality != null) return false;
     if (versionAdvertisedField != null ? !versionAdvertisedField.equals(that.versionAdvertisedField) : that.versionAdvertisedField != null) return false;
@@ -492,6 +507,7 @@ public class ComponentInfo {
   public int hashCode() {
     int result = name != null ? name.hashCode() : 0;
     result = 31 * result + (displayName != null ? displayName.hashCode() : 0);
+    result = 31 * result + (version != null ? version.hashCode() : 0);
     result = 31 * result + (category != null ? category.hashCode() : 0);
     result = 31 * result + (deleted ? 1 : 0);
     result = 31 * result + (cardinality != null ? cardinality.hashCode() : 0);
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceCategory.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceCategory.java
new file mode 100644
index 0000000..3a6e3f0
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceCategory.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.state;
+
+/**
+ * The category of service.
+ */
+public enum ServiceCategory {
+  /**
+   * The service contains form only master and slave components
+   */
+  SERVER,
+
+  /**
+   * The service contains from only client components
+   */
+  CLIENT,
+
+  /**
+   * The service contains all kind of components
+   */
+  LEGACY
+}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceInfo.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceInfo.java
index 943b826..47c1c17 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceInfo.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceInfo.java
@@ -69,6 +69,7 @@ public class ServiceInfo implements Validable, Cloneable {
   private String schemaVersion;
 
   private String name;
+  private ServiceCategory category;
   private String displayName;
   private String version;
   private String comment;
@@ -350,6 +351,14 @@ public class ServiceInfo implements Validable, Cloneable {
     this.name = name;
   }
 
+  public ServiceCategory getCategory() {
+    return category;
+  }
+
+  public void setCategory(ServiceCategory category) {
+    this.category = category;
+  }
+
   public String getParent() {
     return parent;
   }
@@ -489,6 +498,11 @@ public class ServiceInfo implements Validable, Cloneable {
     return components;
   }
 
+  //Used only for testing purposes
+  public void setComponents(List<ComponentInfo> components) {
+    this.components = components;
+  }
+
   /**
    * Finds ComponentInfo by component name
    * @param componentName  name of the component
@@ -503,6 +517,9 @@ public class ServiceInfo implements Validable, Cloneable {
     return null;
   }
   public boolean isClientOnlyService() {
+    if (ServiceCategory.CLIENT.equals(category)) {
+      return true;
+    }
     if (components == null || components.isEmpty()) {
       return false;
     }
diff --git a/ambari-server/src/main/resources/properties.json b/ambari-server/src/main/resources/properties.json
index a995049..0457a47 100644
--- a/ambari-server/src/main/resources/properties.json
+++ b/ambari-server/src/main/resources/properties.json
@@ -189,6 +189,7 @@
         "StackServices/stack_name",
         "StackServices/stack_version",
         "StackServices/service_name",
+        "StackServices/service_category",
         "StackServices/display_name",
         "StackServices/selection",
         "StackServices/user_name",
@@ -227,6 +228,7 @@
         "StackServiceComponents/stack_version",
         "StackServiceComponents/service_name",
         "StackServiceComponents/component_name",
+        "StackServiceComponents/component_version",
         "StackServiceComponents/display_name",
         "StackServiceComponents/component_category",
         "StackServiceComponents/is_client",
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/AmbariMetaInfoTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/AmbariMetaInfoTest.java
index 6c0a6e4..5c89178 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/api/services/AmbariMetaInfoTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/AmbariMetaInfoTest.java
@@ -74,6 +74,7 @@ import org.apache.ambari.server.state.OperatingSystemInfo;
 import org.apache.ambari.server.state.PropertyDependencyInfo;
 import org.apache.ambari.server.state.PropertyInfo;
 import org.apache.ambari.server.state.RepositoryInfo;
+import org.apache.ambari.server.state.ServiceCategory;
 import org.apache.ambari.server.state.ServiceGroup;
 import org.apache.ambari.server.state.ServiceInfo;
 import org.apache.ambari.server.state.StackId;
@@ -1546,6 +1547,30 @@ public class AmbariMetaInfoTest {
   }
 
   @Test
+  public void testResolveComponentVersion() throws AmbariException {
+    ServiceInfo parentServiceInfo = metaInfo.getService("HDP", "2.0.6", "HBASE");
+    assertEquals("0.95.2.2.0.6.0", parentServiceInfo.getVersion());
+
+    ComponentInfo parentComponentInfo = metaInfo.getComponent("HDP", "2.0.6", "HBASE", "HBASE_MASTER");
+    assertEquals("0.95.2.2.0.6.0-hbase-master", parentComponentInfo.getVersion());
+
+    ComponentInfo childComponentInfo = metaInfo.getComponent("HDP", "2.0.6.1", "HBASE", "HBASE_MASTER");
+    assertEquals("0.95.2.2.0.6.1", childComponentInfo.getVersion());
+  }
+
+  @Test
+  public void testResolveServiceCategory() throws AmbariException {
+    ServiceInfo defaultServiceInfo = metaInfo.getService("HDP", "2.0.5", "HBASE");
+    assertEquals(ServiceCategory.LEGACY, defaultServiceInfo.getCategory());
+
+    ServiceInfo parentServiceInfo = metaInfo.getService("HDP", "2.0.6", "HBASE");
+    assertEquals(ServiceCategory.SERVER, parentServiceInfo.getCategory());
+
+    ServiceInfo childServiceInfo = metaInfo.getService("HDP", "2.0.6.1", "HBASE");
+    assertEquals(ServiceCategory.CLIENT, childServiceInfo.getCategory());;
+  }
+
+  @Test
   public void testGetComponentDependencies() throws AmbariException {
     List<DependencyInfo> dependencies = metaInfo.getComponentDependencies("HDP", "1.3.4", "HBASE", "HBASE_MASTER");
     assertEquals(2, dependencies.size());
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/stack/ComponentModuleTest.java b/ambari-server/src/test/java/org/apache/ambari/server/stack/ComponentModuleTest.java
index 060916a..1c90910 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/stack/ComponentModuleTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/stack/ComponentModuleTest.java
@@ -418,6 +418,33 @@ public class ComponentModuleTest {
   }
 
   @Test
+  public void testResolve_Version() throws Exception {
+    String version = "1.1";
+
+    // specified in child only
+    ComponentInfo info = new ComponentInfo();
+    ComponentInfo parentInfo = new ComponentInfo();
+    info.setVersion(version);
+
+    ComponentModule module = resolveComponent(info, parentInfo);
+    assertEquals(version, module.getModuleInfo().getVersion());
+
+    // specified in parent only, shouldn't be inherited
+    info.setVersion(null);
+    parentInfo.setVersion(version);
+
+    module = resolveComponent(info, parentInfo);
+    assertEquals(null, module.getModuleInfo().getVersion());
+
+    // specified in both
+    info.setVersion(version);
+    parentInfo.setVersion("1.0");
+
+    module = resolveComponent(info, parentInfo);
+    assertEquals(version, module.getModuleInfo().getVersion());
+  }
+
+  @Test
   public void testResolve_BulkCommandsDefinition(){
     BulkCommandDefinition bulkCommandsDefinition = new BulkCommandDefinition();
     ComponentInfo info = new ComponentInfo();
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/stack/ServiceModuleTest.java b/ambari-server/src/test/java/org/apache/ambari/server/stack/ServiceModuleTest.java
index f79cd52..4bce2e0 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/stack/ServiceModuleTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/stack/ServiceModuleTest.java
@@ -47,6 +47,7 @@ import org.apache.ambari.server.state.CustomCommandDefinition;
 import org.apache.ambari.server.state.OsSpecific;
 import org.apache.ambari.server.state.PropertyInfo;
 import org.apache.ambari.server.state.RequiredService;
+import org.apache.ambari.server.state.ServiceCategory;
 import org.apache.ambari.server.state.ServiceDependencyType;
 import org.apache.ambari.server.state.ServiceInfo;
 import org.apache.ambari.server.state.ServicePropertyInfo;
@@ -1252,6 +1253,82 @@ public class ServiceModuleTest {
   }
 
 
+  @Test
+  public void testResolve_Category() throws Exception {
+    ServiceCategory serverCategory = ServiceCategory.SERVER;
+
+    // specified in child only
+    ServiceInfo info = new ServiceInfo();
+    ServiceInfo parentInfo = new ServiceInfo();
+    info.setCategory(serverCategory);
+
+    ServiceModule service = resolveService(info, parentInfo);
+    assertEquals(serverCategory, service.getModuleInfo().getCategory());
+
+    // specified in parent only
+    info.setCategory(null);
+    parentInfo.setCategory(serverCategory);
+
+    service = resolveService(info, parentInfo);
+    assertEquals(serverCategory, service.getModuleInfo().getCategory());
+
+    // specified in both
+    ServiceCategory clientCategory = ServiceCategory.CLIENT;
+
+    info.setCategory(serverCategory);
+    parentInfo.setCategory(clientCategory);
+
+    service = resolveService(info, parentInfo);
+    assertEquals(serverCategory, service.getModuleInfo().getCategory());
+  }
+
+  @Test
+  public void testComponentVersionInheritsFromService() throws AmbariException {
+    //inherits from service
+    ServiceInfo serviceInfo = new ServiceInfo();
+    ComponentInfo componentInfo = new ComponentInfo();
+    serviceInfo.setComponents(Lists.newArrayList(componentInfo));
+    serviceInfo.setVersion("1.1");
+    ServiceModule service = resolveService(serviceInfo, new ServiceInfo());
+
+    assertEquals("1.1", service.getModuleInfo().getComponents().get((0)).getVersion());
+
+    //component version overrides service version
+    componentInfo.setVersion("1.1-999");
+    service = resolveService(serviceInfo, new ServiceInfo());
+
+    assertEquals("1.1-999", service.getModuleInfo().getComponents().get((0)).getVersion());
+
+    //component is inherited from parent service, component version in parent service is set from service version
+    //the component version is overwritten by child service version
+    ServiceInfo parentServiceInfo = new ServiceInfo();
+    ComponentInfo parentComponentInfo = new ComponentInfo();
+    parentServiceInfo.setComponents(Lists.newArrayList(parentComponentInfo));
+    parentServiceInfo.setVersion("1.0-parent");
+    serviceInfo.setComponents(null);
+    service = resolveService(serviceInfo, parentServiceInfo);
+
+    assertEquals("1.1", service.getModuleInfo().getComponents().get((0)).getVersion());
+
+    //component is inherited from parent service, the component version is overwritten by child service version
+    parentServiceInfo = new ServiceInfo();
+    parentComponentInfo = new ComponentInfo();
+    parentServiceInfo.setComponents(Lists.newArrayList(parentComponentInfo));
+    parentComponentInfo.setVersion("1.0-parent");
+    serviceInfo.setComponents(null);
+    service = resolveService(serviceInfo, parentServiceInfo);
+
+    assertEquals("1.0-parent", parentServiceInfo.getComponents().get((0)).getVersion());
+    assertEquals("1.1", service.getModuleInfo().getComponents().get((0)).getVersion());
+
+    //component is inherited from parent service, the version is overwritten
+    serviceInfo.setComponents(Lists.newArrayList(componentInfo));
+    service = resolveService(serviceInfo, parentServiceInfo);
+
+    assertEquals("1.1-999", service.getModuleInfo().getComponents().get((0)).getVersion());
+  }
+
+
   private ServiceModule createServiceModule(ServiceInfo serviceInfo) {
     String configType = "type1";
 
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/ServiceInfoTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/ServiceInfoTest.java
index 154c870..c1be358 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/ServiceInfoTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/ServiceInfoTest.java
@@ -159,6 +159,66 @@ public class ServiceInfoTest {
   }
 
   @Test
+  public void testServiceCategory() throws Exception {
+
+    String serviceInfoXml = "<metainfo>\n" +
+        "  <schemaVersion>2.0</schemaVersion>\n" +
+        "  <services>\n" +
+        "    <service>\n" +
+        "      <name>CLIENT_SERVICE_NAME</name>\n" +
+        "      <category>CLIENT</category>\n" +
+        "    </service>\n" +
+        "    <service>\n" +
+        "      <name>SERVER_SERVICE_NAME</name>\n" +
+        "      <category>SERVER</category>\n" +
+        "    </service>\n" +
+        "    <service>\n" +
+        "      <name>DEFAULT</name>\n" +
+        "    </service>\n" +
+        "  </services>\n" +
+        "</metainfo>\n";
+
+    Map<String, ServiceInfo> serviceInfoMap = getServiceInfo(serviceInfoXml);
+
+
+    assertEquals(ServiceCategory.CLIENT, serviceInfoMap.get("CLIENT_SERVICE_NAME").getCategory());
+    assertEquals(ServiceCategory.SERVER, serviceInfoMap.get("SERVER_SERVICE_NAME").getCategory());
+    //the default LEGACY value is set in stack manager, not the service info, this is needed for inheritance
+    assertNull(serviceInfoMap.get("DEFAULT").getCategory());
+  }
+
+  @Test
+  public void testComponentVersion() throws Exception {
+
+    String serviceInfoXml = "<metainfo>\n" +
+        "  <schemaVersion>2.0</schemaVersion>\n" +
+        "  <services>\n" +
+        "    <service>\n" +
+        "      <name>service_name</name>\n" +
+        "      <version>service-version</version>" +
+        "      <components>\n" +
+        "        <component>\n" +
+        "          <name>HBASE_MASTER</name>\n" +
+        "          <version>component-version</version>\n" +
+        "        </component>\n" +
+        "        <component>\n" +
+        "          <name>HBASE_MASTER_2</name>\n" +
+        "        </component>\n" +
+      "        </components>\n" +
+        "    </service>\n" +
+        "  </services>\n" +
+        "</metainfo>\n";
+
+    Map<String, ServiceInfo> serviceInfoMap = getServiceInfo(serviceInfoXml);
+
+
+    assertEquals("service-version", serviceInfoMap.get("service_name").getVersion());
+    assertEquals("component-version", serviceInfoMap.get("service_name").getComponentByName("HBASE_MASTER").getVersion());
+    //the component version is set to service version in stack module, not the service module
+    assertNull(serviceInfoMap.get("service_name").getComponentByName("HBASE_MASTER_2").getVersion());
+  }
+
+  @Test
   public void testSelectionField() throws Exception {
     String serviceInfoXmlDeprecated = "<metainfo>\n" +
         "  <schemaVersion>2.0</schemaVersion>\n" +
diff --git a/ambari-server/src/test/resources/stacks/HDP/2.0.6.1/services/HBASE/metainfo.xml b/ambari-server/src/test/resources/stacks/HDP/2.0.6.1/services/HBASE/metainfo.xml
index c789524..82cd0fa 100644
--- a/ambari-server/src/test/resources/stacks/HDP/2.0.6.1/services/HBASE/metainfo.xml
+++ b/ambari-server/src/test/resources/stacks/HDP/2.0.6.1/services/HBASE/metainfo.xml
@@ -20,6 +20,7 @@
   <services>
     <service>
       <name>HBASE</name>
+      <category>CLIENT</category>
       <comment>Non-relational distributed database and centralized service for configuration management &amp; synchronization</comment>
       <version>0.95.2.2.0.6.1</version>
     </service>
diff --git a/ambari-server/src/test/resources/stacks/HDP/2.0.6/services/HBASE/metainfo.xml b/ambari-server/src/test/resources/stacks/HDP/2.0.6/services/HBASE/metainfo.xml
index bedeecc..5b5cb74 100644
--- a/ambari-server/src/test/resources/stacks/HDP/2.0.6/services/HBASE/metainfo.xml
+++ b/ambari-server/src/test/resources/stacks/HDP/2.0.6/services/HBASE/metainfo.xml
@@ -22,12 +22,13 @@
       <name>HBASE</name>
       <comment>Non-relational distributed database and centralized service for configuration management &amp; synchronization</comment>
       <version>0.95.2.2.0.6.0</version>
-
+      <category>SERVER</category>
       <components>
         <component>
           <name>HBASE_MASTER</name>
           <category>MASTER</category>
           <cardinality>1</cardinality>
+          <version>0.95.2.2.0.6.0-hbase-master</version>
           <dependencies>
             <dependency>
               <name>HDFS/HDFS_CLIENT</name>

-- 
To stop receiving notification emails like this one, please contact
dsen@apache.org.