From d3fcb9370543b88a83a66d67524c4740fdc0f873 Mon Sep 17 00:00:00 2001 From: Julien Pinsonneau Date: Tue, 13 Dec 2022 18:21:19 +0100 Subject: [PATCH] server unix timestamp --- pkg/handler/topology.go | 1 + pkg/model/loki.go | 9 +++++---- pkg/model/loki_test.go | 10 +++++----- web/src/api/loki.ts | 1 + web/src/api/routes.ts | 8 +++++++- web/src/components/__tests-data__/metrics.ts | 2 +- .../netflow-topology/__tests-data__/metrics.ts | 3 ++- web/src/utils/__tests__/metrics.spec.ts | 8 ++++---- web/src/utils/metrics.ts | 8 +++++--- 9 files changed, 31 insertions(+), 19 deletions(-) diff --git a/pkg/handler/topology.go b/pkg/handler/topology.go index 3b28260de..530094c42 100644 --- a/pkg/handler/topology.go +++ b/pkg/handler/topology.go @@ -111,6 +111,7 @@ func getTopologyFlows(cfg *loki.Config, client httpclient.Caller, params url.Val qr := merger.Get() qr.IsMock = cfg.UseMocks + qr.UnixTimestamp = time.Now().Unix() hlog.Tracef("GetTopology response: %v", qr) return qr, http.StatusOK, nil } diff --git a/pkg/model/loki.go b/pkg/model/loki.go index 7f583d7c0..a396e9b80 100644 --- a/pkg/model/loki.go +++ b/pkg/model/loki.go @@ -17,10 +17,11 @@ type QueryResponse struct { // AggregatedQueryResponse represents the modified json response to one or more logQL queries type AggregatedQueryResponse struct { - ResultType ResultType `json:"resultType"` - Result ResultValue `json:"result"` - Stats AggregatedStats `json:"stats"` - IsMock bool `json:"isMock"` + ResultType ResultType `json:"resultType"` + Result ResultValue `json:"result"` + Stats AggregatedStats `json:"stats"` + IsMock bool `json:"isMock"` + UnixTimestamp int64 `json:"unixTimestamp"` } // AggregatedStats represents the stats to one or more logQL queries diff --git a/pkg/model/loki_test.go b/pkg/model/loki_test.go index 3f8d2a467..895df300f 100644 --- a/pkg/model/loki_test.go +++ b/pkg/model/loki_test.go @@ -44,11 +44,11 @@ func TestAggregatedQueryResponseMarshal(t *testing.T) { js, err := json.Marshal(qr) require.NoError(t, err) - assert.Equal(t, `{"resultType":"streams","result":[],"stats":{"numQueries":1,"limitReached":false,"queriesStats":null},"isMock":false}`, string(js)) + assert.Equal(t, `{"resultType":"streams","result":[],"stats":{"numQueries":1,"limitReached":false,"queriesStats":null},"isMock":false,"unixTimestamp":0}`, string(js)) } func TestAggregatedQueryResponseUnmarshal(t *testing.T) { - js := `{"resultType":"streams","result":[],"stats":{"numQueries":1,"limitReached":false,"queriesStats":null},"isMock":false}` + js := `{"resultType":"streams","result":[],"stats":{"numQueries":1,"limitReached":false,"queriesStats":null},"isMock":false,"unixTimestamp":0}` var qr AggregatedQueryResponse err := json.Unmarshal([]byte(js), &qr) require.NoError(t, err) @@ -95,11 +95,11 @@ func TestAggregatedQueryResponseMatrixMarshal(t *testing.T) { js, err := json.Marshal(qr) require.NoError(t, err) - assert.Equal(t, `{"resultType":"matrix","result":[],"stats":{"numQueries":1,"limitReached":false,"queriesStats":null},"isMock":false}`, string(js)) + assert.Equal(t, `{"resultType":"matrix","result":[],"stats":{"numQueries":1,"limitReached":false,"queriesStats":null},"isMock":false,"unixTimestamp":0}`, string(js)) } func TestAggregatedQueryResponseMatrixUnmarshal(t *testing.T) { - js := `{"resultType":"matrix","result":[],"stats":{"numQueries":1,"limitReached":false,"queriesStats":null},"isMock":false}` + js := `{"resultType":"matrix","result":[],"stats":{"numQueries":1,"limitReached":false,"queriesStats":null},"isMock":false,"unixTimestamp":0}` var qr AggregatedQueryResponse err := json.Unmarshal([]byte(js), &qr) require.NoError(t, err) @@ -126,5 +126,5 @@ func TestReencodeStats(t *testing.T) { } reencoded, err := json.Marshal(agg) require.NoError(t, err) - assert.Equal(t, `{"resultType":"streams","result":[],"stats":{"numQueries":1,"limitReached":false,"queriesStats":[{"ingester":{"foo":"bar"}}]},"isMock":false}`, string(reencoded)) + assert.Equal(t, `{"resultType":"streams","result":[],"stats":{"numQueries":1,"limitReached":false,"queriesStats":[{"ingester":{"foo":"bar"}}]},"isMock":false,"unixTimestamp":0}`, string(reencoded)) } diff --git a/web/src/api/loki.ts b/web/src/api/loki.ts index e8f539f81..c0506331c 100644 --- a/web/src/api/loki.ts +++ b/web/src/api/loki.ts @@ -7,6 +7,7 @@ export interface AggregatedQueryResponse { result: StreamResult[] | RawTopologyMetrics[]; stats: Stats; isMock: boolean; + unixTimestamp: number; } export interface Stats { diff --git a/web/src/api/routes.ts b/web/src/api/routes.ts index e84c57726..f65957e93 100644 --- a/web/src/api/routes.ts +++ b/web/src/api/routes.ts @@ -59,7 +59,13 @@ export const getTopology = (params: FlowQuery, range: number | TimeRange): Promi throw new Error(`${r.statusText} [code=${r.status}]`); } const aggQR: AggregatedQueryResponse = r.data; - const metrics = parseMetrics(aggQR.result as RawTopologyMetrics[], range, params.scope!, aggQR.isMock); + const metrics = parseMetrics( + aggQR.result as RawTopologyMetrics[], + range, + params.scope!, + aggQR.unixTimestamp, + aggQR.isMock + ); return { metrics: metrics, stats: aggQR.stats }; }); }; diff --git a/web/src/components/__tests-data__/metrics.ts b/web/src/components/__tests-data__/metrics.ts index af9b4af6f..cefb4c17c 100644 --- a/web/src/components/__tests-data__/metrics.ts +++ b/web/src/components/__tests-data__/metrics.ts @@ -79,4 +79,4 @@ export const metric3: RawTopologyMetrics = { ] }; -export const metrics = parseMetrics([metric1, metric2, metric3], { from: 1653989800, to: 1653990100 }, 'resource'); +export const metrics = parseMetrics([metric1, metric2, metric3], { from: 1653989800, to: 1653990100 }, 'resource', 0); diff --git a/web/src/components/netflow-topology/__tests-data__/metrics.ts b/web/src/components/netflow-topology/__tests-data__/metrics.ts index 1ebd7148f..e37799008 100644 --- a/web/src/components/netflow-topology/__tests-data__/metrics.ts +++ b/web/src/components/netflow-topology/__tests-data__/metrics.ts @@ -282,5 +282,6 @@ export const responseSample = { export const dataSample = parseMetrics( responseSample.data.result as RawTopologyMetrics[], { from: 1647965100, to: 1647965400 }, - 'resource' + 'resource', + 0 ); diff --git a/web/src/utils/__tests__/metrics.spec.ts b/web/src/utils/__tests__/metrics.spec.ts index 966a99f26..20d95484f 100644 --- a/web/src/utils/__tests__/metrics.spec.ts +++ b/web/src/utils/__tests__/metrics.spec.ts @@ -36,7 +36,7 @@ describe('normalize and computeStats', () => { [1664372300, '8'] ]; - const { start, end, step } = calibrateRange([values], { from: 1664372000, to: 1664372300 }); + const { start, end, step } = calibrateRange([values], { from: 1664372000, to: 1664372300 }, 1664372300, true); const norm = normalizeMetrics(values, start, end, step); expect(norm).toEqual([ [1664372000, 5], @@ -98,7 +98,7 @@ describe('normalize and computeStats', () => { [first + 285, '8'] ]; - const { start, end, step } = calibrateRange([values], 300); + const { start, end, step } = calibrateRange([values], 300, now, true); const norm = normalizeMetrics(values, start, end, step); expect(norm).toEqual([ [first, 5], @@ -150,7 +150,7 @@ describe('normalize and computeStats', () => { [1664372300, '8'] ]; - const { start, end, step } = calibrateRange([values], { from: 1664372000, to: 1664372300 }); + const { start, end, step } = calibrateRange([values], { from: 1664372000, to: 1664372300 }, 1664372300, true); const norm = normalizeMetrics(values, start, end, step); expect(norm).toEqual([ [1664372000, 5], @@ -382,7 +382,7 @@ describe('parseMetrics', () => { } ]; - const parsed = parseMetrics(metrics, 300, 'resource'); + const parsed = parseMetrics(metrics, 300, 'resource', 0, true); expect(parsed).toHaveLength(2); expect(parsed[0].source.getDisplayName(true, true)).toEqual('ns1.A'); diff --git a/web/src/utils/metrics.ts b/web/src/utils/metrics.ts index 27ca959d7..68c1fc469 100644 --- a/web/src/utils/metrics.ts +++ b/web/src/utils/metrics.ts @@ -23,11 +23,13 @@ export const parseMetrics = ( raw: RawTopologyMetrics[], range: number | TimeRange, scope: MetricScope, + unixTimestamp: number, isMock?: boolean ): TopologyMetrics[] => { const { start, end, step } = calibrateRange( raw.map(r => r.values), range, + unixTimestamp, isMock ); const metrics = raw.map(r => parseMetric(r, start, end, step, scope)); @@ -153,6 +155,7 @@ const parseMetric = ( export const calibrateRange = ( raw: [number, unknown][][], range: number | TimeRange, + unixTimestamp: number, isMock?: boolean ): { start: number; end: number; step: number } => { // Extract some info based on range, and apply a tolerance about end range when it is close to "now" @@ -161,9 +164,8 @@ export const calibrateRange = ( let start: number; let endWithTolerance: number; if (typeof range === 'number') { - const end = Math.floor(new Date().getTime() / 1000); - endWithTolerance = end - latencyTolerance; - start = end - rangeInSeconds; + endWithTolerance = unixTimestamp - latencyTolerance; + start = unixTimestamp - rangeInSeconds; } else { start = range.from; endWithTolerance = range.to;