diff --git a/controller/metrics.go b/controller/metrics.go index c823ec7f..d4582002 100644 --- a/controller/metrics.go +++ b/controller/metrics.go @@ -94,13 +94,9 @@ func (h *getEnvironmentMetricsHandler) Handle(params metadata.GetEnvironmentMetr return metadata.NewGetEnvironmentMetricsInternalServerError() } defer func() { _ = trx.Rollback() }() - env, err := str.GetEnvironment(int(params.EnvID), trx) + env, err := str.FindEnvironmentForAccount(params.EnvID, int(principal.ID), trx) if err != nil { - logrus.Errorf("error finding environment '%d': %v", int(params.EnvID), err) - return metadata.NewGetEnvironmentMetricsUnauthorized() - } - if int64(env.Id) != principal.ID { - logrus.Errorf("unauthorized environemnt '%d' for '%v'", int(params.EnvID), principal.Email) + logrus.Errorf("error finding environment '%s' for '%s': %v", params.EnvID, principal.Email, err) return metadata.NewGetEnvironmentMetricsUnauthorized() } diff --git a/rest_client_zrok/metadata/get_environment_metrics_parameters.go b/rest_client_zrok/metadata/get_environment_metrics_parameters.go index 6bdd8404..be7aae16 100644 --- a/rest_client_zrok/metadata/get_environment_metrics_parameters.go +++ b/rest_client_zrok/metadata/get_environment_metrics_parameters.go @@ -14,7 +14,6 @@ import ( "github.com/go-openapi/runtime" cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "github.com/go-openapi/swag" ) // NewGetEnvironmentMetricsParams creates a new GetEnvironmentMetricsParams object, @@ -66,7 +65,7 @@ type GetEnvironmentMetricsParams struct { Duration *string // EnvID. - EnvID float64 + EnvID string timeout time.Duration Context context.Context @@ -133,13 +132,13 @@ func (o *GetEnvironmentMetricsParams) SetDuration(duration *string) { } // WithEnvID adds the envID to the get environment metrics params -func (o *GetEnvironmentMetricsParams) WithEnvID(envID float64) *GetEnvironmentMetricsParams { +func (o *GetEnvironmentMetricsParams) WithEnvID(envID string) *GetEnvironmentMetricsParams { o.SetEnvID(envID) return o } // SetEnvID adds the envId to the get environment metrics params -func (o *GetEnvironmentMetricsParams) SetEnvID(envID float64) { +func (o *GetEnvironmentMetricsParams) SetEnvID(envID string) { o.EnvID = envID } @@ -169,7 +168,7 @@ func (o *GetEnvironmentMetricsParams) WriteToRequest(r runtime.ClientRequest, re } // path param envId - if err := r.SetPathParam("envId", swag.FormatFloat64(o.EnvID)); err != nil { + if err := r.SetPathParam("envId", o.EnvID); err != nil { return err } diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index 0b5fcdb8..fc9e8858 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -567,7 +567,7 @@ func init() { "operationId": "getEnvironmentMetrics", "parameters": [ { - "type": "number", + "type": "string", "name": "envId", "in": "path", "required": true @@ -2003,7 +2003,7 @@ func init() { "operationId": "getEnvironmentMetrics", "parameters": [ { - "type": "number", + "type": "string", "name": "envId", "in": "path", "required": true diff --git a/rest_server_zrok/operations/metadata/get_environment_metrics_parameters.go b/rest_server_zrok/operations/metadata/get_environment_metrics_parameters.go index 9b6a9423..6ea94376 100644 --- a/rest_server_zrok/operations/metadata/get_environment_metrics_parameters.go +++ b/rest_server_zrok/operations/metadata/get_environment_metrics_parameters.go @@ -12,7 +12,6 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/strfmt" - "github.com/go-openapi/swag" ) // NewGetEnvironmentMetricsParams creates a new GetEnvironmentMetricsParams object @@ -40,7 +39,7 @@ type GetEnvironmentMetricsParams struct { Required: true In: path */ - EnvID float64 + EnvID string } // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface @@ -96,12 +95,7 @@ func (o *GetEnvironmentMetricsParams) bindEnvID(rawData []string, hasKey bool, f // Required: true // Parameter is provided by construction from the route - - value, err := swag.ConvertFloat64(raw) - if err != nil { - return errors.InvalidType("envId", "path", "float64", raw) - } - o.EnvID = value + o.EnvID = raw return nil } diff --git a/rest_server_zrok/operations/metadata/get_environment_metrics_urlbuilder.go b/rest_server_zrok/operations/metadata/get_environment_metrics_urlbuilder.go index fc8d98fe..a7787fa2 100644 --- a/rest_server_zrok/operations/metadata/get_environment_metrics_urlbuilder.go +++ b/rest_server_zrok/operations/metadata/get_environment_metrics_urlbuilder.go @@ -10,13 +10,11 @@ import ( "net/url" golangswaggerpaths "path" "strings" - - "github.com/go-openapi/swag" ) // GetEnvironmentMetricsURL generates an URL for the get environment metrics operation type GetEnvironmentMetricsURL struct { - EnvID float64 + EnvID string Duration *string @@ -46,7 +44,7 @@ func (o *GetEnvironmentMetricsURL) Build() (*url.URL, error) { var _path = "/metrics/environment/{envId}" - envID := swag.FormatFloat64(o.EnvID) + envID := o.EnvID if envID != "" { _path = strings.Replace(_path, "{envId}", envID, -1) } else { diff --git a/specs/zrok.yml b/specs/zrok.yml index a83947ab..d83417e0 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -425,7 +425,7 @@ paths: parameters: - name: envId in: path - type: number + type: string required: true - name: duration in: query diff --git a/ui/src/api/metadata.js b/ui/src/api/metadata.js index 7a59d7fc..7b8af17a 100644 --- a/ui/src/api/metadata.js +++ b/ui/src/api/metadata.js @@ -56,7 +56,7 @@ export function getAccountMetrics(options) { } /** - * @param {number} envId + * @param {string} envId * @param {object} options Optional options * @param {string} [options.duration] * @return {Promise} environment metrics diff --git a/ui/src/console/detail/account/AccountDetail.js b/ui/src/console/detail/account/AccountDetail.js index 5b2b3675..94b85b8d 100644 --- a/ui/src/console/detail/account/AccountDetail.js +++ b/ui/src/console/detail/account/AccountDetail.js @@ -7,6 +7,7 @@ import React, {useEffect, useState} from "react"; import * as metadata from "../../../api/metadata"; import { Bar, BarChart, CartesianGrid, ResponsiveContainer, XAxis, YAxis } from "recharts"; import moment from "moment"; +import {buildMetrics, bytesToSize} from "../../metrics"; const AccountDetail = (props) => { const customProperties = { @@ -28,7 +29,7 @@ const AccountDetail = (props) => { ); } -const MetricsTab = (props) => { +const MetricsTab = () => { const [metrics30, setMetrics30] = useState(buildMetrics([])); const [metrics7, setMetrics7] = useState(buildMetrics([])); const [metrics1, setMetrics1] = useState(buildMetrics([])); @@ -147,38 +148,4 @@ const MetricsTab = (props) => { ); } -const buildMetrics = (m) => { - let metrics = { - data: m.samples, - rx: 0, - tx: 0 - } - if(m.samples) { - m.samples.forEach(s => { - metrics.rx += s.rx; - metrics.tx += s.tx; - }); - } - return metrics; -} - -const bytesToSize = (sz) => { - let absSz = sz; - if(absSz < 0) { - absSz *= -1; - } - const unit = 1000 - if(absSz < unit) { - return '' + absSz + ' B'; - } - let div = unit - let exp = 0 - for(let n = absSz / unit; n >= unit; n /= unit) { - div *= unit; - exp++; - } - - return '' + (sz / div).toFixed(1) + ' ' + "kMGTPE"[exp] + 'B'; -} - export default AccountDetail; \ No newline at end of file diff --git a/ui/src/console/detail/environment/EnvironmentDetail.js b/ui/src/console/detail/environment/EnvironmentDetail.js index af8865e3..e3861f53 100644 --- a/ui/src/console/detail/environment/EnvironmentDetail.js +++ b/ui/src/console/detail/environment/EnvironmentDetail.js @@ -6,6 +6,7 @@ import {mdiConsoleNetwork} from "@mdi/js"; import {getEnvironmentDetail} from "../../../api/metadata"; import DetailTab from "./DetailTab"; import ActionsTab from "./ActionsTab"; +import MetricsTab from "./MetricsTab"; const EnvironmentDetail = (props) => { const [detail, setDetail] = useState({}); @@ -25,6 +26,9 @@ const EnvironmentDetail = (props) => { + + + diff --git a/ui/src/console/detail/environment/MetricsTab.js b/ui/src/console/detail/environment/MetricsTab.js new file mode 100644 index 00000000..8cee2249 --- /dev/null +++ b/ui/src/console/detail/environment/MetricsTab.js @@ -0,0 +1,129 @@ +import React, {useEffect, useState} from "react"; +import {buildMetrics, bytesToSize} from "../../metrics"; +import * as metadata from "../../../api/metadata"; +import {Col, Container, Row, Tooltip} from "react-bootstrap"; +import {Bar, BarChart, CartesianGrid, ResponsiveContainer, XAxis, YAxis} from "recharts"; +import moment from "moment/moment"; + +const MetricsTab = (props) => { + const [metrics30, setMetrics30] = useState(buildMetrics([])); + const [metrics7, setMetrics7] = useState(buildMetrics([])); + const [metrics1, setMetrics1] = useState(buildMetrics([])); + + console.log("selection", props.selection); + + useEffect(() => { + metadata.getEnvironmentMetrics(props.selection.id) + .then(resp => { + setMetrics30(buildMetrics(resp.data)); + }); + metadata.getEnvironmentMetrics(props.selection.id, {duration: "168h"}) + .then(resp => { + setMetrics7(buildMetrics(resp.data)); + }); + metadata.getEnvironmentMetrics(props.selection.id, {duration: "24h"}) + .then(resp => { + setMetrics1(buildMetrics(resp.data)); + }); + }, []); + + useEffect(() => { + let mounted = true; + let interval = setInterval(() => { + metadata.getEnvironmentMetrics(props.selection.id) + .then(resp => { + if(mounted) { + setMetrics30(buildMetrics(resp.data)); + } + }); + metadata.getEnvironmentMetrics(props.selection.id, {duration: "168h"}) + .then(resp => { + setMetrics7(buildMetrics(resp.data)); + }); + metadata.getEnvironmentMetrics(props.selection.id, {duration: "24h"}) + .then(resp => { + setMetrics1(buildMetrics(resp.data)); + }); + }, 5000); + return () => { + mounted = false; + clearInterval(interval); + } + }, []); + + return ( + + + +

Last 30 Days:

+ +
+ +

Received: {bytesToSize(metrics30.rx)}

+

Sent: {bytesToSize(metrics30.tx)}

+
+ + + + + + v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> + bytesToSize(v)} style={{ fontSize: '75%' }}/> + + + + + + + + + +

Last 7 Days:

+ +
+ +

Received: {bytesToSize(metrics7.rx)}

+

Sent: {bytesToSize(metrics7.tx)}

+
+ + + + + + v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> + bytesToSize(v)} style={{ fontSize: '75%' }}/> + + + + + + + + + +

Last 24 Hours:

+ +
+ +

Received: {bytesToSize(metrics1.rx)}

+

Sent: {bytesToSize(metrics1.tx)}

+
+ + + + + + v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> + bytesToSize(v)} style={{ fontSize: '75%' }}/> + + + + + + + +
+ ); +}; + +export default MetricsTab; \ No newline at end of file diff --git a/ui/src/console/metrics.js b/ui/src/console/metrics.js new file mode 100644 index 00000000..b316a14a --- /dev/null +++ b/ui/src/console/metrics.js @@ -0,0 +1,33 @@ +export const buildMetrics = (m) => { + let metrics = { + data: m.samples, + rx: 0, + tx: 0 + } + if(m.samples) { + m.samples.forEach(s => { + metrics.rx += s.rx; + metrics.tx += s.tx; + }); + } + return metrics; +} + +export const bytesToSize = (sz) => { + let absSz = sz; + if(absSz < 0) { + absSz *= -1; + } + const unit = 1000 + if(absSz < unit) { + return '' + absSz + ' B'; + } + let div = unit + let exp = 0 + for(let n = absSz / unit; n >= unit; n /= unit) { + div *= unit; + exp++; + } + + return '' + (sz / div).toFixed(1) + ' ' + "kMGTPE"[exp] + 'B'; +} \ No newline at end of file