From 7d48683df74418d0109791b1df9bb505f70be919 Mon Sep 17 00:00:00 2001 From: Michael Quigley Date: Mon, 15 May 2023 14:14:52 -0400 Subject: [PATCH] support for displaying limited shares in red in the visualizer (#320) --- controller/overview.go | 25 +++++++++++++++++--- controller/store/shareLimitJournal.go | 28 +++++++++++++++++++++++ rest_model_zrok/environment.go | 3 +++ rest_model_zrok/share.go | 3 +++ rest_server_zrok/embedded_spec.go | 12 ++++++++++ specs/zrok.yml | 4 ++++ ui/src/api/types.js | 2 ++ ui/src/console/detail/share/MetricsTab.js | 2 -- ui/src/console/visualizer/Network.js | 2 +- ui/src/console/visualizer/graph.js | 10 ++++---- 10 files changed, 81 insertions(+), 10 deletions(-) diff --git a/controller/overview.go b/controller/overview.go index c85e2418..ee313ba6 100644 --- a/controller/overview.go +++ b/controller/overview.go @@ -2,6 +2,7 @@ package controller import ( "github.com/go-openapi/runtime/middleware" + "github.com/openziti/zrok/controller/store" "github.com/openziti/zrok/rest_model_zrok" "github.com/openziti/zrok/rest_server_zrok/operations/metadata" "github.com/sirupsen/logrus" @@ -36,7 +37,19 @@ func overviewHandler(_ metadata.OverviewParams, principal *rest_model_zrok.Princ ZID: env.ZId, }, } - + var shrIds []int + for i := range shrs { + shrIds = append(shrIds, shrs[i].Id) + } + shrsLimited, err := str.FindSelectedLatestShareLimitjournal(shrIds, tx) + if err != nil { + logrus.Errorf("error finding limited shares for environment '%v': %v", env.ZId, err) + return metadata.NewOverviewInternalServerError() + } + shrsLimitedMap := make(map[int]store.LimitJournalAction) + for i := range shrsLimited { + shrsLimitedMap[shrsLimited[i].ShareId] = shrsLimited[i].Action + } for _, shr := range shrs { feEndpoint := "" if shr.FrontendEndpoint != nil { @@ -50,7 +63,7 @@ func overviewHandler(_ metadata.OverviewParams, principal *rest_model_zrok.Princ if shr.BackendProxyEndpoint != nil { beProxyEndpoint = *shr.BackendProxyEndpoint } - es.Shares = append(es.Shares, &rest_model_zrok.Share{ + oshr := &rest_model_zrok.Share{ Token: shr.Token, ZID: shr.ZId, ShareMode: shr.ShareMode, @@ -61,7 +74,13 @@ func overviewHandler(_ metadata.OverviewParams, principal *rest_model_zrok.Princ Reserved: shr.Reserved, CreatedAt: shr.CreatedAt.UnixMilli(), UpdatedAt: shr.UpdatedAt.UnixMilli(), - }) + } + if action, found := shrsLimitedMap[shr.Id]; found { + if action == store.LimitAction { + oshr.Limited = true + } + } + es.Shares = append(es.Shares, oshr) } out = append(out, es) } diff --git a/controller/store/shareLimitJournal.go b/controller/store/shareLimitJournal.go index 7dcc351f..2b935cc1 100644 --- a/controller/store/shareLimitJournal.go +++ b/controller/store/shareLimitJournal.go @@ -1,6 +1,7 @@ package store import ( + "fmt" "github.com/jmoiron/sqlx" "github.com/pkg/errors" ) @@ -41,6 +42,33 @@ func (str *Store) FindLatestShareLimitJournal(shrId int, trx *sqlx.Tx) (*ShareLi return j, nil } +func (str *Store) FindSelectedLatestShareLimitjournal(shrIds []int, trx *sqlx.Tx) ([]*ShareLimitJournal, error) { + if len(shrIds) < 1 { + return nil, nil + } + in := "(" + for i := range shrIds { + if i > 0 { + in += ", " + } + in += fmt.Sprintf("%d", shrIds[i]) + } + in += ")" + rows, err := trx.Queryx("select id, share_id, rx_bytes, tx_bytes, action, created_at, updated_at from share_limit_journal where id in (select max(id) as id from share_limit_journal group by share_id) and share_id in " + in) + if err != nil { + return nil, errors.Wrap(err, "error selecting all latest share_limit_journal") + } + var sljs []*ShareLimitJournal + for rows.Next() { + slj := &ShareLimitJournal{} + if err := rows.StructScan(slj); err != nil { + return nil, errors.Wrap(err, "error scanning share_limit_journal") + } + sljs = append(sljs, slj) + } + return sljs, nil +} + func (str *Store) FindAllLatestShareLimitJournal(trx *sqlx.Tx) ([]*ShareLimitJournal, error) { rows, err := trx.Queryx("select id, share_id, rx_bytes, tx_bytes, action, created_at, updated_at from share_limit_journal where id in (select max(id) as id from share_limit_journal group by share_id)") if err != nil { diff --git a/rest_model_zrok/environment.go b/rest_model_zrok/environment.go index 19a354de..17376949 100644 --- a/rest_model_zrok/environment.go +++ b/rest_model_zrok/environment.go @@ -33,6 +33,9 @@ type Environment struct { // host Host string `json:"host,omitempty"` + // limited + Limited bool `json:"limited,omitempty"` + // updated at UpdatedAt int64 `json:"updatedAt,omitempty"` diff --git a/rest_model_zrok/share.go b/rest_model_zrok/share.go index 983c11c6..998744b4 100644 --- a/rest_model_zrok/share.go +++ b/rest_model_zrok/share.go @@ -36,6 +36,9 @@ type Share struct { // frontend selection FrontendSelection string `json:"frontendSelection,omitempty"` + // limited + Limited bool `json:"limited,omitempty"` + // reserved Reserved bool `json:"reserved,omitempty"` diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index 03a22b60..1d9d36a4 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -1104,6 +1104,9 @@ func init() { "host": { "type": "string" }, + "limited": { + "type": "boolean" + }, "updatedAt": { "type": "integer" }, @@ -1308,6 +1311,9 @@ func init() { "frontendSelection": { "type": "string" }, + "limited": { + "type": "boolean" + }, "reserved": { "type": "boolean" }, @@ -2575,6 +2581,9 @@ func init() { "host": { "type": "string" }, + "limited": { + "type": "boolean" + }, "updatedAt": { "type": "integer" }, @@ -2779,6 +2788,9 @@ func init() { "frontendSelection": { "type": "string" }, + "limited": { + "type": "boolean" + }, "reserved": { "type": "boolean" }, diff --git a/specs/zrok.yml b/specs/zrok.yml index 38897e48..2394046f 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -706,6 +706,8 @@ definitions: type: string activity: $ref: "#/definitions/sparkData" + limited: + type: boolean createdAt: type: integer updatedAt: @@ -861,6 +863,8 @@ definitions: type: boolean activity: $ref: "#/definitions/sparkData" + limited: + type: boolean createdAt: type: integer updatedAt: diff --git a/ui/src/api/types.js b/ui/src/api/types.js index 4d5a07cb..0d2fb901 100644 --- a/ui/src/api/types.js +++ b/ui/src/api/types.js @@ -88,6 +88,7 @@ * @property {string} address * @property {string} zId * @property {module:types.sparkData} activity + * @property {boolean} limited * @property {number} createdAt * @property {number} updatedAt */ @@ -201,6 +202,7 @@ * @property {string} backendProxyEndpoint * @property {boolean} reserved * @property {module:types.sparkData} activity + * @property {boolean} limited * @property {number} createdAt * @property {number} updatedAt */ diff --git a/ui/src/console/detail/share/MetricsTab.js b/ui/src/console/detail/share/MetricsTab.js index 51143ffe..d73bff6f 100644 --- a/ui/src/console/detail/share/MetricsTab.js +++ b/ui/src/console/detail/share/MetricsTab.js @@ -9,7 +9,6 @@ const MetricsTab = (props) => { const [metrics1, setMetrics1] = useState(buildMetrics([])); useEffect(() => { - console.log("token", props.share.token); metadata.getShareMetrics(props.share.token) .then(resp => { setMetrics30(buildMetrics(resp.data)); @@ -27,7 +26,6 @@ const MetricsTab = (props) => { useEffect(() => { let mounted = true; let interval = setInterval(() => { - console.log("token", props.share.token); metadata.getShareMetrics(props.share.token) .then(resp => { if(mounted) { diff --git a/ui/src/console/visualizer/Network.js b/ui/src/console/visualizer/Network.js index a36b1283..9e8fda5f 100644 --- a/ui/src/console/visualizer/Network.js +++ b/ui/src/console/visualizer/Network.js @@ -21,7 +21,7 @@ const Network = (props) => { }, []); const paintNode = (node, ctx) => { - let nodeColor = node.selected ? "#9BF316" : "#04adef"; + let nodeColor = node.selected ? "#9BF316" : node.limited ? "#f00": "#04adef"; let textColor = "black"; ctx.textBaseline = "middle"; diff --git a/ui/src/console/visualizer/graph.js b/ui/src/console/visualizer/graph.js index c10d0ab3..f8d0006e 100644 --- a/ui/src/console/visualizer/graph.js +++ b/ui/src/console/visualizer/graph.js @@ -12,7 +12,7 @@ const sortNodes = (nodes) => { const nodesEqual = (a, b) => { if(a.length !== b.length) return false; - return a.every((e, i) => e.id === b[i].id); + return a.every((e, i) => e.id === b[i].id && e.limited === b[i].limited); } export const mergeGraph = (oldGraph, user, newOverview) => { @@ -34,7 +34,8 @@ export const mergeGraph = (oldGraph, user, newOverview) => { id: env.environment.zId, label: env.environment.description, type: "environment", - val: 50 + val: 50, + limited: env.limited }; newGraph.nodes.push(envNode); newGraph.links.push({ @@ -53,6 +54,7 @@ export const mergeGraph = (oldGraph, user, newOverview) => { envZId: env.environment.zId, label: shrLabel, type: "share", + limited: !!shr.limited, val: 50 }; newGraph.nodes.push(shrNode); @@ -75,11 +77,11 @@ export const mergeGraph = (oldGraph, user, newOverview) => { // we're going to need to recompute a new graph... but we want to maintain the instances that already exist... // we want to preserve nodes that exist in the new graph, and remove those that don't. - let outputNodes = oldGraph.nodes.filter(oldNode => newGraph.nodes.find(newNode => newNode.id === oldNode.id)); + let outputNodes = oldGraph.nodes.filter(oldNode => newGraph.nodes.find(newNode => newNode.id === oldNode.id && newNode.limited === oldNode.limited)); let outputLinks = oldGraph.nodes.filter(oldLink => newGraph.links.find(newLink => newLink.target === oldLink.target && newLink.source === oldLink.source)); // and then do the opposite; add any nodes that are in newGraph that are missing from oldGraph. - outputNodes.push(...newGraph.nodes.filter(newNode => !outputNodes.find(oldNode => oldNode.id === newNode.id))); + outputNodes.push(...newGraph.nodes.filter(newNode => !outputNodes.find(oldNode => oldNode.id === newNode.id && oldNode.limited === newNode.limited))); outputLinks.push(...newGraph.links.filter(newLink => !outputLinks.find(oldLink => oldLink.target === newLink.target && oldLink.source === newLink.source))); return {