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

[ambari] 13/15: AMBARI-22449. Improved service/component dependency support (amagyar)

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

amagyar pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git

commit ebf3630e5759d39bdc9e4ea0dd3b97217b5248b2
Author: Attila Magyar <am...@hortonworks.com>
AuthorDate: Fri Jan 5 08:53:59 2018 +0100

    AMBARI-22449. Improved service/component dependency support (amagyar)
---
 .../controller/StackServiceComponentResponse.java  | 10 ++++++
 .../StackServiceComponentResourceProvider.java     |  9 ++++-
 .../apache/ambari/server/state/ComponentInfo.java  | 15 ++++++++
 .../common-services/HDFS/2.1.0.2.0/metainfo.xml    |  1 +
 .../common-services/HDFS/3.0.0.3.0/metainfo.xml    |  1 +
 ambari-web/app/controllers/main/host/details.js    |  8 ++---
 .../app/controllers/wizard/step8_controller.js     |  8 +++--
 ambari-web/app/mappers/stack_service_mapper.js     |  1 +
 ambari-web/app/models/stack_service_component.js   | 33 +++++++++++++++++
 .../test/controllers/main/host/details_test.js     | 23 +++++++++---
 ambari-web/test/controllers/wizard/step8_test.js   | 41 ++++++++++++----------
 11 files changed, 117 insertions(+), 33 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 75fac6e..8797f03 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
@@ -118,6 +118,11 @@ public class StackServiceComponentResponse {
   private String reassignAllowed;
 
   /**
+   * @see ComponentInfo#componentType
+   */
+  private String componentType;
+
+  /**
    * Constructor.
    *
    * @param component
@@ -139,6 +144,7 @@ public class StackServiceComponentResponse {
     bulkCommandMasterComponentName = getBulkCommandsMasterComponentName(component);
     reassignAllowed = component.getReassignAllowed();
     rollingRestartSupported = component.getRollingRestartSupported();
+    componentType = component.getComponentType();
 
     // the custom command names defined for this component
     List<CustomCommandDefinition> definitions = component.getCustomCommands();
@@ -511,6 +517,10 @@ public class StackServiceComponentResponse {
     return bulkCommandMasterComponentName == null ? "":bulkCommandMasterComponentName;
   }
 
+  public String getComponentType() {
+    return componentType;
+  }
+
   /**
    * Interface to help correct Swagger documentation generation
    */
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 a221248..933dcb7 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
@@ -104,6 +104,10 @@ public class StackServiceComponentResourceProvider extends
   private static final String AUTO_DEPLOY_LOCATION_ID = PropertyHelper.getPropertyId(
       "auto_deploy", "location");
 
+  private static final String COMPONENT_TYPE = PropertyHelper.getPropertyId(
+    "StackServiceComponents", "component_type");
+
+
   /**
    * The key property ids for a StackServiceComponent resource.
    */
@@ -137,7 +141,8 @@ public class StackServiceComponentResourceProvider extends
       RECOVERY_ENABLED,
       ROLLING_RESTART_SUPPORTED,
       AUTO_DEPLOY_ENABLED_ID,
-      AUTO_DEPLOY_LOCATION_ID);
+      AUTO_DEPLOY_LOCATION_ID,
+      COMPONENT_TYPE);
 
   protected StackServiceComponentResourceProvider(AmbariManagementController managementController) {
     super(Type.StackServiceComponent, propertyIds, keyPropertyIds, managementController);
@@ -225,6 +230,8 @@ public class StackServiceComponentResourceProvider extends
 
       setResourceProperty(resource, ROLLING_RESTART_SUPPORTED, response.isRollingRestartSupported(),  requestedIds);
 
+      setResourceProperty(resource, COMPONENT_TYPE, response.getComponentType(),  requestedIds);
+
       AutoDeployInfo autoDeployInfo = response.getAutoDeploy();
       if (autoDeployInfo != null) {
         setResourceProperty(resource, AUTO_DEPLOY_ENABLED_ID,
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 d361a29..a4bac56 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
@@ -145,6 +145,12 @@ public class ComponentInfo {
   @XmlElement(name="customFolder")
   private String customFolder;
 
+  /**
+   * Optional component type like HCFS_CLIENT.
+   * HCFS_CLIENT indicates compatibility with HDFS_CLIENT
+   */
+  private String componentType;
+
   public ComponentInfo() {
   }
 
@@ -173,6 +179,7 @@ public class ComponentInfo {
     reassignAllowed = prototype.reassignAllowed;
     customFolder = prototype.customFolder;
     rollingRestartSupported = prototype.rollingRestartSupported;
+    componentType = prototype.componentType;
   }
 
   public String getName() {
@@ -435,6 +442,14 @@ public class ComponentInfo {
     this.customFolder = customFolder;
   }
 
+  public String getComponentType() {
+    return componentType;
+  }
+
+  public void setComponentType(String componentType) {
+    this.componentType = componentType;
+  }
+
   @Override
   public boolean equals(Object o) {
     if (this == o) return true;
diff --git a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/metainfo.xml b/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/metainfo.xml
index 6bbb583..71e487c 100644
--- a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/metainfo.xml
+++ b/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/metainfo.xml
@@ -157,6 +157,7 @@
           <name>HDFS_CLIENT</name>
           <displayName>HDFS Client</displayName>
           <category>CLIENT</category>
+          <componentType>HCFS_CLIENT</componentType>
           <cardinality>1+</cardinality>
           <versionAdvertised>true</versionAdvertised>
           <commandScript>
diff --git a/ambari-server/src/main/resources/common-services/HDFS/3.0.0.3.0/metainfo.xml b/ambari-server/src/main/resources/common-services/HDFS/3.0.0.3.0/metainfo.xml
index 0c629f3..c1dd4af 100644
--- a/ambari-server/src/main/resources/common-services/HDFS/3.0.0.3.0/metainfo.xml
+++ b/ambari-server/src/main/resources/common-services/HDFS/3.0.0.3.0/metainfo.xml
@@ -157,6 +157,7 @@
           <name>HDFS_CLIENT</name>
           <displayName>HDFS Client</displayName>
           <category>CLIENT</category>
+          <componentType>HCFS_CLIENT</componentType>
           <cardinality>1+</cardinality>
           <versionAdvertised>true</versionAdvertised>
           <commandScript>
diff --git a/ambari-web/app/controllers/main/host/details.js b/ambari-web/app/controllers/main/host/details.js
index e52eec0..9e05f2a 100644
--- a/ambari-web/app/controllers/main/host/details.js
+++ b/ambari-web/app/controllers/main/host/details.js
@@ -3005,9 +3005,6 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
     opt = opt || {};
     opt.scope = opt.scope || '*';
     var installedComponents;
-    var dependencies = App.StackServiceComponent.find(componentName).get('dependencies');
-    dependencies = opt.scope === '*' ? dependencies : dependencies.filterProperty('scope', opt.scope);
-    if (dependencies.length == 0) return [];
     switch (opt.scope) {
       case 'host':
         Em.assert("You should pass at least `hostName` or `installedComponents` to options.", opt.hostName || opt.installedComponents);
@@ -3018,9 +3015,8 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
         installedComponents = opt.installedComponents || App.HostComponent.find().mapProperty('componentName').uniq();
         break;
     }
-    return dependencies.filter(function (dependency) {
-      return !installedComponents.contains(dependency.componentName);
-    }).mapProperty('componentName');
+    var component = App.StackServiceComponent.find(componentName);
+    return component.missingDependencies(installedComponents, opt);
   },
 
   /**
diff --git a/ambari-web/app/controllers/wizard/step8_controller.js b/ambari-web/app/controllers/wizard/step8_controller.js
index bd3c36f..2d9f4d2 100644
--- a/ambari-web/app/controllers/wizard/step8_controller.js
+++ b/ambari-web/app/controllers/wizard/step8_controller.js
@@ -1181,13 +1181,15 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz
   },
 
   getClientsMap: function (flag) {
-    var clientNames = App.StackServiceComponent.find().filterProperty('isClient').mapProperty('componentName'),
+    var clients = App.StackServiceComponent.find().filterProperty('isClient'),
       clientsMap = {},
       dependedComponents = flag ? App.StackServiceComponent.find().filterProperty(flag) : App.StackServiceComponent.find();
-    clientNames.forEach(function (clientName) {
+    clients.forEach(function (client) {
+      var clientName = client.get('componentName');
       clientsMap[clientName] = Em.A([]);
       dependedComponents.forEach(function (component) {
-        if (component.get('dependencies').mapProperty('componentName').contains(clientName)) clientsMap[clientName].push(component.get('componentName'));
+        if (component.dependsOn(client))
+          clientsMap[clientName].push(component.get('componentName'));
       });
       if (!clientsMap[clientName].length) delete clientsMap[clientName];
     });
diff --git a/ambari-web/app/mappers/stack_service_mapper.js b/ambari-web/app/mappers/stack_service_mapper.js
index 368a182..9b55a05 100644
--- a/ambari-web/app/mappers/stack_service_mapper.js
+++ b/ambari-web/app/mappers/stack_service_mapper.js
@@ -64,6 +64,7 @@ App.stackServiceMapper = App.QuickDataMapper.create({
     rolling_restart_supported: 'rolling_restart_supported',
     is_master: 'is_master',
     is_client: 'is_client',
+    component_type: 'component_type',
     stack_name: 'stack_name',
     stack_version: 'stack_version',
     stack_service_id: 'service_name',
diff --git a/ambari-web/app/models/stack_service_component.js b/ambari-web/app/models/stack_service_component.js
index 27aa8aa..d4e3446 100644
--- a/ambari-web/app/models/stack_service_component.js
+++ b/ambari-web/app/models/stack_service_component.js
@@ -38,6 +38,7 @@ App.StackServiceComponent = DS.Model.extend({
   rollingRestartSupported: DS.attr('boolean'),
   isMaster: DS.attr('boolean'),
   isClient: DS.attr('boolean'),
+  componentType: DS.attr('string'),
   stackName: DS.attr('string'),
   stackVersion: DS.attr('string'),
   stackService: DS.belongsTo('App.StackService'),
@@ -61,6 +62,38 @@ App.StackServiceComponent = DS.Model.extend({
     return numberUtils.getCardinalityValue(this.get('cardinality'), true);
   }.property('cardinality'),
 
+  /**
+   * Check if the given component is compatible with this one. Having the same name or componentType indicates compatibility.
+   **/
+  dependsOn: function(aStackServiceComponent, opt) {
+    return this.get('dependencies').some(function(each) {
+      return aStackServiceComponent.compatibleWith(App.StackServiceComponent.find(each.componentName));
+    });
+  },
+
+  compatibleWith: function(aStackServiceComponent) {
+    return this.get('componentName') === aStackServiceComponent.get('componentName')
+      || (this.get('componentType') && this.get('componentType') === aStackServiceComponent.get('componentType'));
+  },
+
+  /**
+   * Collect dependencies which are required by this component but not installed.
+   * A compatible installed component (e.g.: componentType=HCFS_CLIENT) is not considered as a missing dependency.
+   **/
+  missingDependencies: function(installedComponents, opt) {
+    opt = opt || {};
+    opt.scope = opt.scope || '*';
+    var dependencies = this.get('dependencies');
+    dependencies = opt.scope === '*' ? dependencies : dependencies.filterProperty('scope', opt.scope);
+    if (dependencies.length == 0) return [];
+    installedComponents = installedComponents.map(function(each) { return App.StackServiceComponent.find(each); });
+    return dependencies.filter(function (dependency) {
+      return !installedComponents.some(function(each) {
+        return each.compatibleWith(App.StackServiceComponent.find(dependency.componentName));
+      });
+    }).mapProperty('componentName');
+  },
+
   /** @property {Boolean} isRequired - component required to install **/
   isRequired: Em.computed.gt('minToInstall', 0),
 
diff --git a/ambari-web/test/controllers/main/host/details_test.js b/ambari-web/test/controllers/main/host/details_test.js
index f9df36d..e9490fb 100644
--- a/ambari-web/test/controllers/main/host/details_test.js
+++ b/ambari-web/test/controllers/main/host/details_test.js
@@ -3566,30 +3566,43 @@ describe('App.MainHostDetailsController', function () {
 
     it("no dependencies", function () {
       var opt = {scope: '*'};
-      this.mock.returns(Em.Object.create({
-        dependencies: []
+      this.mock.withArgs('C1').returns(App.StackServiceComponent.createRecord({
+          'dependencies': []
       }));
       expect(controller.checkComponentDependencies('C1', opt)).to.be.empty;
     });
     it("dependecies already installed", function () {
       var opt = {scope: '*', installedComponents: ['C2']};
-      this.mock.returns(Em.Object.create({
+      this.mock.withArgs('C1').returns(App.StackServiceComponent.createRecord({
         dependencies: [{componentName: 'C2'}]
       }));
+      this.mock.withArgs('C2').returns(App.StackServiceComponent.createRecord({ componentName: 'C2' }));
       expect(controller.checkComponentDependencies('C1', opt)).to.be.empty;
     });
     it("dependecies should be added", function () {
       var opt = {scope: '*', installedComponents: ['C2']};
-      this.mock.returns(Em.Object.create({
+      this.mock.withArgs('C1').returns(App.StackServiceComponent.createRecord({
         dependencies: [{componentName: 'C3'}]
       }));
+      this.mock.withArgs('C2').returns(App.StackServiceComponent.createRecord({ componentName: 'C2' }));
+      this.mock.withArgs('C3').returns(App.StackServiceComponent.createRecord({ componentName: 'C3' }));
       expect(controller.checkComponentDependencies('C1', opt)).to.eql(['C3']);
     });
+    it("dependecies already installed by component type", function () {
+      var opt = {scope: '*', installedComponents: ['C3']};
+      this.mock.withArgs('C1').returns(App.StackServiceComponent.createRecord({
+        dependencies: [{componentName: 'C2'}]
+      }));
+      this.mock.withArgs('C2').returns(App.StackServiceComponent.createRecord({ componentName: 'C2', componentType: 'HCFS_CLIENT' }));
+      this.mock.withArgs('C3').returns(App.StackServiceComponent.createRecord({ componentName: 'C3', componentType: 'HCFS_CLIENT' }));
+      expect(controller.checkComponentDependencies('C1', opt)).to.be.empty;
+    });
     it("scope is host", function () {
       var opt = {scope: 'host', hostName: 'host1'};
-      this.mock.returns(Em.Object.create({
+      this.mock.withArgs('C1').returns(App.StackServiceComponent.createRecord({
         dependencies: [{componentName: 'C3', scope: 'host'}]
       }));
+      this.mock.withArgs('C3').returns(App.StackServiceComponent.createRecord({ componentName: 'C3' }));
       expect(controller.checkComponentDependencies('C1', opt)).to.eql(['C3']);
     });
   });
diff --git a/ambari-web/test/controllers/wizard/step8_test.js b/ambari-web/test/controllers/wizard/step8_test.js
index 2a52598..22bcf96 100644
--- a/ambari-web/test/controllers/wizard/step8_test.js
+++ b/ambari-web/test/controllers/wizard/step8_test.js
@@ -2086,8 +2086,9 @@ describe('App.WizardStep8Controller', function () {
     ];
 
     before(function () {
-      sinon.stub(App.StackServiceComponent, 'find').returns([
-        Em.Object.create({
+      var mock = sinon.stub(App.StackServiceComponent, 'find');
+      var components = [
+        App.StackServiceComponent.createRecord({
           componentName: 'c0',
           isMaster: true,
           dependencies: [
@@ -2105,7 +2106,7 @@ describe('App.WizardStep8Controller', function () {
             }
           ]
         }),
-        Em.Object.create({
+        App.StackServiceComponent.createRecord({
           componentName: 'c1',
           isMaster: true,
           dependencies: [
@@ -2123,7 +2124,7 @@ describe('App.WizardStep8Controller', function () {
             }
           ]
         }),
-        Em.Object.create({
+        App.StackServiceComponent.createRecord({
           componentName: 'c2',
           isMaster: true,
           dependencies: [
@@ -2141,14 +2142,14 @@ describe('App.WizardStep8Controller', function () {
             }
           ]
         }),
-        Em.Object.create({
+        App.StackServiceComponent.createRecord({
           componentName: 'c3',
           isMaster: true,
           dependencies: []
         }),
-        Em.Object.create({
+        App.StackServiceComponent.createRecord({
           componentName: 'c4',
-          isSlave: true,
+          componentCategory: 'SLAVE',
           dependencies: [
             {
               componentName: 'c1'
@@ -2164,9 +2165,9 @@ describe('App.WizardStep8Controller', function () {
             }
           ]
         }),
-        Em.Object.create({
+        App.StackServiceComponent.createRecord({
           componentName: 'c5',
-          isSlave: true,
+          componentCategory: 'SLAVE',
           dependencies: [
             {
               componentName: 'c4'
@@ -2182,9 +2183,9 @@ describe('App.WizardStep8Controller', function () {
             }
           ]
         }),
-        Em.Object.create({
+        App.StackServiceComponent.createRecord({
           componentName: 'c6',
-          isSlave: true,
+          componentCategory: 'SLAVE',
           dependencies: [
             {
               componentName: 'c1'
@@ -2200,12 +2201,12 @@ describe('App.WizardStep8Controller', function () {
             }
           ]
         }),
-        Em.Object.create({
+        App.StackServiceComponent.createRecord({
           componentName: 'c7',
-          isSlave: true,
+          componentCategory: 'SLAVE',
           dependencies: []
         }),
-        Em.Object.create({
+        App.StackServiceComponent.createRecord({
           componentName: 'c8',
           isClient: true,
           dependencies: [
@@ -2223,7 +2224,7 @@ describe('App.WizardStep8Controller', function () {
             }
           ]
         }),
-        Em.Object.create({
+        App.StackServiceComponent.createRecord({
           componentName: 'c9',
           isClient: true,
           dependencies: [
@@ -2241,7 +2242,7 @@ describe('App.WizardStep8Controller', function () {
             }
           ]
         }),
-        Em.Object.create({
+        App.StackServiceComponent.createRecord({
           componentName: 'c10',
           isClient: true,
           dependencies: [
@@ -2259,12 +2260,16 @@ describe('App.WizardStep8Controller', function () {
             }
           ]
         }),
-        Em.Object.create({
+        App.StackServiceComponent.createRecord({
           componentName: 'c11',
           isClient: true,
           dependencies: []
         })
-      ]);
+      ];
+      components.forEach(function(component) {
+        mock.withArgs(component.get('componentName')).returns(component);
+      });
+      mock.returns(components);
     });
 
     after(function () {

-- 
To stop receiving notification emails like this one, please contact
"commits@ambari.apache.org" <co...@ambari.apache.org>.