diff --git a/client/app/scripts/components/node-details.js b/client/app/scripts/components/node-details.js
index 6c566c3722..6bd6017e32 100644
--- a/client/app/scripts/components/node-details.js
+++ b/client/app/scripts/components/node-details.js
@@ -2,15 +2,22 @@ import _ from 'lodash';
import React from 'react';
import { connect } from 'react-redux';
+import { clickCloseDetails, clickShowTopologyForNode } from '../actions/app-actions';
+import { brightenColor, getNeutralColor, getNodeColorDark } from '../utils/color-utils';
+import { resetDocumentTitle, setDocumentTitle } from '../utils/title-utils';
+
import NodeDetailsControls from './node-details/node-details-controls';
import NodeDetailsHealth from './node-details/node-details-health';
import NodeDetailsInfo from './node-details/node-details-info';
import NodeDetailsLabels from './node-details/node-details-labels';
import NodeDetailsRelatives from './node-details/node-details-relatives';
import NodeDetailsTable from './node-details/node-details-table';
-import { clickCloseDetails, clickShowTopologyForNode } from '../actions/app-actions';
-import { brightenColor, getNeutralColor, getNodeColorDark } from '../utils/color-utils';
-import { resetDocumentTitle, setDocumentTitle } from '../utils/title-utils';
+import Warning from './warning';
+
+function getTruncationText(count) {
+ return 'This section was too long to be handled efficiently and has been truncated'
+ + ` (${count} extra entries not included). We are working to remove this limitation.`;
+}
export class NodeDetails extends React.Component {
@@ -196,7 +203,13 @@ export class NodeDetails extends React.Component {
if (table.rows.length > 0) {
return (
-
{table.label}
+
+ {table.label}
+ {table.truncationCount > 0 &&
+
+ }
+
);
diff --git a/client/app/scripts/components/warning.js b/client/app/scripts/components/warning.js
new file mode 100644
index 0000000000..161c68d5ab
--- /dev/null
+++ b/client/app/scripts/components/warning.js
@@ -0,0 +1,39 @@
+import React from 'react';
+import classnames from 'classnames';
+
+
+class Warning extends React.Component {
+
+ constructor(props, context) {
+ super(props, context);
+ this.handleClick = this.handleClick.bind(this);
+ this.state = {
+ expanded: false
+ };
+ }
+
+ handleClick() {
+ const expanded = !this.state.expanded;
+ this.setState({ expanded });
+ }
+
+ render() {
+ const { text } = this.props;
+ const { expanded } = this.state;
+
+ const className = classnames('warning', {
+ 'warning-expanded': expanded
+ });
+
+ return (
+
+
+
+ {expanded && {text}}
+
+
+ );
+ }
+}
+
+export default Warning;
diff --git a/client/app/styles/main.less b/client/app/styles/main.less
index 321468f475..43132ba893 100644
--- a/client/app/styles/main.less
+++ b/client/app/styles/main.less
@@ -1098,6 +1098,42 @@ h2 {
}
}
+.warning {
+ display: inline-block;
+ cursor: pointer;
+ border: 1px dashed transparent;
+ text-transform: none;
+ border-radius: @border-radius;
+ margin-left: 4px;
+
+ &-wrapper {
+ display: flex;
+ }
+
+ &-text {
+ display: inline-block;
+ color: @text-secondary-color;
+ padding-left: 0.5em;
+ }
+
+ &-icon {
+ .btn-opacity;
+ }
+
+ &-expanded {
+ margin-left: 0;
+ padding: 2px 4px;
+ border-color: @text-tertiary-color;
+ }
+
+ &-expanded &-icon {
+ position: relative;
+ top: 4px;
+ left: 2px;
+ }
+
+}
+
.sidebar {
position: fixed;
bottom: 16px;
diff --git a/report/table.go b/report/table.go
index 25052640b8..6ab4f5a20e 100644
--- a/report/table.go
+++ b/report/table.go
@@ -1,37 +1,62 @@
package report
import (
+ "fmt"
"sort"
"strings"
+ log "github.com/Sirupsen/logrus"
"github.com/weaveworks/scope/common/mtime"
)
+// MaxTableRows sets the limit on the table size to render
+// TODO: this won't be needed once we send reports incrementally
+const (
+ MaxTableRows = 20
+ TruncationCountPrefix = "table_truncation_count_"
+)
+
// AddTable appends arbirary key-value pairs to the Node, returning a new node.
func (node Node) AddTable(prefix string, labels map[string]string) Node {
+ count := 0
for key, value := range labels {
+ if count >= MaxTableRows {
+ break
+ }
node = node.WithLatest(prefix+key, mtime.Now(), value)
+ count++
+ }
+ if len(labels) > MaxTableRows {
+ truncationCount := fmt.Sprintf("%d", len(labels)-MaxTableRows)
+ node = node.WithLatest(TruncationCountPrefix+prefix, mtime.Now(), truncationCount)
}
return node
}
// ExtractTable returns the key-value pairs with the given prefix from this Node,
-func (node Node) ExtractTable(prefix string) map[string]string {
- result := map[string]string{}
+func (node Node) ExtractTable(prefix string) (rows map[string]string, truncationCount int) {
+ rows = map[string]string{}
+ truncationCount = 0
node.Latest.ForEach(func(key, value string) {
if strings.HasPrefix(key, prefix) {
label := key[len(prefix):]
- result[label] = value
+ rows[label] = value
}
})
- return result
+ if str, ok := node.Latest.Lookup(TruncationCountPrefix + prefix); ok {
+ if n, err := fmt.Sscanf(str, "%d", &truncationCount); n != 1 || err != nil {
+ log.Warn("Unexpected truncation count format %q", str)
+ }
+ }
+ return rows, truncationCount
}
// Table is the type for a table in the UI.
type Table struct {
- ID string `json:"id"`
- Label string `json:"label"`
- Rows []MetadataRow `json:"rows"`
+ ID string `json:"id"`
+ Label string `json:"label"`
+ Rows []MetadataRow `json:"rows"`
+ TruncationCount int `json:"truncationCount,omitempty"`
}
type tablesByID []Table
@@ -90,12 +115,13 @@ type TableTemplates map[string]TableTemplate
func (t TableTemplates) Tables(node Node) []Table {
var result []Table
for _, template := range t {
+ rows, truncationCount := node.ExtractTable(template.Prefix)
table := Table{
- ID: template.ID,
- Label: template.Label,
- Rows: []MetadataRow{},
+ ID: template.ID,
+ Label: template.Label,
+ Rows: []MetadataRow{},
+ TruncationCount: truncationCount,
}
- rows := node.ExtractTable(template.Prefix)
keys := make([]string, 0, len(rows))
for k := range rows {
keys = append(keys, k)
diff --git a/report/table_test.go b/report/table_test.go
index b1866b072a..cf276ba75c 100644
--- a/report/table_test.go
+++ b/report/table_test.go
@@ -1,6 +1,7 @@
package report_test
import (
+ "fmt"
"reflect"
"testing"
@@ -16,9 +17,37 @@ func TestTables(t *testing.T) {
nmd := report.MakeNode("foo1")
nmd = nmd.AddTable("foo_", want)
- have := nmd.ExtractTable("foo_")
+ have, truncationCount := nmd.ExtractTable("foo_")
+
+ if truncationCount != 0 {
+ t.Error("Table shouldn't had been truncated")
+ }
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
}
+
+func TestTruncation(t *testing.T) {
+ wantTruncationCount := 1
+ want := map[string]string{}
+ for i := 0; i < report.MaxTableRows+wantTruncationCount; i++ {
+ key := fmt.Sprintf("key%d", i)
+ value := fmt.Sprintf("value%d", i)
+ want[key] = value
+ }
+
+ nmd := report.MakeNode("foo1")
+
+ nmd = nmd.AddTable("foo_", want)
+ _, truncationCount := nmd.ExtractTable("foo_")
+
+ if truncationCount != wantTruncationCount {
+ t.Error(
+ "Table should had been truncated by",
+ wantTruncationCount,
+ "and not",
+ truncationCount,
+ )
+ }
+}