You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tez.apache.org by sr...@apache.org on 2016/02/24 23:35:08 UTC

[31/45] tez git commit: TEZ-3069. Tez UI 2: Make error bar fully functional (sree)

TEZ-3069. Tez UI 2: Make error bar fully functional (sree)


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

Branch: refs/heads/TEZ-2980
Commit: 6d4bb2c6366e01971ce07460664e88fa8344eb30
Parents: 963e77a
Author: Sreenath Somarajapuram <sr...@apache.org>
Authored: Sat Jan 30 19:55:25 2016 +0530
Committer: Sreenath Somarajapuram <sr...@apache.org>
Committed: Thu Feb 25 03:32:52 2016 +0530

----------------------------------------------------------------------
 TEZ-2980-CHANGES.txt                            |   1 +
 .../src/main/webapp/app/adapters/abstract.js    |  15 +++
 tez-ui2/src/main/webapp/app/adapters/am.js      |   1 +
 tez-ui2/src/main/webapp/app/adapters/rm.js      |   1 +
 .../src/main/webapp/app/adapters/timeline.js    |   1 +
 .../src/main/webapp/app/components/error-bar.js | 109 +++++++++++++++++++
 .../main/webapp/app/controllers/application.js  |   2 +
 tez-ui2/src/main/webapp/app/entities/entity.js  |   2 +
 tez-ui2/src/main/webapp/app/routes/abstract.js  |  12 +-
 .../src/main/webapp/app/routes/am-pollster.js   |   6 +-
 tez-ui2/src/main/webapp/app/routes/app.js       |   3 +-
 .../src/main/webapp/app/routes/application.js   |   2 +-
 tez-ui2/src/main/webapp/app/routes/attempt.js   |   3 +-
 tez-ui2/src/main/webapp/app/routes/dag.js       |   3 +-
 .../main/webapp/app/routes/dag/index/index.js   |   5 +
 tez-ui2/src/main/webapp/app/routes/pollster.js  |  18 +--
 tez-ui2/src/main/webapp/app/routes/task.js      |   3 +-
 tez-ui2/src/main/webapp/app/routes/vertex.js    |   3 +-
 tez-ui2/src/main/webapp/app/styles/app.less     |   8 +-
 .../src/main/webapp/app/styles/error-bar.less   | 102 +++++++++++++++++
 .../main/webapp/app/templates/application.hbs   |   3 +-
 .../app/templates/components/error-bar.hbs      |  31 ++++++
 .../integration/components/error-bar-test.js    |  43 ++++++++
 .../webapp/tests/unit/entities/entity-test.js   |   3 +
 .../webapp/tests/unit/routes/abstract-test.js   |   6 +-
 25 files changed, 362 insertions(+), 24 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/TEZ-2980-CHANGES.txt
----------------------------------------------------------------------
diff --git a/TEZ-2980-CHANGES.txt b/TEZ-2980-CHANGES.txt
index fc99577..0457cb0 100644
--- a/TEZ-2980-CHANGES.txt
+++ b/TEZ-2980-CHANGES.txt
@@ -28,3 +28,4 @@ ALL CHANGES:
   TEZ-3070. Tez UI 2: Jenkins build is failing
   TEZ-3060. Tez UI 2: Activate auto-refresh
   TEZ-3061. Tez UI 2: Display in-progress vertex table in DAG details
+  TEZ-3069. Tez UI 2: Make error bar fully functional

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/app/adapters/abstract.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/adapters/abstract.js b/tez-ui2/src/main/webapp/app/adapters/abstract.js
index 6cb701b..121d4ee 100644
--- a/tez-ui2/src/main/webapp/app/adapters/abstract.js
+++ b/tez-ui2/src/main/webapp/app/adapters/abstract.js
@@ -52,4 +52,19 @@ export default LoaderAdapter.extend({
     Ember.assert(`Path not found for type:${type} to server:${serverName}`, path);
     return path;
   },
+
+  normalizeErrorResponse: function(status, headers, payload) {
+    var response;
+
+    if(payload && payload.exception && !payload.errors) {
+      payload = `${payload.exception}\n${payload.message}\n${payload.javaClassName}`;
+      response = this._super(status, headers, payload);
+    }
+    else {
+      response = this._super(status, headers, payload);
+      Ember.set(response, '0.title', this.get("outOfReachMessage"));
+    }
+
+    return response;
+  }
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/app/adapters/am.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/adapters/am.js b/tez-ui2/src/main/webapp/app/adapters/am.js
index 85f3d27..c4cb75d 100644
--- a/tez-ui2/src/main/webapp/app/adapters/am.js
+++ b/tez-ui2/src/main/webapp/app/adapters/am.js
@@ -20,6 +20,7 @@ import AbstractAdapter from './abstract';
 
 export default AbstractAdapter.extend({
   serverName: "am",
+  outOfReachMessage: "Application Master (AM) is out of reach. Either it's down, or CORS is not enabled for YARN ResourceManager.",
 
   queryRecord: function(store, type, query) {
     return this.query(store, type, query);

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/app/adapters/rm.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/adapters/rm.js b/tez-ui2/src/main/webapp/app/adapters/rm.js
index b87c77d..252affb 100644
--- a/tez-ui2/src/main/webapp/app/adapters/rm.js
+++ b/tez-ui2/src/main/webapp/app/adapters/rm.js
@@ -20,6 +20,7 @@ import AbstractAdapter from './abstract';
 
 export default AbstractAdapter.extend({
   serverName: "rm",
+  outOfReachMessage: "Resource Manager (RM) is out of reach. Either it's down, or CORS is not enabled.",
 
   // Any rm specific adapter changes must be added here
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/app/adapters/timeline.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/adapters/timeline.js b/tez-ui2/src/main/webapp/app/adapters/timeline.js
index 93e83cb..82faed8 100644
--- a/tez-ui2/src/main/webapp/app/adapters/timeline.js
+++ b/tez-ui2/src/main/webapp/app/adapters/timeline.js
@@ -24,6 +24,7 @@ var MoreObject = more.Object;
 
 export default AbstractAdapter.extend({
   serverName: "timeline",
+  outOfReachMessage: "Timeline server (ATS) is out of reach. Either it's down, or CORS is not enabled.",
 
   filters: {
     dagID: 'TEZ_DAG_ID',

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/app/components/error-bar.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/components/error-bar.js b/tez-ui2/src/main/webapp/app/components/error-bar.js
new file mode 100644
index 0000000..1086010
--- /dev/null
+++ b/tez-ui2/src/main/webapp/app/components/error-bar.js
@@ -0,0 +1,109 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+const DISPLAY_TIME = 30 * 1000;
+
+export default Ember.Component.extend({
+
+  error: null,
+
+  visible: false,
+  detailsAvailable: false,
+
+  classNames: ['error-bar'],
+  classNameBindings: ['visible', 'detailsAvailable'],
+
+  code: null,
+  message: null,
+  details: null,
+  stack: null,
+
+  showDetails: false,
+
+  displayTimerId: 0,
+
+  _errorObserver: Ember.observer("error", function () {
+    var error = this.get("error"),
+
+        code = Ember.get(error, "errors.0.status"),
+        title = Ember.get(error, "errors.0.title"),
+        message = error.message || "Error",
+        details = Ember.get(error, "errors.0.detail") || "",
+        stack = error.stack,
+        lineEndIndex = Math.min(message.indexOf('\n'), message.indexOf('<br'));
+
+    if(code === "0") {
+      code = "";
+    }
+
+    if(title) {
+      message += ". " + title;
+    }
+
+    if(lineEndIndex > 0) {
+      if(details) {
+        details = "\n" + details;
+      }
+      details = message.substr(lineEndIndex) + details;
+      message = message.substr(0, lineEndIndex);
+    }
+
+    if(details) {
+      details += "\n";
+    }
+
+    if(error) {
+      this.setProperties({
+        code: code,
+        message: message,
+        details: details,
+        stack: stack,
+
+        detailsAvailable: !!(details || stack),
+        visible: true
+      });
+
+      this.clearTimer();
+      this.set("displayTimerId", setTimeout(this.close.bind(this), DISPLAY_TIME));
+    }
+    else {
+      this.close();
+    }
+  }),
+
+  clearTimer: function () {
+    clearTimeout(this.get("displayTimerId"));
+  },
+  close: function () {
+    this.set("visible", false);
+    this.clearTimer();
+  },
+
+  actions: {
+    toggleDetailsDisplay: function () {
+      this.toggleProperty("showDetails");
+      this.clearTimer();
+    },
+    close: function () {
+      this.close();
+    }
+  }
+
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/app/controllers/application.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/controllers/application.js b/tez-ui2/src/main/webapp/app/controllers/application.js
index 24db40a..4911a16 100644
--- a/tez-ui2/src/main/webapp/app/controllers/application.js
+++ b/tez-ui2/src/main/webapp/app/controllers/application.js
@@ -25,6 +25,8 @@ const BREADCRUMB_PREFIX = [{
 
 export default Ember.Controller.extend({
   breadcrumbs: null,
+  appError: null,
+
   prefixedBreadcrumbs: Ember.computed("breadcrumbs", function () {
     var prefix = BREADCRUMB_PREFIX,
     breadcrumbs = this.get('breadcrumbs');

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/app/entities/entity.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/entities/entity.js b/tez-ui2/src/main/webapp/app/entities/entity.js
index 8a10dee..6f98097 100644
--- a/tez-ui2/src/main/webapp/app/entities/entity.js
+++ b/tez-ui2/src/main/webapp/app/entities/entity.js
@@ -98,11 +98,13 @@ var Entity = Ember.Object.extend(NameMixin, {
     );
 
     needLoader.then(function (model) {
+      parentModel.refreshLoadTime();
       parentModel.set(needOptions.name, model);
     });
 
     if(needOptions.silent) {
       needLoader = needLoader.catch(function () {
+        parentModel.refreshLoadTime();
         parentModel.set(needOptions.name, null);
       });
     }

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/app/routes/abstract.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/routes/abstract.js b/tez-ui2/src/main/webapp/app/routes/abstract.js
index 923ac96..39b8314 100644
--- a/tez-ui2/src/main/webapp/app/routes/abstract.js
+++ b/tez-ui2/src/main/webapp/app/routes/abstract.js
@@ -98,7 +98,8 @@ export default Ember.Route.extend(NameMixin, {
       then(this.checkAndCall.bind(this, promiseId, "beforeLoad", query, options)).
       then(this.checkAndCall.bind(this, promiseId, "load", query, options)).
       then(this.checkAndCall.bind(this, promiseId, "afterLoad", query, options)).
-      then(this.checkAndCall.bind(this, promiseId, "setValue", query, options));
+      then(this.checkAndCall.bind(this, promiseId, "setValue", query, options)).
+      catch(this.onLoadFailure.bind(this));
   },
 
   setLoading: function (/*query, options*/) {
@@ -124,6 +125,15 @@ export default Ember.Route.extend(NameMixin, {
 
     return value;
   },
+  onLoadFailure: function (error) {
+    if(error instanceof UnlinkedPromise) {
+      Ember.Logger.warn("Slow down, you are refreshing too fast!");
+    }
+    else {
+      this.send("error", error);
+      throw(error);
+    }
+  },
 
   getLoadTime: function (value) {
     if(value instanceof DS.RecordArray) {

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/app/routes/am-pollster.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/routes/am-pollster.js b/tez-ui2/src/main/webapp/app/routes/am-pollster.js
index 5907a91..6752ffd 100644
--- a/tez-ui2/src/main/webapp/app/routes/am-pollster.js
+++ b/tez-ui2/src/main/webapp/app/routes/am-pollster.js
@@ -17,6 +17,8 @@
  * limitations under the License.
  */
 
+import Ember from 'ember';
+
 import PollsterRoute from './pollster';
 
 var MoreObject = more.Object;
@@ -45,13 +47,11 @@ export default PollsterRoute.extend({
         that.reload();
       }
       else {
-        error.message = "Application Master (AM) is out of reach. Either it's down, or CORS is not enabled for YARN ResourceManager.";
         that.send("error", error);
       }
     }, function (error) {
-      error.message = "Resource Manager (RM) is out of reach. Either it's down, or CORS is not enabled.";
       that.send("error", error);
-      that.reload();
+      Ember.run.later(that, "reload", this.get("polling.interval") * 3);
     });
   },
 

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/app/routes/app.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/routes/app.js b/tez-ui2/src/main/webapp/app/routes/app.js
index a60025e..395a9be 100644
--- a/tez-ui2/src/main/webapp/app/routes/app.js
+++ b/tez-ui2/src/main/webapp/app/routes/app.js
@@ -26,7 +26,8 @@ export default AbstractRoute.extend({
   },
 
   model: function (params) {
-    return this.get("loader").queryRecord('app', "tez_" + this.queryFromParams(params).id);
+    return this.get("loader").queryRecord('app', "tez_" + this.queryFromParams(params).id).
+      catch(this.onLoadFailure.bind(this));
   },
 
   actions: {

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/app/routes/application.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/routes/application.js b/tez-ui2/src/main/webapp/app/routes/application.js
index 121eda2..41ea3fb 100644
--- a/tez-ui2/src/main/webapp/app/routes/application.js
+++ b/tez-ui2/src/main/webapp/app/routes/application.js
@@ -38,7 +38,7 @@ export default Ember.Route.extend({
     },
 
     error: function (error) {
-      // Display error bar
+      this.set("controller.appError", error);
       Ember.Logger.error(error);
     },
 

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/app/routes/attempt.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/routes/attempt.js b/tez-ui2/src/main/webapp/app/routes/attempt.js
index 4a1ac20..3d0224f 100644
--- a/tez-ui2/src/main/webapp/app/routes/attempt.js
+++ b/tez-ui2/src/main/webapp/app/routes/attempt.js
@@ -26,7 +26,8 @@ export default AbstractRoute.extend({
   },
 
   model: function (params) {
-    return this.get("loader").queryRecord('attempt', this.queryFromParams(params).id);
+    return this.get("loader").queryRecord('attempt', this.queryFromParams(params).id).
+      catch(this.onLoadFailure.bind(this));
   },
 
   actions: {

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/app/routes/dag.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/routes/dag.js b/tez-ui2/src/main/webapp/app/routes/dag.js
index 51e6ed9..13f1bc0 100644
--- a/tez-ui2/src/main/webapp/app/routes/dag.js
+++ b/tez-ui2/src/main/webapp/app/routes/dag.js
@@ -26,7 +26,8 @@ export default AbstractRoute.extend({
   },
 
   model: function (params) {
-    return this.get("loader").queryRecord('dag', this.queryFromParams(params).id);
+    return this.get("loader").queryRecord('dag', this.queryFromParams(params).id).
+      catch(this.onLoadFailure.bind(this));
   },
 
   actions: {

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/app/routes/dag/index/index.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/routes/dag/index/index.js b/tez-ui2/src/main/webapp/app/routes/dag/index/index.js
index 15d485f..72d8686 100644
--- a/tez-ui2/src/main/webapp/app/routes/dag/index/index.js
+++ b/tez-ui2/src/main/webapp/app/routes/dag/index/index.js
@@ -44,8 +44,13 @@ export default MultiAmPollsterRoute.extend({
     }
   }),
 
+  updateLoadTime: function (value) {
+    return value;
+  },
+
   actions: {
     reload: function () {
+      this._super();
       return true;
     },
     willTransition: function () {

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/app/routes/pollster.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/routes/pollster.js b/tez-ui2/src/main/webapp/app/routes/pollster.js
index 5a7af16..7d62c39 100644
--- a/tez-ui2/src/main/webapp/app/routes/pollster.js
+++ b/tez-ui2/src/main/webapp/app/routes/pollster.js
@@ -25,19 +25,18 @@ export default AbstractRoute.extend({
   polledRecords: null,
 
   // Must be implemented by inheriting classes
-  onRecordPoll: Ember.K,
-  onPollSuccess: Ember.K,
-  onPollFailure: Ember.K,
+  onRecordPoll: function (val) {return val;},
+  onPollSuccess: function (val) {return val;},
+  onPollFailure: function (err) {throw(err);},
 
   pollData: function () {
     var polledRecords = this.get("polledRecords");
 
     if(!this.get("isLoading") && polledRecords) {
       polledRecords = polledRecords.map(this.onRecordPoll.bind(this));
-      return Ember.RSVP.all(polledRecords).then(
-        this.onPollSuccess.bind(this),
-        this.onPollFailure.bind(this)
-      );
+      return Ember.RSVP.all(polledRecords).
+      then(this.updateLoadTime.bind(this)).
+      then(this.onPollSuccess.bind(this), this.onPollFailure.bind(this));
     }
     return Ember.RSVP.reject();
   },
@@ -46,6 +45,11 @@ export default AbstractRoute.extend({
     return this.get("polledRecords") && this.get("loadedValue");
   }),
 
+  updateLoadTime: function (value) {
+    this.send("setLoadTime", this.getLoadTime(value));
+    return value;
+  },
+
   _canPollInit: Ember.on("init", function () {
     // This sets a flag that ensures that the _canPollObserver is called whenever
     // canPoll changes. By default observers on un-used computed properties

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/app/routes/task.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/routes/task.js b/tez-ui2/src/main/webapp/app/routes/task.js
index 42d9715..54d29f1 100644
--- a/tez-ui2/src/main/webapp/app/routes/task.js
+++ b/tez-ui2/src/main/webapp/app/routes/task.js
@@ -26,7 +26,8 @@ export default AbstractRoute.extend({
   },
 
   model: function (params) {
-    return this.get("loader").queryRecord('task', this.queryFromParams(params).id);
+    return this.get("loader").queryRecord('task', this.queryFromParams(params).id).
+      catch(this.onLoadFailure.bind(this));
   },
 
   actions: {

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/app/routes/vertex.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/routes/vertex.js b/tez-ui2/src/main/webapp/app/routes/vertex.js
index 5e8ed33..6e99fd1 100644
--- a/tez-ui2/src/main/webapp/app/routes/vertex.js
+++ b/tez-ui2/src/main/webapp/app/routes/vertex.js
@@ -26,7 +26,8 @@ export default AbstractRoute.extend({
   },
 
   model: function (params) {
-    return this.get("loader").queryRecord('vertex', this.queryFromParams(params).id);
+    return this.get("loader").queryRecord('vertex', this.queryFromParams(params).id).
+      catch(this.onLoadFailure.bind(this));
   },
 
   actions: {

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/app/styles/app.less
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/styles/app.less b/tez-ui2/src/main/webapp/app/styles/app.less
index 758d1ab..cdeccd1 100644
--- a/tez-ui2/src/main/webapp/app/styles/app.less
+++ b/tez-ui2/src/main/webapp/app/styles/app.less
@@ -16,15 +16,19 @@
  * limitations under the License.
  */
 
+// Prerequisites
 @import "colors";
 @import "shared";
 
 @import "tooltip";
 
+// Components
 @import "tab-n-refresh";
 @import "dags-page-search";
+@import "table-controls";
+@import "column-selector";
+@import "error-bar";
 
+// Pages
 @import "page-layout";
 @import "details-page";
-@import "table-controls";
-@import "column-selector";

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/app/styles/error-bar.less
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/styles/error-bar.less b/tez-ui2/src/main/webapp/app/styles/error-bar.less
new file mode 100644
index 0000000..dec2100
--- /dev/null
+++ b/tez-ui2/src/main/webapp/app/styles/error-bar.less
@@ -0,0 +1,102 @@
+/**
+ * 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.
+ */
+
+.error-bar {
+  position: fixed;
+  z-index: 1000;
+
+  padding: 9px 30px;
+  min-height: 50px;
+  width: 100%;
+
+  bottom: -50px;
+  opacity: 0;
+  height: 50px;
+
+  &.visible {
+    bottom: -10px;
+    opacity: 1;
+    height: auto;
+  }
+
+  transition: all .2s cubic-bezier(0.175, 0.885, 0.320, 1.275);
+  transition-property: bottom, opacity, height;
+  -webkit-transition: all .2s cubic-bezier(0.175, 0.885, 0.320, 1.275);
+  -webkit-transition-property: bottom, opacity, height;
+
+  border-top: 1px @border-lite solid;
+  background-color: #F5F5DC;
+  color: @text-red;
+
+  .message, .details {
+    overflow: scroll;
+    max-height: 100px;
+
+    text-align: left;
+  }
+
+  .details {
+    display: none;
+    visibility: hidden;
+
+    margin-bottom: 10px;
+
+    border-top: 1px @border-lite solid;
+
+    .force-scrollbar;
+    white-space: pre-line;
+
+    &.visible {
+      display: block;
+    }
+  }
+
+  .close-button, .show-details {
+    position: absolute;
+    top: 12px;
+
+    color: @text-color;
+    opacity: .7;
+
+    &:hover {
+      cursor: pointer;
+      opacity: 1;
+    }
+  }
+
+  .close-button {
+    right: 30px;
+  }
+
+  .show-details {
+    right: 45px;
+    visibility: hidden;
+  }
+
+  &.details-available {
+    .message {
+      cursor: pointer;
+    }
+    .details {
+      visibility: visible;
+    }
+    .show-details {
+      visibility: visible;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/app/templates/application.hbs
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/templates/application.hbs b/tez-ui2/src/main/webapp/app/templates/application.hbs
index 16a0329..7bdc2b7 100644
--- a/tez-ui2/src/main/webapp/app/templates/application.hbs
+++ b/tez-ui2/src/main/webapp/app/templates/application.hbs
@@ -23,7 +23,6 @@
       <div class="lr-margin content">
         {{#link-to 'application' class="logo"}}
           <img src="assets/images/logo.png" width="70px"/>
-          <span>{{unbound App.env.version}}</span>
         {{/link-to}}
 
         <div class="breadcrumb-container">
@@ -64,3 +63,5 @@
 </div>
 
 {{outlet "modal"}}
+
+{{error-bar error=appError}}

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/app/templates/components/error-bar.hbs
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/templates/components/error-bar.hbs b/tez-ui2/src/main/webapp/app/templates/components/error-bar.hbs
new file mode 100644
index 0000000..a21bba0
--- /dev/null
+++ b/tez-ui2/src/main/webapp/app/templates/components/error-bar.hbs
@@ -0,0 +1,31 @@
+{{!
+ * 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.
+}}
+
+<div class="message" {{action "toggleDetailsDisplay"}}>
+  <i class="fa fa-exclamation-circle"></i>
+  {{#if code}}
+    <b>{{code}}</b> :
+  {{/if}}
+  {{message}}
+  <i class="show-details fa {{if showDetails 'fa-minus-circle' 'fa-plus-circle'}}"></i>
+</div>
+<div class="details {{if showDetails "visible"}}">{{{details}}}{{#if stack}}<b>Stack:</b>
+    {{stack}}
+  {{/if}}
+</div>
+<i class="close-button fa fa-arrow-circle-down" {{action "close"}}></i>

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/tests/integration/components/error-bar-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/integration/components/error-bar-test.js b/tez-ui2/src/main/webapp/tests/integration/components/error-bar-test.js
new file mode 100644
index 0000000..10c6c7d
--- /dev/null
+++ b/tez-ui2/src/main/webapp/tests/integration/components/error-bar-test.js
@@ -0,0 +1,43 @@
+/**
+ * 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.
+ */
+
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+moduleForComponent('error-bar', 'Integration | Component | error bar', {
+  integration: true
+});
+
+test('Basic creation test', function(assert) {
+
+  // Set any properties with this.set('myProperty', 'value');
+  // Handle any actions with this.on('myAction', function(val) { ... });" + EOL + EOL +
+
+  this.render(hbs`{{error-bar}}`);
+
+  assert.equal(this.$().text().trim(), '');
+
+  // Template block usage:" + EOL +
+  this.render(hbs`
+    {{#error-bar}}
+      template block text
+    {{/error-bar}}
+  `);
+
+  assert.equal(this.$().text().trim(), '');
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/tests/unit/entities/entity-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/unit/entities/entity-test.js b/tez-ui2/src/main/webapp/tests/unit/entities/entity-test.js
index fb0bd8a..9e2550d 100644
--- a/tez-ui2/src/main/webapp/tests/unit/entities/entity-test.js
+++ b/tez-ui2/src/main/webapp/tests/unit/entities/entity-test.js
@@ -78,6 +78,7 @@ test('loadAllNeeds basic test', function(assert) {
   let adapter = this.subject(),
       loader,
       testModel = Ember.Object.create({
+        refreshLoadTime: Ember.K,
         needs: {
           app: "appID",
           foo: "fooID"
@@ -115,6 +116,7 @@ test('loadAllNeeds silent=false test', function(assert) {
   let adapter = this.subject(),
       loader,
       testModel = Ember.Object.create({
+        refreshLoadTime: Ember.K,
         needs: {
           app: {
             idKey: "appID",
@@ -142,6 +144,7 @@ test('loadAllNeeds silent=true test', function(assert) {
   let adapter = this.subject(),
       loader,
       testModel = Ember.Object.create({
+        refreshLoadTime: Ember.K,
         needs: {
           app: {
             idKey: "appID",

http://git-wip-us.apache.org/repos/asf/tez/blob/6d4bb2c6/tez-ui2/src/main/webapp/tests/unit/routes/abstract-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/unit/routes/abstract-test.js b/tez-ui2/src/main/webapp/tests/unit/routes/abstract-test.js
index 202e889..9ed3452 100644
--- a/tez-ui2/src/main/webapp/tests/unit/routes/abstract-test.js
+++ b/tez-ui2/src/main/webapp/tests/unit/routes/abstract-test.js
@@ -18,8 +18,6 @@
 
 import Ember from 'ember';
 
-import UnlinkedPromise from '../../../errors/unlinked-promise';
-
 import { moduleFor, test } from 'ember-qunit';
 
 moduleFor('route:abstract', 'Unit | Route | abstract', {
@@ -174,8 +172,8 @@ test('loadData test - ID change check with exception throw', function(assert) {
 
   route.loadData().then(function () {
     assert.notOk("Shouldn't be called");
-  }).catch(function (e) {
-    assert.ok(e instanceof UnlinkedPromise, "Exception thrown");
+  }).catch(function () {
+    assert.ok(true, "Exception thrown");
   });
 });