diff --git a/Makefile b/Makefile index fb2da5b6f..e5c5d74cb 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ .ONESHELL: .SHELLFLAGS += -x -e +.PHONY: web PWD = $(shell pwd) UID = $(shell id -u) GID = $(shell id -g) @@ -33,6 +34,8 @@ run: internal/resources/macoui internal/resources/blocky internal/resources/tftp go run ${GO_FLAGS} ${PWD} server # Web +web: web-lint web-build + web-install: cd ${PWD}/web npm ci diff --git a/pkg/roles/dns/types/role.go b/pkg/roles/dns/types/role.go index 45575537b..3c48e33bd 100644 --- a/pkg/roles/dns/types/role.go +++ b/pkg/roles/dns/types/role.go @@ -3,8 +3,9 @@ package types import "time" const ( - KeyRole = "dns" - KeyZones = "zones" + KeyRole = "dns" + KeyZones = "zones" + KeyHandlerType = "handler" ) const ( diff --git a/pkg/roles/dns/zone.go b/pkg/roles/dns/zone.go index 95964b0a7..9adfb4067 100644 --- a/pkg/roles/dns/zone.go +++ b/pkg/roles/dns/zone.go @@ -25,25 +25,22 @@ const ( ) type Zone struct { - inst roles.Instance - role *Role + inst roles.Instance + role *Role + log *zap.Logger + etcdKey string + + h []Handler records map[string]map[string]*Record recordsWatchCtx context.CancelFunc + recordsSync sync.RWMutex - log *zap.Logger - Name string `json:"-"` - - etcdKey string + Name string `json:"-"` HandlerConfigs []map[string]interface{} `json:"handlerConfigs"` - - h []Handler - - recordsSync sync.RWMutex - DefaultTTL uint32 `json:"defaultTTL"` - - Authoritative bool `json:"authoritative"` - Hook string `json:"hook"` + DefaultTTL uint32 `json:"defaultTTL"` + Authoritative bool `json:"authoritative"` + Hook string `json:"hook"` } func (z *Zone) Handlers() []Handler { @@ -70,6 +67,7 @@ func (z *Zone) resolveUpdateMetrics(dur time.Duration, q *utils.DNSRequest, h Ha map[string]interface{}{ "key": z.inst.KV().Key( types.KeyRole, + types.KeyHandlerType, h.Identifier(), ).String(), "default": tsdbTypes.Metric{ @@ -77,6 +75,19 @@ func (z *Zone) resolveUpdateMetrics(dur time.Duration, q *utils.DNSRequest, h Ha }, }, )) + go z.inst.DispatchEvent(tsdbTypes.EventTopicTSDBInc, roles.NewEvent( + context.Background(), + map[string]interface{}{ + "key": z.inst.KV().Key( + types.KeyRole, + types.KeyZones, + z.Name, + ).String(), + "default": tsdbTypes.Metric{ + ResetOnWrite: true, + }, + }, + )) } } diff --git a/pkg/roles/tsdb/api.go b/pkg/roles/tsdb/api.go index 678faaeec..0b4d44bec 100644 --- a/pkg/roles/tsdb/api.go +++ b/pkg/roles/tsdb/api.go @@ -23,6 +23,9 @@ func (r *Role) APIMetrics() usecase.Interactor { if input.Category != "" { pf = pf.Add(input.Category) } + if len(input.ExtraKeys) > 0 { + pf = pf.Add(input.ExtraKeys...) + } prefix := pf.Prefix(true).String() rawMetrics, err := r.i.KV().Get( ctx, @@ -57,7 +60,8 @@ func (r *Role) APIMetrics() usecase.Interactor { v.Value = value } output.Records = append(output.Records, types.APIMetricsRecord{ - Keys: keyParts[:2], + // Remove node and timestamp from keys + Keys: keyParts[:len(keyParts)-2], Time: time.Unix(int64(ts), 0), Node: node, Value: v.Value, diff --git a/pkg/roles/tsdb/types/api.go b/pkg/roles/tsdb/types/api.go index a11169c31..6e6be56e1 100644 --- a/pkg/roles/tsdb/types/api.go +++ b/pkg/roles/tsdb/types/api.go @@ -17,10 +17,11 @@ func (APIMetricsRole) Enum() []interface{} { } type APIMetricsGetInput struct { - Role APIMetricsRole `query:"role" required:"true"` - Category string `query:"category"` - Node string `query:"node"` - Since *time.Time `query:"since" description:"Optionally set a start time for which to return datapoints after"` + Role APIMetricsRole `query:"role" required:"true"` + Category string `query:"category"` + ExtraKeys []string `query:"extraKeys"` + Node string `query:"node"` + Since *time.Time `query:"since" description:"Optionally set a start time for which to return datapoints after"` } type APIMetricsRecord struct { diff --git a/schema.yml b/schema.yml index ae9258ea9..438bd73db 100644 --- a/schema.yml +++ b/schema.yml @@ -1568,6 +1568,12 @@ paths: name: category schema: type: string + - in: query + name: extraKeys + schema: + items: + type: string + type: array - in: query name: node schema: diff --git a/web/src/pages/dns/DNSTableChart.ts b/web/src/pages/dns/DNSTableChart.ts new file mode 100644 index 000000000..07dcf6d87 --- /dev/null +++ b/web/src/pages/dns/DNSTableChart.ts @@ -0,0 +1,96 @@ +import { ChartData, ChartOptions } from "chart.js"; +import { RolesTsdbApi, TypesAPIMetricsGetOutput, TypesAPIMetricsRole } from "gravity-api"; + +import { css } from "lit"; +import { customElement, property } from "lit/decorators.js"; + +import { DEFAULT_CONFIG } from "../../api/Config"; +import { AKChart } from "../../elements/charts/Chart"; + +@customElement("gravity-dns-zone-chart") +export class DNSTableChart extends AKChart { + @property() + zone?: string; + + static get styles() { + return super.styles.concat(css` + :host { + display: flex; + flex-direction: row; + justify-content: end; + } + .container { + width: 20rem; + height: 3rem; + } + `); + } + + async apiRequest(): Promise { + return new RolesTsdbApi(DEFAULT_CONFIG).tsdbGetMetrics({ + role: TypesAPIMetricsRole.Dns, + category: "zones", + extraKeys: [this.zone || ""], + }); + } + + getChartType(): string { + return "line"; + } + + firstUpdated(): void { + super.firstUpdated(); + if (!this.parentElement) { + return; + } + this.parentElement.style.width = "28rem"; + this.parentElement.style.padding = "0"; + } + + getOptions(): ChartOptions { + return { + maintainAspectRatio: false, + plugins: { + legend: { + display: false, + }, + }, + layout: { + padding: 0, + }, + scales: { + x: { + type: "time", + display: false, + }, + y: { + type: "linear", + display: false, + }, + }, + } as ChartOptions; + } + + getChartData(data: TypesAPIMetricsGetOutput): ChartData { + const chartData: ChartData = { + datasets: [], + }; + chartData.datasets.push({ + backgroundColor: "rgba(0,0,0,0)", + borderColor: "#3873e0", + spanGaps: true, + fill: "origin", + cubicInterpolationMode: "monotone", + tension: 0.4, + pointStyle: false, + data: + data.records?.map((record) => { + return { + x: record.time.getTime(), + y: record.value, + }; + }) || [], + }); + return chartData; + } +} diff --git a/web/src/pages/dns/DNSZonesPage.ts b/web/src/pages/dns/DNSZonesPage.ts index 250e64883..54354b783 100644 --- a/web/src/pages/dns/DNSZonesPage.ts +++ b/web/src/pages/dns/DNSZonesPage.ts @@ -9,6 +9,7 @@ import "../../elements/forms/ModalForm"; import { PaginatedResponse, TableColumn } from "../../elements/table/Table"; import { TablePage } from "../../elements/table/TablePage"; import { PaginationWrapper } from "../../utils"; +import "./DNSTableChart"; import "./DNSZoneForm"; import "./wizard/DNSZoneWizard"; @@ -47,6 +48,7 @@ export class DNSZonesPage extends TablePage { new TableColumn("Zone"), new TableColumn("Records"), new TableColumn("Authoritative"), + new TableColumn(""), new TableColumn("Actions"), ]; } @@ -58,6 +60,7 @@ export class DNSZonesPage extends TablePage { `, html`${item.recordCount}`, html`${item.authoritative ? "Yes" : "No"}`, + html``, html` ${"Update"} ${"Update Zone"} diff --git a/web/src/pages/dns/wizard/ZoneImportWizardPage.ts b/web/src/pages/dns/wizard/ZoneImportWizardPage.ts index 47e62ab48..53d65af6a 100644 --- a/web/src/pages/dns/wizard/ZoneImportWizardPage.ts +++ b/web/src/pages/dns/wizard/ZoneImportWizardPage.ts @@ -4,11 +4,11 @@ import { customElement } from "@lit/reactive-element/decorators/custom-element.j import { TemplateResult, html } from "lit"; import { DEFAULT_CONFIG } from "../../../api/Config"; +import { convertToTitle } from "../../../common/utils"; import { KeyUnknown } from "../../../elements/forms/Form"; import "../../../elements/forms/FormGroup"; import "../../../elements/forms/HorizontalFormElement"; import { WizardFormPage } from "../../../elements/wizard/WizardFormPage"; -import { convertToTitle } from "../../../common/utils"; @customElement("gravity-dns-wizard-import") export class ZoneImportWizardPage extends WizardFormPage { diff --git a/web/src/pages/overview/charts/DNSRequestsChart.ts b/web/src/pages/overview/charts/DNSRequestsChart.ts index fde7f9a9b..f6fad628a 100644 --- a/web/src/pages/overview/charts/DNSRequestsChart.ts +++ b/web/src/pages/overview/charts/DNSRequestsChart.ts @@ -11,7 +11,10 @@ import { AKChart } from "../../../elements/charts/Chart"; @customElement("gravity-overview-charts-dns-requests") export class DNSRequestsChart extends AKChart { apiRequest(): Promise { - return new RolesTsdbApi(DEFAULT_CONFIG).tsdbGetMetrics({ role: TypesAPIMetricsRole.Dns }); + return new RolesTsdbApi(DEFAULT_CONFIG).tsdbGetMetrics({ + role: TypesAPIMetricsRole.Dns, + category: "handler", + }); } getChartType(): string { @@ -23,7 +26,13 @@ export class DNSRequestsChart extends AKChart { datasets: [], }; groupBy(data.records || [], (record) => record.node).forEach(([node, records]) => { - groupBy(records, (record) => record.keys![1]).forEach(([handler, records]) => { + groupBy(records, (record) => { + // TODO: Remove in the future + if (record.keys?.length === 3) { + return record.keys![1] && record.keys![2]; + } + return record.keys![1]; + }).forEach(([handler, records]) => { const background = getColorFromString(handler); background.a = 0.3; chartData.datasets.push({