You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by mz...@apache.org on 2019/09/19 21:14:25 UTC

[mesos] branch master updated: Implemented displaying roles of multi-role frameworks as a tree.

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

mzhu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mesos.git


The following commit(s) were added to refs/heads/master by this push:
     new b13de7e  Implemented displaying roles of multi-role frameworks as a tree.
b13de7e is described below

commit b13de7eee6eda644ddb136947a655b9d9439784e
Author: Andrei Sekretenko <as...@mesosphere.io>
AuthorDate: Thu Sep 19 14:13:57 2019 -0700

    Implemented displaying roles of multi-role frameworks as a tree.
    
    This patch makes the UI pages `frameworks` and `framework` display
    roles of multi-role frameworks as collapsible tree (instead of a list).
    
    Review: https://reviews.apache.org/r/71178/
---
 src/webui/app/app.js                          | 78 +++++++++++++++++++++++++++
 src/webui/app/frameworks/framework.html       | 10 ++--
 src/webui/app/frameworks/frameworks.html      | 27 +++++-----
 src/webui/app/frameworks/roles-tree-root.html |  7 +++
 src/webui/app/frameworks/roles-tree.html      | 16 ++++++
 src/webui/app/frameworks/roles.html           |  2 +
 src/webui/assets/css/mesos.css                | 41 ++++++++++++++
 7 files changed, 160 insertions(+), 21 deletions(-)

diff --git a/src/webui/app/app.js b/src/webui/app/app.js
index f6f1138..24fab09 100644
--- a/src/webui/app/app.js
+++ b/src/webui/app/app.js
@@ -257,6 +257,84 @@
         templateUrl: 'app/shared/timestamp.html'
       }
     }])
+    .directive('mFrameworkRoles', function() {
+      return {
+        restrict: 'E',
+        scope: {roles: '=', frameworkId: '='},
+        templateUrl: 'app/frameworks/roles.html'
+      }
+    })
+    .directive('mFrameworkRolesTree', function() {
+      // This helper builds a prefix tree from a list of roles.
+      // Each role from the list corresponds to a leaf node in the tree.
+      // The path to that leaf node equals to the role (with '/.' added if this
+      // path is a prefix of other roles).
+      //
+      // For example, given a list of roles ['a/b','a', 'e/f', 'e/g']
+      // the following tree will be built:
+      // {'a': {'.': {}, 'b' : {}}, 'e': {'f': {}, 'g': '{}'}}
+      // (corresponding paths are 'a/.', 'a/b', 'e/f' and 'e/g')
+      function buildTree(roles) {
+        var root = {};
+
+        for (var path of roles) {
+          const tokens = path.split('/');
+          var i = 0;
+          var node = root;
+          for (i = 0; i < tokens.length && (tokens[i] in node); ++i) {
+            node = node[tokens[i]];
+          }
+
+          if (i > 0 && (i == tokens.length || Object.keys(node).length == 0)) {
+            node['.'] = {};
+          }
+
+          for (; i < tokens.length; ++i) {
+            node[tokens[i]] = {};
+            node = node[tokens[i]];
+          }
+        }
+
+        return root;
+      };
+
+      function prepareTree(path, name, node) {
+        const prefix = path ? path + '/' : '';
+        return {
+          "children": Object.keys(node).sort().map(
+              k => prepareTree(prefix + k, k, node[k])),
+          "name": name,
+          "path": path
+          }
+      };
+
+      return {
+        restrict: 'E',
+        scope: {roles: '=', frameworkId: '='},
+        link: function($scope, _element, _attrs) {
+
+          // TODO (asekretenko): after MESOS-9915 consider getting the roles
+          // hierarchy from master directly (instead of buildTree()).
+          $scope.node = prepareTree("", "", buildTree($scope.roles));
+
+          $scope.storagePrefix = 'framework-roles-tree.' + $scope.frameworkId;
+          $scope.visible = JSON.parse(
+              localStorage.getItem($scope.storagePrefix) || '{}');
+
+          $scope.toggle = function(path) {
+            if (path in $scope.visible) {
+              delete $scope.visible[path];
+            } else {
+              $scope.visible[path] = true;
+            }
+
+            localStorage.setItem($scope.storagePrefix,
+                                 JSON.stringify($scope.visible));
+          };
+        },
+        templateUrl: 'app/frameworks/roles-tree-root.html'
+      }
+    })
     .directive('mPagination', function() {
       return { templateUrl: 'app/shared/pagination.html' }
     })
diff --git a/src/webui/app/frameworks/framework.html b/src/webui/app/frameworks/framework.html
index 82f6b27..93ec74c 100644
--- a/src/webui/app/frameworks/framework.html
+++ b/src/webui/app/frameworks/framework.html
@@ -23,13 +23,11 @@
         <dd ng-show="framework.webui_url"><a href="{{framework.webui_url}}">{{framework.webui_url}}</a></dd>
         <dt>User:</dt>
         <dd>{{framework.user}}</dd>
-        <!-- TODO(bmahler): Consider having a break between each role
-             in order to increase readability. Also, this doesn't
-             display well when there are a lot of roles (e.g. a large
-             organization with a lot of teams & services, using roles
-             like /engineering/frontend/webserver, etc). -->
         <dt>Roles:</dt>
-        <dd>{{framework.roles.toString()}}</dd>
+        <dd>
+          <m-framework-roles framework-id="framework.id" roles="framework.roles">
+          </m-framework-roles>
+        </dd>
         <dt>Principal:</dt>
         <dd>{{framework.principal}}</dd>
         <dt>Registered:</dt>
diff --git a/src/webui/app/frameworks/frameworks.html b/src/webui/app/frameworks/frameworks.html
index d37c613..737f407 100644
--- a/src/webui/app/frameworks/frameworks.html
+++ b/src/webui/app/frameworks/frameworks.html
@@ -45,11 +45,10 @@
       </td>
       <td>{{framework.user}}</td>
       <td>{{framework.name}}</td>
-      <!-- TODO(bmahler): This doesn't display well when there are a lot
-           of roles (e.g. a large organization with a lot of teams &
-           services, using roles like /engineering/frontend/webserver, etc).
-           Figure out a way to display this without bloating the table. -->
-      <td>{{framework.roles.toString()}}</td>
+      <td>
+        <m-framework-roles framework-id="framework.id" roles="framework.roles">
+        </m-framework-roles>
+      </td>
       <td>{{framework.principal}}</td>
       <td>{{framework.tasks.length}}</td>
       <td>{{framework.used_resources.cpus | number}}</td>
@@ -108,11 +107,10 @@
     </td>
     <td>{{framework.user}}</td>
     <td>{{framework.name}}</td>
-    <!-- TODO(bmahler): This doesn't display well when there are a lot
-         of roles (e.g. a large organization with a lot of teams &
-         services, using roles like /engineering/frontend/webserver, etc).
-         Figure out a way to display this without bloating the table. -->
-    <td>{{framework.roles.toString()}}</td>
+    <td>
+      <m-framework-roles framework-id="framework.id" roles="framework.roles">
+      </m-framework-roles>
+    </td>
     <td>{{framework.principal}}</td>
     <td>{{framework.tasks.length}}</td>
     <td>{{framework.used_resources.cpus | number}}</td>
@@ -162,11 +160,10 @@
       <td>{{framework.hostname}}</td>
       <td>{{framework.user}}</td>
       <td>{{framework.name}}</td>
-      <!-- TODO(bmahler): This doesn't display well when there are a lot
-           of roles (e.g. a large organization with a lot of teams &
-           services, using roles like /engineering/frontend/webserver, etc).
-           Figure out a way to display this without bloating the table. -->
-      <td>{{framework.roles.toString()}}</td>
+      <td>
+        <m-framework-roles framework-id="framework.id" roles="framework.roles">
+        </m-framework-roles>
+      </td>
       <td>{{framework.principal}}</td>
       <td>
         <m-timestamp value="{{framework.registered_time * 1000}}"></m-timestamp>
diff --git a/src/webui/app/frameworks/roles-tree-root.html b/src/webui/app/frameworks/roles-tree-root.html
new file mode 100644
index 0000000..982aac1
--- /dev/null
+++ b/src/webui/app/frameworks/roles-tree-root.html
@@ -0,0 +1,7 @@
+<div ng-click="toggle('')" ng-class="visible[''] ? 'tree-expanded': 'tree-collapsed'">
+  <div class="tree-internal">
+    {{ roles.length }} roles
+  </div>
+</div>
+
+<ng-include ng-if="visible['']" src="'app/frameworks/roles-tree.html'"></ng-include>
diff --git a/src/webui/app/frameworks/roles-tree.html b/src/webui/app/frameworks/roles-tree.html
new file mode 100644
index 0000000..61bc055
--- /dev/null
+++ b/src/webui/app/frameworks/roles-tree.html
@@ -0,0 +1,16 @@
+<ul ng-class="node.path ? 'tree' : 'tree-root'">
+  <li ng-repeat="node in node.children">
+    <div ng-if="node.children.length == 0" class="tree-leaf">
+      {{ node.name }}
+    </div>
+
+    <div ng-if="node.children.length > 0" ng-click="toggle(node.path)" ng-class="visible[node.path] ? 'tree-expanded': 'tree-collapsed'">
+      <div class="tree-internal">
+        {{ node.name }}
+      </div>
+    </div>
+
+    <ng-include ng-if="node.children.length > 0 && visible[node.path]" src="'app/frameworks/roles-tree.html'">
+    </ng-include>
+  </li>
+</ul>
diff --git a/src/webui/app/frameworks/roles.html b/src/webui/app/frameworks/roles.html
new file mode 100644
index 0000000..54e3dfd
--- /dev/null
+++ b/src/webui/app/frameworks/roles.html
@@ -0,0 +1,2 @@
+<span ng-if='roles.length < 2'> {{roles.join(' ')}}</span>
+<m-framework-roles-tree ng-if='roles.length >= 2' framework-id="frameworkId" roles="roles">
diff --git a/src/webui/assets/css/mesos.css b/src/webui/assets/css/mesos.css
index 0ff47cd..5066414 100644
--- a/src/webui/assets/css/mesos.css
+++ b/src/webui/assets/css/mesos.css
@@ -129,6 +129,47 @@ time:hover {
   cursor: pointer;
 }
 
+ul.tree {
+  list-style-type: none;
+  padding-left: 1em;
+}
+
+ul.tree-root {
+  list-style-type: none;
+  padding-left: 0em;
+}
+
+div.tree-internal {
+  border-bottom: 1px #999 dashed;
+  display: inline-block;
+}
+
+div.tree-collapsed:hover,
+div.tree-expanded:hover {
+  cursor:pointer;
+}
+
+div.tree-collapsed:before,
+div.tree-expanded:before,
+div.tree-leaf:before {
+ width: 1em;
+ padding-right: 0.3em;
+ display: inline-block;
+ text-align: right;
+}
+
+div.tree-collapsed:before {
+  content: "▹";
+}
+
+div.tree-expanded:before {
+  content: "▿";
+}
+
+div.tree-leaf:before {
+  content: "•";
+}
+
 .inline .btn-toggle,
 .table-condensed .btn-toggle {
   margin-bottom: -2px;