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

ambari git commit: AMBARI-14337. Add service wizard hangs up on Customize Services step on secure cluster after RU. (jaimin)

Repository: ambari
Updated Branches:
  refs/heads/trunk 7bb890075 -> 7e52e66d5


AMBARI-14337. Add service wizard hangs up on Customize Services step on secure cluster after RU. (jaimin)


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

Branch: refs/heads/trunk
Commit: 7e52e66d5f2c118f822d4b0d9e1bc9eb6043663c
Parents: 7bb8900
Author: Jaimin Jetly <ja...@hortonworks.com>
Authored: Thu Dec 10 16:08:54 2015 -0800
Committer: Jaimin Jetly <ja...@hortonworks.com>
Committed: Thu Dec 10 16:09:46 2015 -0800

----------------------------------------------------------------------
 .../app/mixins/wizard/addSecurityConfigs.js     |   3 +-
 ambari-web/app/utils/object_utils.js            | 142 ++++++
 ambari-web/test/utils/object_utils_test.js      | 442 +++++++++++++++++++
 3 files changed, 586 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/7e52e66d/ambari-web/app/mixins/wizard/addSecurityConfigs.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins/wizard/addSecurityConfigs.js b/ambari-web/app/mixins/wizard/addSecurityConfigs.js
index b010fcf..5b2bce8 100644
--- a/ambari-web/app/mixins/wizard/addSecurityConfigs.js
+++ b/ambari-web/app/mixins/wizard/addSecurityConfigs.js
@@ -17,6 +17,7 @@
  */
 
 var App = require('app');
+var objectUtils = require('utils/object_utils');
 
 /**
  * Mixin for loading and setting secure configs
@@ -70,7 +71,7 @@ App.AddSecurityConfigs = Em.Mixin.create({
     this.loadStackDescriptorConfigs().then(function(data) {
       var stackArtifacts = data;
       self.loadClusterDescriptorConfigs().then(function(clusterArtifacts) {
-        dfd.resolve(self.createServicesStackDescriptorConfigs($.extend(true, {}, stackArtifacts, clusterArtifacts)));
+        dfd.resolve(self.createServicesStackDescriptorConfigs(objectUtils.deepMerge(data, clusterArtifacts)));
       }, function() {
         dfd.resolve(self.createServicesStackDescriptorConfigs(stackArtifacts));
       });

http://git-wip-us.apache.org/repos/asf/ambari/blob/7e52e66d/ambari-web/app/utils/object_utils.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/object_utils.js b/ambari-web/app/utils/object_utils.js
index 294fb95..2f68631 100644
--- a/ambari-web/app/utils/object_utils.js
+++ b/ambari-web/app/utils/object_utils.js
@@ -18,6 +18,17 @@
 
 var stringUtils = require('utils/string_utils');
 
+var types = {
+  'get': function(prop) {
+    return Object.prototype.toString.call(prop);
+  },
+  'object': '[object Object]',
+  'array': '[object Array]',
+  'string': '[object String]',
+  'boolean': '[object Boolean]',
+  'number': '[object Number]'
+};
+
 module.exports = {
 
   isChild: function(obj)
@@ -162,5 +173,136 @@ module.exports = {
       return leaf;
     }
     return r(obj,'');
+  },
+
+  /**
+   *
+   * @param {object|array|object[]} target
+   * @param {object|array|object[]} source
+   * @param {function} handler
+   * @returns {object|array|object[]}
+   */
+  deepMerge: function(target, source, handler) {
+    if (typeof target !== 'object' || typeof source !== 'object') return target;
+    var handlerOpts = Array.prototype.slice.call(arguments, 3);
+    var isArray = Em.isArray(source);
+    var ret = handler && typeof handler.apply(this, [target, source].concat(handlerOpts)) !== 'undefined' ?
+          handler(target, source) :
+          isArray ? [] : {};
+    var self = this;
+
+    // handle array
+    if (isArray) {
+      target = target || [];
+      ret = ret.concat(target);
+
+      if (types.object === types.get(target[0])) {
+        ret = self.smartArrayObjectMerge(target, source);
+      } else {
+        for(var i = 0; i < source.length; i++) {
+          if (typeof ret[i] === 'undefined') {
+            ret[i] = source[i];
+          } else if (typeof source[i] === 'object') {
+            ret[i] = this.deepMerge(target[i], source[i], handler, target, source);
+          } else {
+            if (target.indexOf(source[i]) === -1) {
+              ret.push(source[i]);
+            }
+          }
+        }
+      }
+    } else {
+      if (target && typeof target === 'object') {
+        Em.keys(target).forEach(function(key) {
+          ret[key] = target[key];
+        });
+      }
+      Em.keys(source).forEach(function(key) {
+        // handle value which is not Array or Object
+        if (typeof source[key] !== 'object' || !source[key]) {
+          ret[key] = source[key];
+        } else {
+          if (!target[key]) {
+            ret[key] = source[key];
+          } else {
+            ret[key] = self.deepMerge(target[key], source[key], handler, target, source);
+          }
+        }
+      });
+    }
+
+    return ret;
+  },
+
+  /**
+   * Find objects by index key (@see detectIndexedKey) and merge them.
+   *
+   * @param {object[]} target
+   * @param {object[]} source
+   * @returns {object[]}
+   */
+  smartArrayObjectMerge: function(target, source) {
+    // keep the first object and take all keys that contains primitive value
+    var id = this.detectIndexedKey(target);
+    var self = this;
+    // when uniq key not found let's merge items by the key itself
+    if (!id) {
+      source.forEach(function(obj) {
+        Em.keys(obj).forEach(function(objKey) {
+          var ret = self.objectByRoot(objKey, target);
+          if (!Em.isNone(ret)) {
+            if ([types.object, types.array].contains(types.get(ret))) {
+              target[objKey] = self.deepMerge(obj[objKey], ret);
+            } else {
+              target[objKey] = ret;
+            }
+          } else {
+            var _obj = {};
+            _obj[objKey] = obj[objKey];
+            target.push(_obj);
+          }
+        });
+      });
+      return target;
+    }
+
+    return target.mapProperty(id).concat(source.mapProperty(id)).uniq().map(function(value) {
+      if (!target.someProperty(id, value)) {
+        return source.findProperty(id, value);
+      } else if (!source.someProperty(id, value)) {
+        return target.findProperty(id, value);
+      }
+      return self.deepMerge(target.findProperty(id, value), source.findProperty(id, value));
+    });
+  },
+
+  /**
+   * Determines key with uniq value. This key will be used to find correct objects in target and source to merge.
+   *
+   * @param {object} target
+   * @returns {string|undefined}
+   */
+  detectIndexedKey: function(target) {
+    var keys = Em.keys(target[0]).map(function(key) {
+      if ([types.object, types.array].contains(types.get(target[0][key]))) {
+        return null;
+      }
+      return key;
+    }).compact();
+    return keys.filter(function(key) {
+      var values = target.mapProperty(key);
+      return values.length === values.uniq().length;
+    })[0];
+  },
+
+  /**
+   *
+   * @param {string} rootKey
+   * @param {object[]} target
+   */
+  objectByRoot: function(rootKey, target) {
+    return target.map(function(item) {
+      return item[rootKey] || null;
+    }).compact()[0];
   }
 };

http://git-wip-us.apache.org/repos/asf/ambari/blob/7e52e66d/ambari-web/test/utils/object_utils_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/utils/object_utils_test.js b/ambari-web/test/utils/object_utils_test.js
index 1c84d78..35e7689 100644
--- a/ambari-web/test/utils/object_utils_test.js
+++ b/ambari-web/test/utils/object_utils_test.js
@@ -178,4 +178,446 @@ describe('utils/object_utils', function() {
     });
   });
 
+  describe('#deepMerge', function() {
+    var tests = [
+      {
+        target: {
+          a: [
+            {
+              c: 3
+            }
+          ]
+        },
+        source: {
+          a: [
+            {
+              b: 2
+            }
+          ]
+        },
+        e: {
+          a: [
+            {
+              c: 3
+            },
+            {
+              b: 2
+            }
+          ]
+        }
+      },
+      {
+        target: {
+          a: {}
+        },
+        source: {
+          a: {
+            b: 2,
+            c: [1,2,3]
+          }
+        },
+        e: {
+          a: {
+            b: 2,
+            c: [1,2,3]
+          }
+        }
+      },
+      {
+        target: {
+          artifact_data: {
+            services: [
+              {
+                name: "HIVE",
+                configurations: [
+                  {
+                    "hive-site": {
+                      hive_prop1: "hive_val1"
+                    }
+                  }
+                ]
+              }
+            ]
+          }
+        },
+        source: {
+          artifact_data: {
+            services: [
+              {
+                name: "HDFS",
+                configurations: [
+                  {
+                    "hdfs-site": {
+                      hdfs_prop1: "hdfs_val1"
+                    }
+                  }
+                ]
+              }
+            ]
+          }
+        },
+        e: {
+          artifact_data: {
+            services: [
+              {
+                name: "HIVE",
+                configurations: [
+                  {
+                    "hive-site": {
+                      hive_prop1: "hive_val1"
+                    }
+                  }
+                ]
+              },
+              {
+                name: "HDFS",
+                configurations: [
+                  {
+                    "hdfs-site": {
+                      hdfs_prop1: "hdfs_val1"
+                    }
+                  }
+                ]
+              }
+            ]
+          }
+        }
+      },
+      {
+        source: {
+          "artifact_data" : {
+            "identities" : [
+              {
+                "principal" : {
+                  "value" : "HTTP/_HOST@${realm}",
+                  "type" : "service"
+                },
+                "name" : "spnego",
+                "keytab" : {
+                  "file" : "${keytab_dir}/spnego.service.keytab",
+                  "owner" : {
+                    "name" : "root",
+                    "access" : "r"
+                  },
+                  "group" : {
+                    "name" : "${cluster-env/user_group}",
+                    "access" : "r"
+                  }
+                }
+              },
+              {
+                "principal" : {
+                  "value" : "${cluster-env/smokeuser}-----@${realm}",
+                  "local_username" : "${cluster-env/smokeuser}",
+                  "configuration" : "cluster-env/smokeuser_principal_name",
+                  "type" : "user"
+                },
+                "name" : "smokeuser",
+                "keytab" : {
+                  "file" : "${keytab_dir}/smokeuser.headless.keytab",
+                  "owner" : {
+                    "name" : "${cluster-env/smokeuser}",
+                    "access" : "r"
+                  },
+                  "configuration" : "cluster-env/smokeuser_keytab",
+                  "group" : {
+                    "name" : "${cluster-env/user_group}",
+                    "access" : "r"
+                  }
+                }
+              }
+            ]
+          }
+        },
+        target: {
+          "artifact_data" : {
+            "identities" : [
+              {
+                "principal" : {
+                  "value" : "${cluster-env/smokeuser}@${realm}",
+                  "local_username" : "${cluster-env/smokeuser}",
+                  "configuration" : "cluster-env/smokeuser_principal_name",
+                  "type" : "user"
+                },
+                "name" : "smokeuser",
+                "keytab" : {
+                  "file" : "${keytab_dir}/smokeuser.headless.keytab",
+                  "owner" : {
+                    "name" : "${cluster-env/smokeuser}",
+                    "access" : "r"
+                  },
+                  "configuration" : "cluster-env/smokeuser_keytab",
+                  "group" : {
+                    "name" : "${cluster-env/user_group}",
+                    "access" : "r"
+                  }
+                }
+              },
+              {
+                "principal" : {
+                  "value" : "HTTP/_HOST@${realm}",
+                  "local_username" : null,
+                  "configuration" : null,
+                  "type" : "service"
+                },
+                "name" : "spnego",
+                "keytab" : {
+                  "file" : "${keytab_dir}/spnego.service.keytab",
+                  "owner" : {
+                    "name" : "root",
+                    "access" : "r"
+                  },
+                  "configuration" : null,
+                  "group" : {
+                    "name" : "${cluster-env/user_group}",
+                    "access" : "d"
+                  }
+                }
+              },
+              {
+                "name": "anotherOne"
+              }
+            ]
+          }
+        },
+        e: {
+          "artifact_data" : {
+            "identities" : [
+              {
+                "principal" : {
+                  "value" : "${cluster-env/smokeuser}-----@${realm}",
+                  "local_username" : "${cluster-env/smokeuser}",
+                  "configuration" : "cluster-env/smokeuser_principal_name",
+                  "type" : "user"
+                },
+                "name" : "smokeuser",
+                "keytab" : {
+                  "file" : "${keytab_dir}/smokeuser.headless.keytab",
+                  "owner" : {
+                    "name" : "${cluster-env/smokeuser}",
+                    "access" : "r"
+                  },
+                  "configuration" : "cluster-env/smokeuser_keytab",
+                  "group" : {
+                    "name" : "${cluster-env/user_group}",
+                    "access" : "r"
+                  }
+                }
+              },
+              {
+                "principal" : {
+                  "value" : "HTTP/_HOST@${realm}",
+                  "local_username" : null,
+                  "configuration" : null,
+                  "type" : "service"
+                },
+                "name" : "spnego",
+                "keytab" : {
+                  "file" : "${keytab_dir}/spnego.service.keytab",
+                  "owner" : {
+                    "name" : "root",
+                    "access" : "r"
+                  },
+                  "configuration" : null,
+                  "group" : {
+                    "name" : "${cluster-env/user_group}",
+                    "access" : "r"
+                  }
+                }
+              },
+              {
+                "name": "anotherOne"
+              }
+            ]
+          }
+        }
+      }
+    ];
+
+    tests.forEach(function(test) {
+      it("Should merge objects `{0}`, `{1}`".format(JSON.stringify(test.target), JSON.stringify(test.source)), function() {
+        expect(objectUtils.deepMerge(test.target, test.source, test.handler)).to.be.eql(test.e);
+      });
+    });
+  });
+
+  describe('#detectIndexedKey', function() {
+    var tests = [
+      {
+        target: [
+          {
+            a: 1,
+            b: []
+          },
+          {
+            a: 3,
+            b: 2
+          },
+          {
+            a: 2,
+            b: {}
+          }
+        ],
+        e: 'a',
+        m: 'should detect uniq key as `a`'
+      },
+      {
+        target: [
+          {
+            "principal" : {
+              "value" : "HTTP/_HOST@${realm}",
+              "local_username" : null,
+              "configuration" : null,
+              "type" : "service"
+            },
+            "name" : "spnego",
+            "keytab" : {
+              "file" : "${keytab_dir}/spnego.service.keytab",
+              "owner" : {
+                "name" : "root",
+                "access" : "r"
+              },
+              "configuration" : null,
+              "group" : {
+                "name" : "${cluster-env/user_group}",
+                "access" : "r"
+              }
+            }
+          },
+          {
+            "principal" : {
+              "value" : "${cluster-env/smokeuser}-${cluster_name}@${realm}",
+              "local_username" : "${cluster-env/smokeuser}",
+              "configuration" : "cluster-env/smokeuser_principal_name",
+              "type" : "user"
+            },
+            "name" : "smokeuser",
+            "keytab" : {
+              "file" : "${keytab_dir}/smokeuser.headless.keytab",
+              "owner" : {
+                "name" : "${cluster-env/smokeuser}",
+                "access" : "r"
+              },
+              "configuration" : "cluster-env/smokeuser_keytab",
+              "group" : {
+                "name" : "${cluster-env/user_group}",
+                "access" : "r"
+              }
+            }
+          }
+        ],
+        e: 'name',
+        m: 'should detect uniq key as `name`'
+      },
+    ];
+
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        expect(objectUtils.detectIndexedKey(test.target)).to.eql(test.e);
+      });
+    });
+  });
+
+  describe('#smartArrayObjectMerge', function() {
+    var tests = [
+      {
+        target: [
+          {
+            a: 2,
+            B: 2
+          }
+        ],
+        source: [
+          {
+            a: 3,
+            c: 4
+          },
+        ],
+        m: 'should merge {0} {1}, into {2}',
+        e: [
+          {
+            a: 2,
+            B: 2
+          },
+          {
+            a: 3,
+            c: 4
+          }
+        ]
+      },
+      {
+        target: [
+          {
+            a: 2,
+            B: 2
+          }
+        ],
+        source: [
+          {
+            a: 2,
+            B: 3,
+            b: 4
+          },
+          {
+            a: 3,
+            c: 4
+          }
+        ],
+        m: 'should merge {0} {1}, into {2}',
+        e: [
+          {
+            a: 2,
+            B: 3,
+            b: 4
+          },
+          {
+            a: 3,
+            c: 4
+          }
+        ]
+      },
+      {
+        target: [
+          {
+            "spark-defaults" : {
+              "spark.history.kerberos.enabled" : "true",
+              "spark.history.enabled" : "no"
+            }
+          }
+        ],
+        source: [
+          {
+            "spark-defaults" : {
+              "spark.history.kerberos.enabled" : "false"
+            }
+          },
+          {
+            "spark-site" : {
+              "spark.property" : "false"
+            }
+          }
+        ],
+        m: 'should merge {0} {1}, into {2}',
+        e: [
+          {
+            "spark-defaults" : {
+              "spark.history.kerberos.enabled" : "true",
+              "spark.history.enabled" : "no"
+            }
+          },
+          {
+            "spark-site" : {
+              "spark.property" : "false"
+            }
+          }
+        ]
+      }
+    ];
+
+    tests.forEach(function(test) {
+      it(test.m.format(JSON.stringify(test.target), JSON.stringify(test.source), JSON.stringify(test.e)), function() {
+        expect(objectUtils.smartArrayObjectMerge(test.target, test.source).toArray()).to.be.eql(test.e);
+      });
+    });
+  });
 });