From cac68ec6876dac127144f0b8f9c547da32e6f627 Mon Sep 17 00:00:00 2001 From: Gabriel Bernal Date: Thu, 15 May 2025 09:45:19 +0200 Subject: [PATCH 01/35] chore: bump package version and remove unused dockerfile --- Dockerfile | 2 +- Dockerfile.konflux | 53 ---------------------------------------------- web/package.json | 2 +- 3 files changed, 2 insertions(+), 55 deletions(-) delete mode 100644 Dockerfile.konflux diff --git a/Dockerfile b/Dockerfile index 7dc474c..b65d8fe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ RUN make install-frontend-ci-clean COPY web/ web/ RUN make build-frontend -FROM registry.ci.openshift.org/ocp/builder:rhel-8-golang-1.22-openshift-4.17 as go-builder +FROM brew.registry.redhat.io/rh-osbs/openshift-golang-builder:rhel_9_golang_1.23 as go-builder WORKDIR /opt/app-root diff --git a/Dockerfile.konflux b/Dockerfile.konflux deleted file mode 100644 index 5df7805..0000000 --- a/Dockerfile.konflux +++ /dev/null @@ -1,53 +0,0 @@ -FROM registry.redhat.io/ubi9/nodejs-18:latest AS web-builder - -WORKDIR /opt/app-root - -USER 0 - -ENV HUSKY=0 -COPY web/package*.json web/ -COPY Makefile Makefile -RUN make install-frontend-ci - -COPY web/ web/ -RUN make build-frontend - -FROM brew.registry.redhat.io/rh-osbs/openshift-golang-builder:rhel_9_1.22 as go-builder - -WORKDIR /opt/app-root - -COPY Makefile Makefile -COPY go.mod go.mod -COPY go.sum go.sum - -RUN make install-backend - -COPY cmd/ cmd/ -COPY pkg/ pkg/ - -ENV GOFLAGS='-mod=mod' -ENV GOEXPERIMENT=strictfipsruntime -ENV CGO_ENABLED=1 - -RUN make build-backend BUILD_OPTS="-tags strictfipsruntime" - -FROM registry.redhat.io/rhel9-2-els/rhel:9.2 - -RUN mkdir /licenses -COPY LICENSE /licenses/. - -USER 1001 - -COPY --from=web-builder /opt/app-root/web/dist /opt/app-root/web/dist -COPY --from=go-builder /opt/app-root/plugin-backend /opt/app-root - -ENTRYPOINT ["/opt/app-root/plugin-backend", "-static-path", "/opt/app-root/web/dist", "-config-path", "/opt/app-root/web/dist"] - -LABEL com.redhat.component="coo-distributed-tracing-console-plugin" \ - name="openshift/distributed-tracing-console-plugin" \ - version="v0.3.0" \ - summary="OpenShift console plugin to view and explore traces" \ - io.openshift.tags="openshift,observability-ui,distributed tracing" \ - io.k8s.display-name="OpenShift console distributed tracing plugin" \ - maintainer="Observability UI Team " \ - description="OpenShift console plugin to view and explore traces" diff --git a/web/package.json b/web/package.json index 2d539a7..add32f5 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "distributed-tracing-console-plugin", - "version": "0.0.1", + "version": "1.0.0", "private": true, "repository": "git@github.com:openshift/distributed-tracing-console-plugin.git", "license": "Apache-2.0", From 29d46003f917755a7efa7913765ed5e67896af6e Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 15 May 2025 12:08:40 +0200 Subject: [PATCH 02/35] OU-804: Add help text to namespace filter Signed-off-by: Andreas Gerstmayr --- ...n__distributed-tracing-console-plugin.json | 7 +- web/package-lock.json | 139 +++++------------- web/package.json | 1 + .../TracesPage/Toolbar/AttributeFilters.tsx | 68 ++++++--- 4 files changed, 92 insertions(+), 123 deletions(-) diff --git a/web/locales/en/plugin__distributed-tracing-console-plugin.json b/web/locales/en/plugin__distributed-tracing-console-plugin.json index 47ee308..e9584cc 100644 --- a/web/locales/en/plugin__distributed-tracing-console-plugin.json +++ b/web/locales/en/plugin__distributed-tracing-console-plugin.json @@ -16,15 +16,18 @@ "TempoStack and TempoMonolithic instances with multi-tenancy are supported. Instances without multi-tenancy are not supported.": "TempoStack and TempoMonolithic instances with multi-tenancy are supported. Instances without multi-tenancy are not supported.", "Tenant": "Tenant", "Select a tenant": "Select a tenant", - "Trace": "Trace", - "Tracing": "Tracing", "Traces": "Traces", "Trace details": "Trace details", + "Trace": "Trace", + "Tracing": "Tracing", "Limit traces": "Limit traces", "No datapoints found.": "No datapoints found.", "Hide graph": "Hide graph", "Show graph": "Show graph", "Filter": "Filter", + "Namespace": "Namespace", + "Filter by namespace": "Filter by namespace", + "This filter is based on the <1>k8s.namespace.name resource attribute. To set this attribute, it is recommended to enable the <4>Kubernetes Attributes Processor in your OpenTelemetry Collector pipeline.": "This filter is based on the <1>k8s.namespace.name resource attribute. To set this attribute, it is recommended to enable the <4>Kubernetes Attributes Processor in your OpenTelemetry Collector pipeline.", "between {{min}} and {{max}}": "between {{min}} and {{max}}", "greater than {{min}}": "greater than {{min}}", "less than {{max}}": "less than {{max}}", diff --git a/web/package-lock.json b/web/package-lock.json index e5c32b4..91ed4fb 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -8517,31 +8517,6 @@ "node": ">= 0.8.0" } }, - "node_modules/cheerio": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", - "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==", - "license": "MIT", - "dependencies": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.1.0", - "encoding-sniffer": "^0.2.0", - "htmlparser2": "^9.1.0", - "parse5": "^7.1.2", - "parse5-htmlparser2-tree-adapter": "^7.0.0", - "parse5-parser-stream": "^7.1.2", - "undici": "^6.19.5", - "whatwg-mimetype": "^4.0.0" - }, - "engines": { - "node": ">=18.17" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" - } - }, "node_modules/cheerio-select": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", @@ -10378,19 +10353,6 @@ "node": ">= 0.8" } }, - "node_modules/encoding-sniffer": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz", - "integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==", - "license": "MIT", - "dependencies": { - "iconv-lite": "^0.6.3", - "whatwg-encoding": "^3.1.1" - }, - "funding": { - "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" - } - }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -12989,25 +12951,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/htmlparser2": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", - "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.1.0", - "entities": "^4.5.0" - } - }, "node_modules/http-assert": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz", @@ -13230,6 +13173,27 @@ "yarn": ">=1" } }, + "node_modules/i18next-parser/node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, "node_modules/i18next-parser/node_modules/commander": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", @@ -13253,6 +13217,25 @@ "node": ">=14.14" } }, + "node_modules/i18next-parser/node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, "node_modules/i18next-parser/node_modules/typescript": { "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", @@ -18929,18 +18912,6 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/parse5-parser-stream": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", - "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", - "license": "MIT", - "dependencies": { - "parse5": "^7.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, "node_modules/parse5/node_modules/entities": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", @@ -25394,15 +25365,6 @@ "node": "*" } }, - "node_modules/undici": { - "version": "6.21.3", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", - "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==", - "license": "MIT", - "engines": { - "node": ">=18.17" - } - }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -26765,18 +26727,6 @@ "node": ">=0.8.0" } }, - "node_modules/whatwg-encoding": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", - "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", - "license": "MIT", - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/whatwg-fetch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", @@ -26784,15 +26734,6 @@ "dev": true, "license": "MIT" }, - "node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", diff --git a/web/package.json b/web/package.json index 2d539a7..f33cb73 100644 --- a/web/package.json +++ b/web/package.json @@ -106,6 +106,7 @@ "@perses-dev/dashboards": "0.51.0-rc.0", "@perses-dev/plugin-system": "0.51.0-rc.0", "@tanstack/react-query": "4.36.1", + "cheerio":"1.0.0-rc.12", "react-router-dom": "5.3.4" } } diff --git a/web/src/pages/TracesPage/Toolbar/AttributeFilters.tsx b/web/src/pages/TracesPage/Toolbar/AttributeFilters.tsx index 6d3e97a..af806bb 100644 --- a/web/src/pages/TracesPage/Toolbar/AttributeFilters.tsx +++ b/web/src/pages/TracesPage/Toolbar/AttributeFilters.tsx @@ -8,7 +8,7 @@ import { FormGroup, } from '@patternfly/react-core'; import { FilterIcon, HelpIcon } from '@patternfly/react-icons'; -import { useTranslation } from 'react-i18next'; +import { Trans, useTranslation } from 'react-i18next'; import { TempoInstance } from '../../../hooks/useTempoInstance'; import { ControlledSimpleSelect } from '../../../components/ControlledSelects'; import { TypeaheadSelectOption } from '@patternfly/react-templates'; @@ -21,6 +21,10 @@ import { traceQLToFilter } from './Filter/traceql_to_filter'; import { TypeaheadCheckboxSelect } from '../../../components/TypeaheadCheckboxSelect'; import { DurationField, Filter, splitByUnquotedWhitespace } from './Filter/filter'; import { useTagValues } from '../../../hooks/useTagValues'; +import { Link } from 'react-router-dom-v5-compat'; + +const k8sAttributesProcessorLink = + 'https://docs.redhat.com/en/documentation/openshift_container_platform/latest/html/red_hat_build_of_opentelemetry/configuring-the-collector#kubernetes-attributes-processor_otel-collector-processors'; const serviceNameFilter = { content: 'Service Name', value: 'serviceName' }; const spanNameFilter = { content: 'Span Name', value: 'spanName' }; @@ -98,47 +102,65 @@ export function AttributeFilters(props: AttributeFiltersProps) { setFilter({ ...filter, serviceName: x })} /> setFilter({ ...filter, spanName: x })} /> {t('Filter by namespace')}} + bodyContent={ +
+ + This filter is based on the k8s.namespace.name resource attribute. To + set this attribute, it is recommended to enable the{' '} + Kubernetes Attributes Processor in + your OpenTelemetry Collector pipeline. + +
+ } + > + + + } show={activeFilter === namespaceFilter.value} options={namespaceOptions ?? []} value={filter.namespace} setValue={(x) => setFilter({ ...filter, namespace: x })} /> setFilter({ ...filter, status: x })} /> setFilter({ ...filter, spanDuration: value })} /> setFilter({ ...filter, traceDuration: value })} /> setFilter({ ...filter, customMatchers: value })} @@ -148,7 +170,9 @@ export function AttributeFilters(props: AttributeFiltersProps) { } interface TypeaheadStringAttributeFilterProps { - label: string; + filterName: string; + label?: React.ReactNode; + labelHelp?: React.ReactElement; show?: boolean; options: TypeaheadSelectOption[]; value: string[]; @@ -156,22 +180,24 @@ interface TypeaheadStringAttributeFilterProps { } function TypeaheadStringAttributeFilter(props: TypeaheadStringAttributeFilterProps) { - const { label, show, options, value, setValue } = props; + const { filterName, label, labelHelp, show, options, value, setValue } = props; return ( setValue(value.filter((x) => x !== label))} deleteLabelGroup={() => setValue([])} - categoryName={label} + categoryName={filterName} showToolbarItem={show} >
-  }> +  } labelHelp={labelHelp}> 0 ? ' (' + value.length + ')' : ''}`} + placeholder={`Filter by ${filterName}${ + value.length > 0 ? ' (' + value.length + ')' : '' + }`} options={options} value={value} setValue={setValue} @@ -184,7 +210,7 @@ function TypeaheadStringAttributeFilter(props: TypeaheadStringAttributeFilterPro } interface DurationAttributeFilterProps { - label: string; + filterName: string; show?: boolean; value: DurationField; setValue: (value: DurationField) => void; @@ -192,7 +218,7 @@ interface DurationAttributeFilterProps { function DurationAttributeFilter(props: DurationAttributeFilterProps) { const { t } = useTranslation('plugin__distributed-tracing-console-plugin'); - const { label, show, value, setValue } = props; + const { filterName, show, value, setValue } = props; const { min, max } = value; let labels: string[] = []; @@ -209,7 +235,7 @@ function DurationAttributeFilter(props: DurationAttributeFilterProps) { labels={labels} deleteLabel={() => setValue({ min: undefined, max: undefined })} deleteLabelGroup={() => setValue({ min: undefined, max: undefined })} - categoryName={label} + categoryName={filterName} showToolbarItem={show} > @@ -261,14 +287,14 @@ export function DurationTextInput(props: DurationTextInputProps) { } interface CustomAttributesFilterProps { - label: string; + filterName: string; show?: boolean; value: string[]; setValue: (value: string[]) => void; } function CustomAttributesFilter(props: CustomAttributesFilterProps) { - const { label, show, value, setValue } = props; + const { filterName, show, value, setValue } = props; const { t } = useTranslation('plugin__distributed-tracing-console-plugin'); return ( @@ -276,7 +302,7 @@ function CustomAttributesFilter(props: CustomAttributesFilterProps) { labels={value} deleteLabel={(_category, label) => setValue(value.filter((x) => x !== label))} deleteLabelGroup={() => setValue([])} - categoryName={label} + categoryName={filterName} showToolbarItem={show} > @@ -290,10 +316,8 @@ function CustomAttributesFilter(props: CustomAttributesFilterProps) {
{t( 'Attributes are written in the form key=value and are combined via AND. Multiple attributes can be separated via space. String values must be quoted. Example:', - )} -
-                    {'span.http.status_code=200 span.http.method="GET" duration>5s'}
-                  
+ )}{' '} + {'span.http.status_code=200 span.http.method="GET" duration>5s'}
} > From bb115c50c7ebeaaa0de3f18684428476a6beed28 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 15 May 2025 13:38:24 +0200 Subject: [PATCH 03/35] OU-805: account for selected time range when showing attribute values Signed-off-by: Andreas Gerstmayr --- web/src/hooks/useTagValues.ts | 6 ++++-- .../pages/TracesPage/Toolbar/AttributeFilters.tsx | 13 +++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/web/src/hooks/useTagValues.ts b/web/src/hooks/useTagValues.ts index a05e0bf..5303ad8 100644 --- a/web/src/hooks/useTagValues.ts +++ b/web/src/hooks/useTagValues.ts @@ -8,14 +8,16 @@ export function useTagValues( tag: string, query: string, enabled: boolean, + start?: number, + end?: number, ) { return useQuery({ - queryKey: ['useTagValues', tempo, tag, query], + queryKey: ['useTagValues', tempo, tag, query, start, end], enabled, queryFn: async function () { if (!tempo) return []; const client = TempoDatasource.createClient({ directUrl: getProxyURLFor(tempo) }, {}); - const values = await client.searchTagValues({ tag, q: query }); + const values = await client.searchTagValues({ tag, q: query, start, end }); return values.tagValues .map((tagValue) => ({ content: tagValue.value, diff --git a/web/src/pages/TracesPage/Toolbar/AttributeFilters.tsx b/web/src/pages/TracesPage/Toolbar/AttributeFilters.tsx index 6d3e97a..56e97f3 100644 --- a/web/src/pages/TracesPage/Toolbar/AttributeFilters.tsx +++ b/web/src/pages/TracesPage/Toolbar/AttributeFilters.tsx @@ -21,6 +21,8 @@ import { traceQLToFilter } from './Filter/traceql_to_filter'; import { TypeaheadCheckboxSelect } from '../../../components/TypeaheadCheckboxSelect'; import { DurationField, Filter, splitByUnquotedWhitespace } from './Filter/filter'; import { useTagValues } from '../../../hooks/useTagValues'; +import { useTimeRange } from '@perses-dev/plugin-system'; +import { isAbsoluteTimeRange, toAbsoluteTimeRange } from '@perses-dev/core'; const serviceNameFilter = { content: 'Service Name', value: 'serviceName' }; const spanNameFilter = { content: 'Span Name', value: 'spanName' }; @@ -63,23 +65,34 @@ export function AttributeFilters(props: AttributeFiltersProps) { setQuery(filterToTraceQL(filter)); }; + const { timeRange } = useTimeRange(); + const absTimeRange = !isAbsoluteTimeRange(timeRange) ? toAbsoluteTimeRange(timeRange) : timeRange; + const startTime = Math.round(absTimeRange.start.getTime() / 1000); + const endTime = Math.round(absTimeRange.end.getTime() / 1000); + const { data: serviceNameOptions } = useTagValues( tempo, 'resource.service.name', filterToTraceQL({ ...filter, serviceName: [] }), activeFilter === serviceNameFilter.value, + startTime, + endTime, ); const { data: spanNameOptions } = useTagValues( tempo, 'name', filterToTraceQL({ ...filter, spanName: [] }), activeFilter === spanNameFilter.value, + startTime, + endTime, ); const { data: namespaceOptions } = useTagValues( tempo, 'resource.k8s.namespace.name', filterToTraceQL({ ...filter, namespace: [] }), activeFilter === namespaceFilter.value, + startTime, + endTime, ); return ( From 3e4c12f6d1cd4be03de77fcdbba65f68f67b2b2c Mon Sep 17 00:00:00 2001 From: PeterYurkovich Date: Thu, 15 May 2025 08:58:17 -0400 Subject: [PATCH 04/35] add andreasgerstmayr to owners --- OWNERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OWNERS b/OWNERS index 09656f6..f6ea503 100644 --- a/OWNERS +++ b/OWNERS @@ -4,9 +4,11 @@ reviewers: - zhuje - peteryurkovich - IshwarKanse + - andreasgerstmayr approvers: - jgbernalp - kyoto - zhuje - peteryurkovich - IshwarKanse + - andreasgerstmayr From 7ec3b879944da1888b7d52676c5c38e0456fc51c Mon Sep 17 00:00:00 2001 From: Ishwar Kanse Date: Fri, 16 May 2025 10:57:43 +0530 Subject: [PATCH 05/35] Use RHEL 8 golang builder to resolve GLIBC_2.32 not found issue --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index b65d8fe..ad0865c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,7 +29,7 @@ ENV CGO_ENABLED=1 RUN make build-backend BUILD_OPTS="-tags strictfipsruntime" -FROM registry.access.redhat.com/ubi8/ubi +FROM registry.access.redhat.com/ubi9/ubi-minimal COPY --from=web-builder /opt/app-root/web/dist /opt/app-root/web/dist COPY --from=go-builder /opt/app-root/plugin-backend /opt/app-root From ab3c837795af3d6be25f7be8be9bf508b948a92a Mon Sep 17 00:00:00 2001 From: Ishwar Kanse Date: Thu, 15 May 2025 21:37:19 +0530 Subject: [PATCH 06/35] Update tests after the filter feature --- tests/cypress.config.ts | 2 +- tests/fixtures/update-plugin-image.sh | 13 ++- tests/tests/dt-plugin-tests.cy.ts | 138 +++++++++++++++----------- 3 files changed, 90 insertions(+), 63 deletions(-) diff --git a/tests/cypress.config.ts b/tests/cypress.config.ts index b254389..c8c6c36 100644 --- a/tests/cypress.config.ts +++ b/tests/cypress.config.ts @@ -22,7 +22,7 @@ export default defineConfig({ LOGIN_PASSWORD: process.env.CYPRESS_LOGIN_USERS.split(',')[0].split(':')[1], }, fixturesFolder: 'fixtures', - defaultCommandTimeout: 30000, + defaultCommandTimeout: 40000, retries: { runMode: 0, openMode: 0, diff --git a/tests/fixtures/update-plugin-image.sh b/tests/fixtures/update-plugin-image.sh index 4f7dd15..5d3daaa 100755 --- a/tests/fixtures/update-plugin-image.sh +++ b/tests/fixtures/update-plugin-image.sh @@ -7,9 +7,16 @@ COO_CSV_NAME=$(oc get csv --kubeconfig "${KUBECONFIG}" --namespace="${DTP_NAMESP oc get csv "${COO_CSV_NAME}" -n "${DTP_NAMESPACE}" -o yaml > "${RANDOM_FILE}" --kubeconfig "${KUBECONFIG}" +# Patch the CSV file env vars sed -i "s#value: .*distributed-tracing-console-plugin.*#value: ${DT_CONSOLE_IMAGE}#g" "${RANDOM_FILE}" -oc apply -f "${RANDOM_FILE}" --kubeconfig "${KUBECONFIG}" +# Patch the CSV file related images +sed -i "s#^\([[:space:]]*- image:\).*distributed-tracing-console-plugin.*#\1 ${DT_CONSOLE_IMAGE}#g" "${RANDOM_FILE}" -oc wait --for=condition=ready pod -l app.kubernetes.io/instance=distributed-tracing -n "${DTP_NAMESPACE}" --timeout=60s --kubeconfig "${KUBECONFIG}" -oc wait --for=condition=ready pod -l app.kubernetes.io/name=observability-operator -n "${DTP_NAMESPACE}" --timeout=60s --kubeconfig "${KUBECONFIG}" \ No newline at end of file +# Apply the patched CSV resource file +oc replace -f "${RANDOM_FILE}" --kubeconfig "${KUBECONFIG}" + +# Wait for the operator to reconcile the change and make sure all the pods are running. +sleep 5 +oc wait --for=condition=Ready pods --selector=app.kubernetes.io/part-of=observability-operator -n "${DTP_NAMESPACE}" --timeout=60s +oc wait --for=condition=ready pods -l app.kubernetes.io/name=observability-operator -n "${DTP_NAMESPACE}" --timeout=60s --kubeconfig "${KUBECONFIG}" diff --git a/tests/tests/dt-plugin-tests.cy.ts b/tests/tests/dt-plugin-tests.cy.ts index 5048a88..9eda732 100644 --- a/tests/tests/dt-plugin-tests.cy.ts +++ b/tests/tests/dt-plugin-tests.cy.ts @@ -139,6 +139,7 @@ describe('OpenShift Distributed Tracing UI Plugin tests', () => { throw new Error('No CYPRESS env set for operator installation, check the README for more details.'); } + cy.log('Set Distributed Tracing Console Plugin image in operator CSV'); if (Cypress.env('DT_CONSOLE_IMAGE')) { cy.log('DT_CONSOLE_IMAGE is set. the image will be patched in COO operator CSV'); cy.exec( @@ -150,17 +151,10 @@ describe('OpenShift Distributed Tracing UI Plugin tests', () => { DTP_NAMESPACE: `${DTP.namespace}` }, timeout: 120000, - failOnNonZeroExit: false + failOnNonZeroExit: true } ) .then((result) => { - // The command has completed - // 'result' is an object containing: - // - stdout: The standard output of the command - // - stderr: The standard error of the command - // - code: The exit code of the command (0 for success) - // - signal: The signal that terminated the command, if any - - expect(result.code).to.eq(0); // Assert that the command was successful + expect(result.code).to.eq(0); cy.log(`COO CSV updated successfully with Distributed Tracing Console Plugin image: ${result.stdout}`); }); } else { @@ -169,7 +163,17 @@ describe('OpenShift Distributed Tracing UI Plugin tests', () => { cy.log('Create Distributed Tracing UI Plugin instance.'); cy.exec(`oc apply -f ./fixtures/tracing-ui-plugin.yaml --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`); - cy.get('.pf-v5-c-alert, .pf-v6-c-alert', { timeout: 2 * 60 * 1000 }) + cy.exec( + `sleep 15 && oc wait --for=condition=Ready pods --selector=app.kubernetes.io/instance=distributed-tracing -n ${DTP.namespace} --timeout=60s --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, + { + timeout: 80000, + failOnNonZeroExit: true + } + ).then((result) => { + expect(result.code).to.eq(0); + cy.log(`Pods are now running in namespace: ${Cypress.env('DTP_NAMESPACE')}`); + }); + cy.get('.pf-v5-c-alert, .pf-v6-c-alert', { timeout: 120000 }) .contains('Web console update is available') .then(($alert) => { // If the alert is found, assert that it exists @@ -226,22 +230,38 @@ describe('OpenShift Distributed Tracing UI Plugin tests', () => { }); // Tests start from here. - it('(Test Distributed Tracing UI plugin page without any Tempo instances)', function () { + it('Test Distributed Tracing UI plugin page without any Tempo instances', () => { cy.log('Navigate to the observe/traces page'); cy.visit('/observe/traces'); - cy.log('Assert that the page contains the text No Tempo instances yet'); - cy.contains('No Tempo instances yet').should('be.visible'); + cy.log('Assert that the Traces page shows the empty state.'); + cy.get('.pf-v6-c-empty-state__title-text') + .should('be.visible') + .and('have.text', 'No Tempo instances yet'); - cy.log('Click on the Create a Tempo instance button'); - cy.get('button.pf-v5-c-menu-toggle.pf-m-primary').click(); - - cy.log('Check the dropdown for Create a Tempo instance'); - cy.get('.pf-v5-c-menu__item-text').contains('Create a TempoStack instance').should('have.class', 'pf-v5-c-menu__item-text'); - cy.get('.pf-v5-c-menu__item-text').contains('Create a TempoMonolithic instance').should('have.class', 'pf-v5-c-menu__item-text'); + cy.log('Assert that the View documentation button is visible.'); + cy.contains('.pf-v6-c-button', 'View documentation') + .should('be.visible') + .and('have.text', 'View documentation'); + + cy.log('Assert create a tempo instance toggle visibility and text.'); + const createTempoToggle = cy.contains('.pf-v6-c-menu-toggle', 'Create a Tempo instance'); + createTempoToggle.should('be.visible'); + + cy.log('Click the toggle to show creation options.'); + createTempoToggle.click(); + + cy.log('Assert dropdown items for Tempo instance creation are visible.'); + cy.contains('.pf-v6-c-menu__item-text', 'Create a TempoStack instance') + .should('be.visible') + .and('have.text', 'Create a TempoStack instance'); + + cy.contains('.pf-v6-c-menu__item-text', 'Create a TempoMonolithic instance') + .should('be.visible') + .and('have.text', 'Create a TempoMonolithic instance'); }); - it('(Test Distributed Tracing UI plugin)', function () { + it('(Test Distributed Tracing UI plugin with Tempo instances and verify traces)', function () { cy.log('Create TempoStack and TempoMonolithic instances'); cy.exec( 'chainsaw test --config ./fixtures/.chainsaw.yaml --skip-delete ./fixtures/chainsaw-tests/multitenancy ./fixtures/chainsaw-tests/monolithic-multitenancy-openshift', @@ -253,38 +273,40 @@ describe('OpenShift Distributed Tracing UI Plugin tests', () => { failOnNonZeroExit: true } ) .then((result) => { - // The command has completed - // 'result' is an object containing: - // - stdout: The standard output of the command - // - stderr: The standard error of the command - // - code: The exit code of the command (0 for success) - // - signal: The signal that terminated the command, if any - - expect(result.code).to.eq(0); // Assert that the command was successful + expect(result.code).to.eq(0); cy.log(`Chainsaw test ran successfully: ${result.stdout}`); }); - // Navigate to the /observe/traces page + cy.log('Navigate to the observe/traces page'); cy.visit('/observe/traces'); - cy.log('Check traces from TempoStack instance from Tracing UI'); - cy.get('.pf-v5-c-menu-toggle__button > .pf-v5-c-menu-toggle__controls > .pf-v5-c-menu-toggle__toggle-icon').click(); - cy.get('#tempoinstance-dropdown-item-chainsaw-multitenancy__simplest > .pf-v5-c-menu__item-main > .pf-v5-c-menu__item-text').click(); - cy.get('#tenant-dropdown-input > .pf-v5-c-text-input-group__text > .pf-v5-c-text-input-group__text-input').click(); - cy.get('#tenant-dropdown-item-prod > .pf-v5-c-menu__item-main > .pf-v5-c-menu__item-text').click(); - cy.get('[style="height: auto; flex-direction: column-reverse;"] > .pf-v5-c-button').click(); - cy.get('#tenant-dropdown-input > .pf-v5-c-text-input-group__text > .pf-v5-c-text-input-group__text-input').click(); - cy.get('#tenant-dropdown-item-dev > .pf-v5-c-menu__item-main > .pf-v5-c-menu__item-text').click(); - cy.get('.pf-v5-l-stack > .pf-v5-c-menu-toggle > .pf-v5-c-menu-toggle__text').click(); - cy.get(':nth-child(1) > .pf-v5-c-menu__item > .pf-v5-c-menu__item-main > .pf-v5-c-menu__item-text').click(); - cy.get('[style="height: auto; flex-direction: column-reverse;"] > .pf-v5-c-button').click(); + cy.log('Assert traces in TempoStack instance.'); + cy.get(':nth-child(1) > .pf-v6-c-toolbar__item > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__button > .pf-v6-c-menu-toggle__controls > .pf-v6-c-menu-toggle__toggle-icon').click(); + cy.get(':nth-child(2) > .pf-v6-c-menu__item > .pf-v6-c-menu__item-main > .pf-v6-c-menu__item-text').click(); + cy.get(':nth-child(1) > :nth-child(2) > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__button').click(); + cy.get(':nth-child(2) > .pf-v6-c-menu__item > .pf-v6-c-menu__item-main > .pf-v6-c-menu__item-text').click(); + cy.get(':nth-child(2) > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__controls > .pf-v6-c-menu-toggle__toggle-icon').click(); + cy.get(':nth-child(1) > .pf-v6-c-menu__item > .pf-v6-c-menu__item-main > .pf-v6-c-menu__item-text').click(); + cy.get(':nth-child(1) > :nth-child(2) > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__button > .pf-v6-c-menu-toggle__controls > .pf-v6-c-menu-toggle__toggle-icon').click(); + cy.get(':nth-child(1) > .pf-v6-c-menu__item > .pf-v6-c-menu__item-main > .pf-v6-c-menu__item-text').click(); + cy.get('.pf-v6-c-toolbar__group > :nth-child(1) > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__controls > .pf-v6-c-menu-toggle__toggle-icon').click(); + cy.get(':nth-child(1) > .pf-v6-c-menu__item > .pf-v6-c-menu__item-main > .pf-v6-c-menu__item-text').click(); + cy.get('.pf-m-toggle-group > .pf-v6-c-toolbar__group > :nth-child(2) > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__button').click(); + cy.contains('.pf-v6-c-menu__item-text', 'http') + .closest('.pf-v6-c-menu__item') + .find('input[type="checkbox"]') + .check(); + cy.contains('.pf-v6-c-menu__item-text', 'grpc') + .closest('.pf-v6-c-menu__item') + .find('input[type="checkbox"]') + .check(); cy.get('.MuiDataGrid-row--firstVisible > [data-field="name"] > .MuiBox-root > .MuiTypography-root').click(); - cy.get('[data-index="1"] > .css-1cqmfcw > .MuiStack-root > .MuiBox-root').click({ force: true }); - cy.get('.css-tgncor').then(($el) => { - cy.log(`Actual text in .css-tgncor (TempoStack): ${$el.text()}`); + cy.contains('div', 'okey-dokey').click({ force: true }); + cy.get('.css-1bmckj4').then(($el) => { + cy.log(`Actual text in .css-1bmckj4 (TempoMonolithic): ${$el.text()}`); expect($el.text()).to.satisfy((text) => text === 'http' || text === 'grpc'); }); cy.get('.MuiTypography-h2').should('have.text', 'okey-dokey'); - cy.get('.MuiTabs-flexContainer > .MuiButtonBase-root').should('be.visible'); + cy.get('.MuiTabs-list > .MuiButtonBase-root').should('be.visible'); cy.get(':nth-child(5) > :nth-child(1) > .MuiListItemText-root > .MuiTypography-h5').should('have.text', 'net.peer.ip'); cy.get(':nth-child(5) > :nth-child(1) > .MuiListItemText-root > .MuiTypography-body1').should('have.text', '1.2.3.4'); cy.get(':nth-child(2) > .MuiListItemText-root > .MuiTypography-h5').should('have.text', 'peer.service'); @@ -294,25 +316,23 @@ describe('OpenShift Distributed Tracing UI Plugin tests', () => { cy.log(`Actual text in service.name (TempoStack): ${$el.text()}`); expect($el.text()).to.satisfy((text) => text === 'http' || text === 'grpc'); }); + cy.get('.pf-v6-c-breadcrumb__list > :nth-child(1) > a').click(); - cy.log('Check traces from TempoMonolithc instance from Tracing UI'); - cy.get('.pf-v5-c-breadcrumb__list > :nth-child(1) > a').click(); - cy.get('#tempoinstance-dropdown-input > .pf-v5-c-text-input-group__text > .pf-v5-c-text-input-group__text-input').click(); - cy.get('#tempoinstance-dropdown-item-chainsaw-monolithic-multitenancy__monolithic-multitenancy-openshift > .pf-v5-c-menu__item-main > .pf-v5-c-menu__item-text').click(); - cy.get('#tenant-dropdown-input > .pf-v5-c-text-input-group__text > .pf-v5-c-text-input-group__text-input').click(); - cy.get('#tenant-dropdown-item-prod').click(); - cy.get('[style="height: auto; flex-direction: column-reverse;"] > .pf-v5-c-button').click(); - cy.get('#tenant-dropdown-input > .pf-v5-c-text-input-group__text > .pf-v5-c-text-input-group__text-input').click(); - cy.get('#tenant-dropdown-item-dev').click(); - cy.get('[style="height: auto; flex-direction: column-reverse;"] > .pf-v5-c-button').click(); + cy.log('Assert traces in TempoMonolithic instance.'); + cy.get(':nth-child(1) > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__button').click(); + cy.get(':nth-child(1) > .pf-v6-c-menu__item > .pf-v6-c-menu__item-main > .pf-v6-c-menu__item-text').click(); + cy.get(':nth-child(2) > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle').click(); + cy.get(':nth-child(3) > .pf-v6-c-menu__item > .pf-v6-c-menu__item-main > .pf-v6-c-menu__item-text').click(); + cy.get('.pf-v6-c-form__group-control > .pf-v6-c-button > .pf-v6-c-button__text').click(); + cy.get('.pf-m-toggle-group > .pf-m-action-group > .pf-v6-c-toolbar__item > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-button > .pf-v6-c-button__text').click(); cy.get('.MuiDataGrid-row--firstVisible > [data-field="name"] > .MuiBox-root > .MuiTypography-root').click(); - cy.get('[data-index="1"] > .css-1cqmfcw > .MuiStack-root > .MuiBox-root').click({ force: true }); - cy.get('.css-tgncor').then(($el) => { - cy.log(`Actual text in .css-tgncor (TempoMonolithic): ${$el.text()}`); + cy.contains('div', 'okey-dokey').click({ force: true }); + cy.get('.css-1bmckj4').then(($el) => { + cy.log(`Actual text in .css-1bmckj4 (TempoMonolithic): ${$el.text()}`); expect($el.text()).to.satisfy((text) => text === 'http' || text === 'grpc'); }); cy.get('.MuiTypography-h2').should('have.text', 'okey-dokey'); - cy.get('.MuiTabs-flexContainer > .MuiButtonBase-root').should('be.visible'); + cy.get('.MuiTabs-list > .MuiButtonBase-root').should('be.visible'); cy.get(':nth-child(5) > :nth-child(1) > .MuiListItemText-root > .MuiTypography-h5').should('have.text', 'net.peer.ip'); cy.get(':nth-child(5) > :nth-child(1) > .MuiListItemText-root > .MuiTypography-body1').should('have.text', '1.2.3.4'); cy.get(':nth-child(2) > .MuiListItemText-root > .MuiTypography-h5').should('have.text', 'peer.service'); @@ -322,7 +342,7 @@ describe('OpenShift Distributed Tracing UI Plugin tests', () => { cy.log(`Actual text in service.name (TempoMonolithic): ${$el.text()}`); expect($el.text()).to.satisfy((text) => text === 'http' || text === 'grpc'); }); - cy.get('.pf-v5-c-breadcrumb__list > :nth-child(1) > a').click(); + cy.get('.pf-v6-c-breadcrumb__list > :nth-child(1) > a').click(); }); }); From 6d50c05eda42f0454680676fd1c390902859d533 Mon Sep 17 00:00:00 2001 From: Ishwar Kanse Date: Fri, 16 May 2025 20:01:00 +0530 Subject: [PATCH 07/35] fix var --- tests/tests/dt-plugin-tests.cy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests/dt-plugin-tests.cy.ts b/tests/tests/dt-plugin-tests.cy.ts index 9eda732..f800196 100644 --- a/tests/tests/dt-plugin-tests.cy.ts +++ b/tests/tests/dt-plugin-tests.cy.ts @@ -171,7 +171,7 @@ describe('OpenShift Distributed Tracing UI Plugin tests', () => { } ).then((result) => { expect(result.code).to.eq(0); - cy.log(`Pods are now running in namespace: ${Cypress.env('DTP_NAMESPACE')}`); + cy.log(`Distributed Tracing Console plugin pod is now running in namespace: ${DTP.namespace}`); }); cy.get('.pf-v5-c-alert, .pf-v6-c-alert', { timeout: 120000 }) .contains('Web console update is available') From 2a81578a81d8d112a32665bcad9b5f21b68ea9d6 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Mon, 19 May 2025 16:40:52 +0200 Subject: [PATCH 08/35] TRACING-5408: Refresh query if Tempo instance or tenant changes Signed-off-by: Andreas Gerstmayr --- web/src/pages/TracesPage/QueryBrowser.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/web/src/pages/TracesPage/QueryBrowser.tsx b/web/src/pages/TracesPage/QueryBrowser.tsx index b00f7cf..ff87d8e 100644 --- a/web/src/pages/TracesPage/QueryBrowser.tsx +++ b/web/src/pages/TracesPage/QueryBrowser.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback, useEffect, useMemo } from 'react'; import { Divider, PageSection, Split, SplitItem, Stack, Title } from '@patternfly/react-core'; import { useTranslation } from 'react-i18next'; import { @@ -61,6 +61,13 @@ export function QueryBrowserBody() { const [limit, setLimit] = useQueryParam('limit', withDefault(NumberParam, DEFAULT_LIMIT)); const { timeRange, setTimeRange, refresh } = useTimeRange(); + // Refresh query if Tempo instance or tenant changes. + // The Perses data source is selected via a mock DatasourceApiImpl implementation in , + // therefore Perses doesn't refresh automatically if the Tempo instance changes. + useEffect(() => { + refresh(); + }, [tempo, refresh]); + const runQuery = useCallback( (query: string) => { setQuery(query); From 1a4cb97a9e36186925baa1b91225a5775d3e16e4 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 12 Jun 2025 14:09:28 +0200 Subject: [PATCH 09/35] Update Perses dependencies to 0.51.0 Signed-off-by: Andreas Gerstmayr --- ...n__distributed-tracing-console-plugin.json | 1 - web/package-lock.json | 2893 ++++++++++++----- web/package.json | 26 +- web/src/hooks/useTagValues.ts | 4 +- .../TracesPage/Toolbar/TraceQLEditor.tsx | 7 +- 5 files changed, 2141 insertions(+), 790 deletions(-) diff --git a/web/locales/en/plugin__distributed-tracing-console-plugin.json b/web/locales/en/plugin__distributed-tracing-console-plugin.json index e9584cc..b0d0ab5 100644 --- a/web/locales/en/plugin__distributed-tracing-console-plugin.json +++ b/web/locales/en/plugin__distributed-tracing-console-plugin.json @@ -25,7 +25,6 @@ "Hide graph": "Hide graph", "Show graph": "Show graph", "Filter": "Filter", - "Namespace": "Namespace", "Filter by namespace": "Filter by namespace", "This filter is based on the <1>k8s.namespace.name resource attribute. To set this attribute, it is recommended to enable the <4>Kubernetes Attributes Processor in your OpenTelemetry Collector pipeline.": "This filter is based on the <1>k8s.namespace.name resource attribute. To set this attribute, it is recommended to enable the <4>Kubernetes Attributes Processor in your OpenTelemetry Collector pipeline.", "between {{min}} and {{max}}": "between {{min}} and {{max}}", diff --git a/web/package-lock.json b/web/package-lock.json index 87e5153..149da0a 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -17,14 +17,13 @@ "@patternfly/react-core": "^6.2.0", "@patternfly/react-icons": "^5.4.2", "@patternfly/react-templates": "^6.2.2", - "@perses-dev/core": "0.51.0-rc.0", - "@perses-dev/dashboards": "0.51.0-rc.0", - "@perses-dev/plugin-system": "0.51.0-rc.0", - "@perses-dev/scatter-chart-plugin": "^0.6.0", - "@perses-dev/tempo-plugin": "^0.51.0-beta.2", - "@perses-dev/trace-table-plugin": "^0.6.0", - "@perses-dev/tracing-gantt-chart-plugin": "^0.6.0", - "@tanstack/react-query": "4.36.1", + "@perses-dev/core": "^0.51.0", + "@perses-dev/dashboards": "^0.51.0", + "@perses-dev/plugin-system": "^0.51.0", + "@perses-dev/scatter-chart-plugin": "^0.7.0", + "@perses-dev/tempo-plugin": "^0.51.0-rc.3", + "@perses-dev/trace-table-plugin": "^0.7.0", + "@perses-dev/tracing-gantt-chart-plugin": "^0.7.0", "copy-webpack-plugin": "^12.0.2", "i18next": "^23.10.0", "i18next-http-backend": "^2.5.0", @@ -73,6 +72,9 @@ "webpack": "^5.68.0", "webpack-cli": "^4.9.2", "webpack-dev-server": "^4.7.4" + }, + "peerDependencies": { + "react-router-dom": "<7" } }, "node_modules/@ampproject/remapping": { @@ -91,9 +93,9 @@ } }, "node_modules/@atlaskit/pragmatic-drag-and-drop": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@atlaskit/pragmatic-drag-and-drop/-/pragmatic-drag-and-drop-1.6.1.tgz", - "integrity": "sha512-RC0E/uicBBIoCxHOLcrFf0DP9N4h6AfluSdz8yC4ttYu1Q3UPbnpBEBQGdOxdl3vcNsB06zw378Nj6eNnxbzFg==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@atlaskit/pragmatic-drag-and-drop/-/pragmatic-drag-and-drop-1.7.2.tgz", + "integrity": "sha512-GFlFVusm+PpzNwpk4ju8+w9a9pWD5NIGi4DoJ9g6CXTUMlQ4BCsivvmUk+azsV31luEKZtf2J0tg1y3vdltrTQ==", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.0.0", @@ -102,12 +104,12 @@ } }, "node_modules/@atlaskit/pragmatic-drag-and-drop-hitbox": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@atlaskit/pragmatic-drag-and-drop-hitbox/-/pragmatic-drag-and-drop-hitbox-1.0.3.tgz", - "integrity": "sha512-/Sbu/HqN2VGLYBhnsG7SbRNg98XKkbF6L7XDdBi+izRybfaK1FeMfodPpm/xnBHPJzwYMdkE0qtLyv6afhgMUA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@atlaskit/pragmatic-drag-and-drop-hitbox/-/pragmatic-drag-and-drop-hitbox-1.1.0.tgz", + "integrity": "sha512-JWt6eVp6Br2FPHRM8s0dUIHQk/jFInGP1f3ti5CdtM1Ji5/pt8Akm44wDC063Gv2i5RGseixtbW0z/t6RYtbdg==", "license": "Apache-2.0", "dependencies": { - "@atlaskit/pragmatic-drag-and-drop": "^1.1.0", + "@atlaskit/pragmatic-drag-and-drop": "^1.6.0", "@babel/runtime": "^7.0.0" } }, @@ -126,9 +128,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz", - "integrity": "sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==", + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.5.tgz", + "integrity": "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==", "dev": true, "license": "MIT", "peer": true, @@ -137,23 +139,23 @@ } }, "node_modules/@babel/core": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz", - "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==", + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz", + "integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==", "dev": true, "license": "MIT", "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.1", - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helpers": "^7.27.1", - "@babel/parser": "^7.27.1", - "@babel/template": "^7.27.1", - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.4", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.27.4", + "@babel/types": "^7.27.3", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -169,13 +171,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", - "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", + "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.27.1", - "@babel/types": "^7.27.1", + "@babel/parser": "^7.27.5", + "@babel/types": "^7.27.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -185,14 +187,14 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.1.tgz", - "integrity": "sha512-WnuuDILl9oOBbKnb4L+DyODx7iC47XfzmNCpTttFsSp6hTG7XZxu60+4IO+2/hPfcGOoKbFiwoI/+zwARbNQow==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@babel/types": "^7.27.1" + "@babel/types": "^7.27.3" }, "engines": { "node": ">=6.9.0" @@ -305,16 +307,16 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz", - "integrity": "sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", "dev": true, "license": "MIT", "peer": true, "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/traverse": "^7.27.3" }, "engines": { "node": ">=6.9.0" @@ -447,27 +449,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", - "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@babel/template": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", - "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", + "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", "license": "MIT", "dependencies": { - "@babel/types": "^7.27.1" + "@babel/types": "^7.27.3" }, "bin": { "parser": "bin/babel-parser.js" @@ -926,9 +928,9 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.1.tgz", - "integrity": "sha512-QEcFlMl9nGTgh1rn2nIeU5bkfb9BAjaQcWbiP4LvKxUot52ABcTkpcyJ7f2Q2U2RuQ84BNLgts3jRme2dTx6Fw==", + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.5.tgz", + "integrity": "sha512-JF6uE2s67f0y2RZcm2kpAUEbD50vH62TyWVebxwHAlbSdM49VqPz8t4a1uIjp4NIOIZ4xzLfjY5emt/RCyC7TQ==", "dev": true, "license": "MIT", "peer": true, @@ -1019,9 +1021,9 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.27.1.tgz", - "integrity": "sha512-ttDCqhfvpE9emVkXbPD8vyxxh4TWYACVybGkDj+oReOGwnp066ITEivDlLwe0b1R0+evJ13IXQuLNB5w1fhC5Q==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.27.3.tgz", + "integrity": "sha512-s4Jrok82JpiaIprtY2nHsYmrThKvvwgHwjgd7UMiYhZaN0asdXNLr0y+NjTfkA7SyQE5i2Fb7eawUOZmLvyqOA==", "dev": true, "license": "MIT", "peer": true, @@ -1388,16 +1390,16 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.27.2.tgz", - "integrity": "sha512-AIUHD7xJ1mCrj3uPozvtngY3s0xpv7Nu7DoUSnzNY6Xam1Cy4rUznR//pvMHOhQ4AvbCexhbqXCtpxGHOGOO6g==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.27.3.tgz", + "integrity": "sha512-7ZZtznF9g4l2JCImCo5LNKFHB5eXnN39lLtLY5Tg+VkR0jwOt7TBciMckuiQIOIW7L5tkQOCh3bVGYeXgMx52Q==", "dev": true, "license": "MIT", "peer": true, "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.27.3", "@babel/plugin-transform-parameters": "^7.27.1" }, "engines": { @@ -1532,9 +1534,9 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.1.tgz", - "integrity": "sha512-B19lbbL7PMrKr52BNPjCqg1IyNUIjTcxKj8uX9zHO+PmWN93s19NDr/f69mIkEp2x9nmDJ08a7lgHaTTzvW7mw==", + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.5.tgz", + "integrity": "sha512-uhB8yHerfe3MWnuLAhEbeQ4afVoqv8BQsPqrTv7e/jZ9y00kJL6l9a/f4OWaKxotmjzewfEyXE1vgDJenkQ2/Q==", "dev": true, "license": "MIT", "peer": true, @@ -1842,9 +1844,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz", - "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -1865,16 +1867,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", - "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", + "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.1", - "@babel/parser": "^7.27.1", - "@babel/template": "^7.27.1", - "@babel/types": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1883,9 +1885,9 @@ } }, "node_modules/@babel/types": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", - "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", + "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", @@ -1938,9 +1940,9 @@ } }, "node_modules/@codemirror/language": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.0.tgz", - "integrity": "sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ==", + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.1.tgz", + "integrity": "sha512-5kS1U7emOGV84vxC+ruBty5sUgcD0te6dyupyRVG2zaSjhTDM73LhVKUtVwiqSe6QwmEoA4SCiU8AKPFyumAWQ==", "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", @@ -1963,9 +1965,9 @@ } }, "node_modules/@codemirror/search": { - "version": "6.5.10", - "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.10.tgz", - "integrity": "sha512-RMdPdmsrUf53pb2VwflKGHEe1XVM07hI7vV2ntgw1dmqhimpatSJKva4VA9h4TLUDOD4EIF02201oZurpnEFsg==", + "version": "6.5.11", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.11.tgz", + "integrity": "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==", "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", @@ -1995,12 +1997,13 @@ } }, "node_modules/@codemirror/view": { - "version": "6.36.8", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.8.tgz", - "integrity": "sha512-yoRo4f+FdnD01fFt4XpfpMCcCAo9QvZOtbrXExn4SqzH32YC6LgzqxfLZw/r6Ge65xyY03mK/UfUqrVw1gFiFg==", + "version": "6.37.1", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.37.1.tgz", + "integrity": "sha512-Qy4CAUwngy/VQkEz0XzMKVRcckQuqLYWKqVpDDDghBe5FSXSqfVrJn49nw3ePZHxRUz4nRmb05Lgi+9csWo4eg==", "license": "MIT", "dependencies": { "@codemirror/state": "^6.5.0", + "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } @@ -2192,6 +2195,26 @@ "ms": "^2.1.1" } }, + "node_modules/@dagrejs/dagre": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@dagrejs/dagre/-/dagre-1.1.2.tgz", + "integrity": "sha512-F09dphqvHsbe/6C2t2unbmpr5q41BNPEfJCdn8Z7aEBpVSy/zFQ/b4SWsweQjWNsYMDvE2ffNUN8X0CeFsEGNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@dagrejs/graphlib": "2.2.2" + } + }, + "node_modules/@dagrejs/graphlib": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@dagrejs/graphlib/-/graphlib-2.2.2.tgz", + "integrity": "sha512-CbyGpCDKsiTg/wuk79S7Muoj8mghDGAESWGxcSyhHX5jD35vYMBZochYVFzlHxynpE9unpu6O+4ZuhrLxASsOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">17.0.0" + } + }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", @@ -3900,196 +3923,757 @@ "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", "license": "MIT" }, - "node_modules/@module-federation/bridge-react-webpack-plugin": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@module-federation/bridge-react-webpack-plugin/-/bridge-react-webpack-plugin-0.8.12.tgz", - "integrity": "sha512-fiSf9Df4RqdKiL1WtS7eCqAWqDnLNwMZ9qU7ZMjXSAhI3wOLzycFflpVx7PWOxSoTJPtc61hwD5lkEpNoHezkg==", + "node_modules/@modern-js/node-bundle-require": { + "version": "2.67.6", + "resolved": "https://registry.npmjs.org/@modern-js/node-bundle-require/-/node-bundle-require-2.67.6.tgz", + "integrity": "sha512-rRiDQkrm3kgn0E/GNrcvqo4c71PaUs2R8Xmpv6GUKbEr6lz7VNgfZmAhdAQPtNfRfiBe+1sFLzEcwfEdDo/dTA==", "license": "MIT", "dependencies": { - "@module-federation/sdk": "0.8.12", - "@types/semver": "7.5.8", - "semver": "7.6.3" + "@modern-js/utils": "2.67.6", + "@swc/helpers": "^0.5.17", + "esbuild": "0.17.19" } }, - "node_modules/@module-federation/bridge-react-webpack-plugin/node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "license": "MIT" - }, - "node_modules/@module-federation/bridge-react-webpack-plugin/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, + "node_modules/@modern-js/node-bundle-require/node_modules/@esbuild/android-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", + "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/@module-federation/data-prefetch": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@module-federation/data-prefetch/-/data-prefetch-0.8.12.tgz", - "integrity": "sha512-YjCk6KPBQ4Ml2/2aIKQt11I4jBKOTvK3xHyAQMKPpX5aBbMEwonDhkpTitygUw3SMBdi3CP1KMRDJ3suj3O6Mg==", + "node_modules/@modern-js/node-bundle-require/node_modules/@esbuild/android-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", + "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "@module-federation/runtime": "0.8.12", - "@module-federation/sdk": "0.8.12", - "fs-extra": "9.1.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@module-federation/error-codes": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.8.12.tgz", - "integrity": "sha512-K+F4iiV62KY+IpjK6ggn3vI5Yt/T/LUb6xuazY78bhAGwLaHe1DYr7BfSutKMpiB+Dcs6U4dYOBogSMnnl0j4Q==", - "license": "MIT" - }, - "node_modules/@module-federation/inject-external-runtime-core-plugin": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@module-federation/inject-external-runtime-core-plugin/-/inject-external-runtime-core-plugin-0.8.12.tgz", - "integrity": "sha512-/vFW+eiBiqXOKkDYVKl5JXGI0H4Whj10P8JadowxuHcEuR+R7kkXXauYuGYKaxIFqG4zbN3r9su2qxIEEqOsOw==", + "node_modules/@modern-js/node-bundle-require/node_modules/@esbuild/android-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", + "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", + "cpu": [ + "x64" + ], "license": "MIT", - "peerDependencies": { - "@module-federation/runtime-tools": "0.8.12" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@module-federation/managers": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@module-federation/managers/-/managers-0.8.12.tgz", - "integrity": "sha512-+BBdBMptHiiT2ZsfPovQuHYkse6R1+dmDS6bAinw3UGpb418W8ERp5I4jHeyhEtr3t3mb/dh4sAsYM14/EWJ9Q==", + "node_modules/@modern-js/node-bundle-require/node_modules/@esbuild/darwin-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", + "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "@module-federation/sdk": "0.8.12", - "find-pkg": "2.0.0", - "fs-extra": "9.1.0" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@module-federation/manifest": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@module-federation/manifest/-/manifest-0.8.12.tgz", - "integrity": "sha512-UwC6/QK37x7Xr5K+89iKxOy/uQHqFMd49axOhgDmFSrWQOULFQd51EpTOxFXgwZfiq/h0uVBYq/c0GB3Tdu8GA==", + "node_modules/@modern-js/node-bundle-require/node_modules/@esbuild/darwin-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", + "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "@module-federation/dts-plugin": "0.8.12", - "@module-federation/managers": "0.8.12", - "@module-federation/sdk": "0.8.12", - "chalk": "3.0.0", - "find-pkg": "2.0.0" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@module-federation/manifest/node_modules/@module-federation/dts-plugin": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-0.8.12.tgz", - "integrity": "sha512-BhRZsG68XtGzKM0N02/3ldiW+PTc+QaHCMNyiZnk3GcxS0qe6USu7fTQwRIPCC2GONIdoHD+GVYPN/NJWjbTFg==", + "node_modules/@modern-js/node-bundle-require/node_modules/@esbuild/freebsd-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", + "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "@module-federation/error-codes": "0.8.12", - "@module-federation/managers": "0.8.12", - "@module-federation/sdk": "0.8.12", - "@module-federation/third-party-dts-extractor": "0.8.12", - "adm-zip": "^0.5.10", - "ansi-colors": "^4.1.3", - "axios": "^1.7.4", - "chalk": "3.0.0", - "fs-extra": "9.1.0", - "isomorphic-ws": "5.0.0", - "koa": "2.15.3", - "lodash.clonedeepwith": "4.5.0", - "log4js": "6.9.1", - "node-schedule": "2.1.1", - "rambda": "^9.1.0", - "ws": "8.18.0" - }, - "peerDependencies": { - "typescript": "^4.9.0 || ^5.0.0", - "vue-tsc": ">=1.0.24" - }, - "peerDependenciesMeta": { - "vue-tsc": { - "optional": true - } + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@module-federation/manifest/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@modern-js/node-bundle-require/node_modules/@esbuild/freebsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", + "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=12" } }, - "node_modules/@module-federation/manifest/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "node_modules/@modern-js/node-bundle-require/node_modules/@esbuild/linux-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", + "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", + "cpu": [ + "arm" + ], "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/@module-federation/manifest/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@modern-js/node-bundle-require/node_modules/@esbuild/linux-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", + "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=7.0.0" + "node": ">=12" } }, - "node_modules/@module-federation/manifest/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/@module-federation/manifest/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@modern-js/node-bundle-require/node_modules/@esbuild/linux-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", + "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", + "cpu": [ + "ia32" + ], "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/@module-federation/manifest/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@modern-js/node-bundle-require/node_modules/@esbuild/linux-loong64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", + "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", + "cpu": [ + "loong64" + ], "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/@module-federation/manifest/node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "node_modules/@modern-js/node-bundle-require/node_modules/@esbuild/linux-mips64el": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", + "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", + "cpu": [ + "mips64el" + ], "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10.0.0" + "node": ">=12" + } + }, + "node_modules/@modern-js/node-bundle-require/node_modules/@esbuild/linux-ppc64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", + "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@modern-js/node-bundle-require/node_modules/@esbuild/linux-riscv64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", + "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@modern-js/node-bundle-require/node_modules/@esbuild/linux-s390x": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", + "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@modern-js/node-bundle-require/node_modules/@esbuild/linux-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", + "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@modern-js/node-bundle-require/node_modules/@esbuild/netbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", + "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@modern-js/node-bundle-require/node_modules/@esbuild/openbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", + "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@modern-js/node-bundle-require/node_modules/@esbuild/sunos-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", + "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@modern-js/node-bundle-require/node_modules/@esbuild/win32-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", + "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@modern-js/node-bundle-require/node_modules/@esbuild/win32-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", + "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@modern-js/node-bundle-require/node_modules/@esbuild/win32-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", + "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@modern-js/node-bundle-require/node_modules/esbuild": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", + "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.17.19", + "@esbuild/android-arm64": "0.17.19", + "@esbuild/android-x64": "0.17.19", + "@esbuild/darwin-arm64": "0.17.19", + "@esbuild/darwin-x64": "0.17.19", + "@esbuild/freebsd-arm64": "0.17.19", + "@esbuild/freebsd-x64": "0.17.19", + "@esbuild/linux-arm": "0.17.19", + "@esbuild/linux-arm64": "0.17.19", + "@esbuild/linux-ia32": "0.17.19", + "@esbuild/linux-loong64": "0.17.19", + "@esbuild/linux-mips64el": "0.17.19", + "@esbuild/linux-ppc64": "0.17.19", + "@esbuild/linux-riscv64": "0.17.19", + "@esbuild/linux-s390x": "0.17.19", + "@esbuild/linux-x64": "0.17.19", + "@esbuild/netbsd-x64": "0.17.19", + "@esbuild/openbsd-x64": "0.17.19", + "@esbuild/sunos-x64": "0.17.19", + "@esbuild/win32-arm64": "0.17.19", + "@esbuild/win32-ia32": "0.17.19", + "@esbuild/win32-x64": "0.17.19" + } + }, + "node_modules/@modern-js/utils": { + "version": "2.67.6", + "resolved": "https://registry.npmjs.org/@modern-js/utils/-/utils-2.67.6.tgz", + "integrity": "sha512-cxY7HsSH0jIN3rlL6RZ0tgzC1tH0gHW++8X6h7sXCNCylhUdbGZI9yTGbpAS8bU7c97NmPaTKg+/ILt00Kju1Q==", + "license": "MIT", + "dependencies": { + "@swc/helpers": "^0.5.17", + "caniuse-lite": "^1.0.30001520", + "lodash": "^4.17.21", + "rslog": "^1.1.0" + } + }, + "node_modules/@module-federation/bridge-react-webpack-plugin": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@module-federation/bridge-react-webpack-plugin/-/bridge-react-webpack-plugin-0.14.3.tgz", + "integrity": "sha512-lRkAeNpRdsOFIYx+SSEzsWUZbr2RdfcLA0UbadBaWV3FgeoSd0mef9IO9+KlY1y05anvwOS17VlsX0DeCbvMXg==", + "license": "MIT", + "dependencies": { + "@module-federation/sdk": "0.14.3", + "@types/semver": "7.5.8", + "semver": "7.6.3" + } + }, + "node_modules/@module-federation/bridge-react-webpack-plugin/node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "license": "MIT" + }, + "node_modules/@module-federation/bridge-react-webpack-plugin/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@module-federation/cli": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@module-federation/cli/-/cli-0.14.3.tgz", + "integrity": "sha512-BRR1d+piUSKW5OAuU+ej/zS3pMS4ismea9XHD/DWGJXW/Am7h1pFxRNYAZ8iflLJQ46oqjS/j1ECc5WJmbHlxw==", + "license": "MIT", + "dependencies": { + "@modern-js/node-bundle-require": "2.67.6", + "@module-federation/dts-plugin": "0.14.3", + "@module-federation/sdk": "0.14.3", + "chalk": "3.0.0", + "commander": "11.1.0" + }, + "bin": { + "mf": "bin/mf.js" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@module-federation/cli/node_modules/@module-federation/dts-plugin": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-0.14.3.tgz", + "integrity": "sha512-QiE4wcra6dNo36028cX//QfX0uKF6UeoQoaVIIu06imF4KjCNQD3bE91D6H3DlVVD/UjnIDeUSt9AoGesLzbSA==", + "license": "MIT", + "dependencies": { + "@module-federation/error-codes": "0.14.3", + "@module-federation/managers": "0.14.3", + "@module-federation/sdk": "0.14.3", + "@module-federation/third-party-dts-extractor": "0.14.3", + "adm-zip": "^0.5.10", + "ansi-colors": "^4.1.3", + "axios": "^1.8.2", + "chalk": "3.0.0", + "fs-extra": "9.1.0", + "isomorphic-ws": "5.0.0", + "koa": "2.16.1", + "lodash.clonedeepwith": "4.5.0", + "log4js": "6.9.1", + "node-schedule": "2.1.1", + "rambda": "^9.1.0", + "ws": "8.18.0" + }, + "peerDependencies": { + "typescript": "^4.9.0 || ^5.0.0", + "vue-tsc": ">=1.0.24" + }, + "peerDependenciesMeta": { + "vue-tsc": { + "optional": true + } + } + }, + "node_modules/@module-federation/cli/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@module-federation/cli/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@module-federation/cli/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@module-federation/cli/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/@module-federation/cli/node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/@module-federation/cli/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@module-federation/cli/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@module-federation/cli/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@module-federation/data-prefetch": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@module-federation/data-prefetch/-/data-prefetch-0.14.3.tgz", + "integrity": "sha512-jGSeo4e32PxTIqPxxwb11oqBXLzygx7fsbV0RXHhy0W1IXDzFObYbHCN95ohxAEh25Hn5jinxBCFn/ltEzQUlA==", + "license": "MIT", + "dependencies": { + "@module-federation/runtime": "0.14.3", + "@module-federation/sdk": "0.14.3", + "fs-extra": "9.1.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@module-federation/error-codes": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.14.3.tgz", + "integrity": "sha512-sBJ3XKU9g5Up31jFeXPFsD8AgORV7TLO/cCSMuRewSfgYbG/3vSKLJmfHrO6+PvjZSb9VyV2UaF02ojktW65vw==", + "license": "MIT" + }, + "node_modules/@module-federation/inject-external-runtime-core-plugin": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@module-federation/inject-external-runtime-core-plugin/-/inject-external-runtime-core-plugin-0.14.3.tgz", + "integrity": "sha512-OurBx/gDkRPKl9pidefG4EtJeSk8izaj3ZVN/sGGMOXLFeWLK2i0ZSUM/5ogPLj9NPdQC8tTlPalEUsRQ38DoA==", + "license": "MIT", + "peerDependencies": { + "@module-federation/runtime-tools": "0.14.3" + } + }, + "node_modules/@module-federation/managers": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@module-federation/managers/-/managers-0.14.3.tgz", + "integrity": "sha512-uQiLRUvy2yiWm7Xa75y8/He3swW0l2hn8Ef09mvSXhjewwFQMPClQAmZa1UCgNk1F7s/dXDtL9E8vlnX/aZdOQ==", + "license": "MIT", + "dependencies": { + "@module-federation/sdk": "0.14.3", + "find-pkg": "2.0.0", + "fs-extra": "9.1.0" + } + }, + "node_modules/@module-federation/manifest": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@module-federation/manifest/-/manifest-0.14.3.tgz", + "integrity": "sha512-GsD4PK7JTDOX8g2NyGhsoejhfyP88h6wCaxW4zAq6X91CE9Yu1R/Ec6QHhp9jfXdQlgkoXz1nQRlkbiU7RNTDA==", + "license": "MIT", + "dependencies": { + "@module-federation/dts-plugin": "0.14.3", + "@module-federation/managers": "0.14.3", + "@module-federation/sdk": "0.14.3", + "chalk": "3.0.0", + "find-pkg": "2.0.0" + } + }, + "node_modules/@module-federation/manifest/node_modules/@module-federation/dts-plugin": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-0.14.3.tgz", + "integrity": "sha512-QiE4wcra6dNo36028cX//QfX0uKF6UeoQoaVIIu06imF4KjCNQD3bE91D6H3DlVVD/UjnIDeUSt9AoGesLzbSA==", + "license": "MIT", + "dependencies": { + "@module-federation/error-codes": "0.14.3", + "@module-federation/managers": "0.14.3", + "@module-federation/sdk": "0.14.3", + "@module-federation/third-party-dts-extractor": "0.14.3", + "adm-zip": "^0.5.10", + "ansi-colors": "^4.1.3", + "axios": "^1.8.2", + "chalk": "3.0.0", + "fs-extra": "9.1.0", + "isomorphic-ws": "5.0.0", + "koa": "2.16.1", + "lodash.clonedeepwith": "4.5.0", + "log4js": "6.9.1", + "node-schedule": "2.1.1", + "rambda": "^9.1.0", + "ws": "8.18.0" + }, + "peerDependencies": { + "typescript": "^4.9.0 || ^5.0.0", + "vue-tsc": ">=1.0.24" + }, + "peerDependenciesMeta": { + "vue-tsc": { + "optional": true + } + } + }, + "node_modules/@module-federation/manifest/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@module-federation/manifest/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@module-federation/manifest/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@module-federation/manifest/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/@module-federation/manifest/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@module-federation/manifest/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@module-federation/manifest/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", @@ -4105,49 +4689,46 @@ } }, "node_modules/@module-federation/runtime": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.8.12.tgz", - "integrity": "sha512-eYohRfambj/qzxz6tEakDn459ROcixWO4zL5gmTEOmwG+jCDnxGR14j1guopyrrpjb6EKFNrPVWtYZTPPfGdQQ==", + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.14.3.tgz", + "integrity": "sha512-7ZHpa3teUDVhraYdxQGkfGHzPbjna4LtwbpudgzAxSLLFxLDNanaxCuSeIgSM9c+8sVUNC9kvzUgJEZB0krPJw==", "license": "MIT", "dependencies": { - "@module-federation/error-codes": "0.8.12", - "@module-federation/runtime-core": "0.6.20", - "@module-federation/sdk": "0.8.12" + "@module-federation/error-codes": "0.14.3", + "@module-federation/runtime-core": "0.14.3", + "@module-federation/sdk": "0.14.3" } }, "node_modules/@module-federation/runtime-core": { - "version": "0.6.20", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-0.6.20.tgz", - "integrity": "sha512-rX7sd/i7tpkAbfMD4TtFt/57SWNC/iv7UYS8g+ad7mnCJggWE1YEKsKSFgcvp4zU3thwR+j2y+kOCwd1sQvxEA==", + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-0.14.3.tgz", + "integrity": "sha512-xMFQXflLVW/AJTWb4soAFP+LB4XuhE7ryiLIX8oTyUoBBgV6U2OPghnFljPjeXbud72O08NYlQ1qsHw1kN/V8Q==", "license": "MIT", "dependencies": { - "@module-federation/error-codes": "0.8.12", - "@module-federation/sdk": "0.8.12" + "@module-federation/error-codes": "0.14.3", + "@module-federation/sdk": "0.14.3" } }, "node_modules/@module-federation/runtime-tools": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.8.12.tgz", - "integrity": "sha512-H97wR/toSU9+UO9S0VHLmPg/7QgohMI52EZlR0Kh0F4PD6fbiWuO4kBIp7R6g0dMI9D4NVKWTKV8xA9ASU+k7g==", + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.14.3.tgz", + "integrity": "sha512-QBETX7iMYXdSa3JtqFlYU+YkpymxETZqyIIRiqg0gW+XGpH3jgU68yjrme2NBJp7URQi/CFZG8KWtfClk0Pjgw==", "license": "MIT", "dependencies": { - "@module-federation/runtime": "0.8.12", - "@module-federation/webpack-bundler-runtime": "0.8.12" + "@module-federation/runtime": "0.14.3", + "@module-federation/webpack-bundler-runtime": "0.14.3" } }, "node_modules/@module-federation/sdk": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.8.12.tgz", - "integrity": "sha512-zFgXYBHbzwIqlrLfn6ewIRXDZCctDDQT2nFhbsZr29yWQgpmW1fm2kJCxQsG0DENGGN1KpzfDoxjjvSKJS/ZHA==", - "license": "MIT", - "dependencies": { - "isomorphic-rslog": "0.0.7" - } + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.14.3.tgz", + "integrity": "sha512-THJZMfbXpqjQOLblCQ8jjcBFFXsGRJwUWE9l/Q4SmuCSKMgAwie7yLT0qSGrHmyBYrsUjAuy+xNB4nfKP0pnGw==", + "license": "MIT" }, "node_modules/@module-federation/third-party-dts-extractor": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@module-federation/third-party-dts-extractor/-/third-party-dts-extractor-0.8.12.tgz", - "integrity": "sha512-+ZO5XpBwEhP9v/Tqk51YgswwbAzj4TXKZ54++uVDsLnOh9yydVGJ/b5pQcSrc8B1C55+498tsDEcNsFgZODgMQ==", + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@module-federation/third-party-dts-extractor/-/third-party-dts-extractor-0.14.3.tgz", + "integrity": "sha512-XAbUoN5hP9iSnrKGikDIy8CloWCKHRIpe+DWOlq8u7uXoRpAPs/a5K7uegxB27dZUNxSFEfqDeHrpQORNnDqPg==", "license": "MIT", "dependencies": { "find-pkg": "2.0.0", @@ -4173,19 +4754,19 @@ } }, "node_modules/@module-federation/webpack-bundler-runtime": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.8.12.tgz", - "integrity": "sha512-zd343RO7/R7Xjh5ym5KdnYQ70z4LBmMxWsa44FS0nyNv04sOq6V1eZSCGKbEhbfqqhbS5Wfj8OzJyedeVvV/OQ==", + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.14.3.tgz", + "integrity": "sha512-hIyJFu34P7bY2NeMIUHAS/mYUHEY71VTAsN0A0AqEJFSVPszheopu9VdXq0VDLrP9KQfuXT8SDxeYeJXyj0mgA==", "license": "MIT", "dependencies": { - "@module-federation/runtime": "0.8.12", - "@module-federation/sdk": "0.8.12" + "@module-federation/runtime": "0.14.3", + "@module-federation/sdk": "0.14.3" } }, "node_modules/@mui/core-downloads-tracker": { - "version": "6.4.11", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.11.tgz", - "integrity": "sha512-CzAQs9CTzlwbsF9ZYB4o4lLwBv1/qNE264NjuYao+ctAXsmlPtYa8RtER4UsUXSMxNN9Qi+aQdYcKl2sUpnmAw==", + "version": "6.4.12", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.12.tgz", + "integrity": "sha512-M7IkG4LqSJfkY+thlQQHNkcS5NdmMDwLq/2RKoW40XR0mv/2BYb6X8fRnyaxP4zGdPD2M4MQdbzKihSVormJ7Q==", "license": "MIT", "peer": true, "funding": { @@ -4194,15 +4775,15 @@ } }, "node_modules/@mui/material": { - "version": "6.4.11", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.11.tgz", - "integrity": "sha512-k2D3FLJS+/qD0qnd6ZlAjGFvaaxe1Dl10NyvpeDzIebMuYdn8VqYe6XBgGueEAtnzSJM4V03VD9kb5Fi24dnTA==", + "version": "6.4.12", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.12.tgz", + "integrity": "sha512-VqoLNS5UaNqoS1FybezZR/PaAvzbTmRe0Mx//afXbolIah43eozpX2FckaFffLvMoiSIyxx1+AMHyENTr2Es0Q==", "license": "MIT", "peer": true, "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/core-downloads-tracker": "^6.4.11", - "@mui/system": "^6.4.11", + "@mui/core-downloads-tracker": "^6.4.12", + "@mui/system": "^6.4.12", "@mui/types": "~7.2.24", "@mui/utils": "^6.4.9", "@popperjs/core": "^2.11.8", @@ -4223,7 +4804,7 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^6.4.11", + "@mui/material-pigment-css": "^6.4.12", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -4307,9 +4888,9 @@ } }, "node_modules/@mui/system": { - "version": "6.4.11", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.11.tgz", - "integrity": "sha512-gibtsrZEwnDaT5+I/KloOj/yHluX5G8heknuxBpQOdEQ3Gc0avjSImn5hSeKp8D4thiwZiApuggIjZw1dQguUA==", + "version": "6.4.12", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.12.tgz", + "integrity": "sha512-fgEfm1qxpKCztndESeL1L0sLwA2c7josZ2w42D8OM3pbLee4bH2twEjoMo6qf7z2rNw1Uc9EU9haXeMoq0oTdQ==", "license": "MIT", "peer": true, "dependencies": { @@ -4455,9 +5036,9 @@ "license": "MIT" }, "node_modules/@mui/x-date-pickers": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.29.3.tgz", - "integrity": "sha512-/A0/8fpLnEFeJKr5YQsI8jqlWPJlOtgfCGcqXHVDOLxgV3lW49+Kh5TZAc1yi6HKT3AG6k4DkNwTuu/RjJeMFA==", + "version": "7.29.4", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.29.4.tgz", + "integrity": "sha512-wJ3tsqk/y6dp+mXGtT9czciAMEO5Zr3IIAHg9x6IL0Eqanqy0N3chbmQQZv3iq0m2qUpQDLvZ4utZBUTJdjNzw==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.7", @@ -4544,7 +5125,8 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/@nexucis/fuzzy/-/fuzzy-0.5.1.tgz", "integrity": "sha512-+swL9itqBe1rx5Pr8ihaIS7STOeFI90HpOFF8y/3wo3ryTxKs0Hf4xc+wiA4yi9nrY4wo3VC8HJOxNiekSBE4w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -4582,26 +5164,26 @@ } }, "node_modules/@openshift-console/dynamic-plugin-sdk": { - "version": "4.19.0-prerelease.1", - "resolved": "https://registry.npmjs.org/@openshift-console/dynamic-plugin-sdk/-/dynamic-plugin-sdk-4.19.0-prerelease.1.tgz", - "integrity": "sha512-2//K5B743tkj3ndoBljlTy6VSjtly6OFZrW9AQ5jCJGa3nI+OivT+XD4ZtZ4q4liVavBTxC+Y0I3z6gxvGLQtg==", + "version": "4.19.0-prerelease.2", + "resolved": "https://registry.npmjs.org/@openshift-console/dynamic-plugin-sdk/-/dynamic-plugin-sdk-4.19.0-prerelease.2.tgz", + "integrity": "sha512-esjzQZMqJTt8ceepSdPWd9s8kKTRJAqcLfMtbfvIIuLWNDk+0OeopiG5k25DXBSe9L8zlOpkmJKogi5aJJl+FA==", "dev": true, "license": "Apache-2.0", "dependencies": { + "@patternfly/react-topology": "^6.2.0", "classnames": "2.x", "immutable": "3.x", "lodash": "^4.17.21", "react": "^17.0.1", - "react-i18next": "^11.7.3", - "react-redux": "7.2.2", + "react-i18next": "^11.12.0", + "react-redux": "7.2.9", "react-router": "5.3.x", "react-router-dom": "5.3.x", "react-router-dom-v5-compat": "^6.11.2", "redux": "4.0.1", "redux-thunk": "2.4.0", "reselect": "4.x", - "typesafe-actions": "^4.2.1", - "whatwg-fetch": "2.x" + "typesafe-actions": "^4.2.1" } }, "node_modules/@openshift-console/dynamic-plugin-sdk-webpack": { @@ -4791,10 +5373,56 @@ "integrity": "sha512-h+ducOLDMSxcuec3+YY3x+stM5ZUSnrl/lC/eVmjypil2El08NuE2MNEPMQWdhrod6VRRZFMNqZw/m82iv6U1A==", "license": "MIT" }, + "node_modules/@patternfly/react-topology": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@patternfly/react-topology/-/react-topology-6.2.0.tgz", + "integrity": "sha512-tqLW9MBJOVq+R1b7MtHsz9zh+kjQ6wW5Ef3ub4cRJPHS3+2kVLv/943MdvSfHDa4uwsjT854Wr0roxlfRtRtvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@dagrejs/dagre": "1.1.2", + "@patternfly/react-core": "^6.0.0", + "@patternfly/react-icons": "^6.0.0", + "@patternfly/react-styles": "^6.0.0", + "@types/d3": "^7.4.0", + "@types/d3-force": "^1.2.1", + "@types/react-measure": "^2.0.6", + "d3": "^7.8.0", + "mobx": "^6.9.0", + "mobx-react": "^7.6.0", + "point-in-svg-path": "^1.0.1", + "popper.js": "^1.16.1", + "react-measure": "^2.3.0", + "tslib": "^2.0.0", + "webcola": "3.4.0" + }, + "peerDependencies": { + "react": "^17 || ^18", + "react-dom": "^17 || ^18" + } + }, + "node_modules/@patternfly/react-topology/node_modules/@patternfly/react-icons": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-6.2.2.tgz", + "integrity": "sha512-XkBwzuV/uiolX+T6QgB3RIqphM1m+vAZjAe3McYtyY22j1rsOdlWDE4RtRrJ1q7EoIZwyZHj0h8T9vMfUsLn4Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "react": "^17 || ^18", + "react-dom": "^17 || ^18" + } + }, + "node_modules/@patternfly/react-topology/node_modules/@patternfly/react-styles": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-6.2.2.tgz", + "integrity": "sha512-rncRDq66H8VnLyb9DrHHlZtPddlpNL9+W0XuQC0L7F6p78hOwSZmoGTW2Vq8/wJplDj8h/61qRpfRF9VEYPW0g==", + "dev": true, + "license": "MIT" + }, "node_modules/@perses-dev/components": { - "version": "0.51.0-rc.0", - "resolved": "https://registry.npmjs.org/@perses-dev/components/-/components-0.51.0-rc.0.tgz", - "integrity": "sha512-rHg4t+gf9ZwrlJXglhYMSBC9iigYjmFOOjptL6CDzC3prEJp6J4W+M97qmCFWWsZksGmjBJhEn6IgFHrN2Oy+Q==", + "version": "0.51.0", + "resolved": "https://registry.npmjs.org/@perses-dev/components/-/components-0.51.0.tgz", + "integrity": "sha512-tcBxv9tEYT9P41mUg8CBMj4tL5NXOP1sVSn9eJYG7XKzygH1BFuvHcsOsylyTH5F+Xm5R78llRX+TuEAWKRIng==", "license": "Apache-2.0", "dependencies": { "@atlaskit/pragmatic-drag-and-drop": "^1.4.0", @@ -4802,12 +5430,13 @@ "@codemirror/lang-json": "^6.0.1", "@fontsource/lato": "^4.5.10", "@mui/x-date-pickers": "^7.23.1", - "@perses-dev/core": "0.51.0-rc.0", + "@perses-dev/core": "0.51.0", "@tanstack/react-table": "^8.20.5", "@uiw/react-codemirror": "^4.19.1", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "echarts": "5.5.0", + "immer": "^10.1.1", "lodash": "^4.17.21", "mathjs": "^10.6.4", "mdi-material-ui": "^7.9.2", @@ -4815,8 +5444,7 @@ "react-colorful": "^5.6.1", "react-error-boundary": "^3.1.4", "react-hook-form": "^7.51.3", - "react-virtuoso": "^4.12.2", - "zod": "^3.21.4" + "react-virtuoso": "^4.12.2" }, "peerDependencies": { "@mui/material": "^6.1.10", @@ -4825,9 +5453,9 @@ } }, "node_modules/@perses-dev/core": { - "version": "0.51.0-rc.0", - "resolved": "https://registry.npmjs.org/@perses-dev/core/-/core-0.51.0-rc.0.tgz", - "integrity": "sha512-RKb5faHrx1RAEW5NziStiemPkfJzBiglSmX6IkcNr4IcMa2IDfDX968Ey92i0/DeRop8gu5VE8vc292wA0p64Q==", + "version": "0.51.0", + "resolved": "https://registry.npmjs.org/@perses-dev/core/-/core-0.51.0.tgz", + "integrity": "sha512-RY72PRVTVEURCWWgUO8KInes0W6lwI5YauLjwpn9HjwxA+JocoHmm723byOAyE145f4sqeZA+MWlYVYj7ymzLg==", "license": "Apache-2.0", "dependencies": { "date-fns": "^4.1.0", @@ -4842,14 +5470,14 @@ } }, "node_modules/@perses-dev/dashboards": { - "version": "0.51.0-rc.0", - "resolved": "https://registry.npmjs.org/@perses-dev/dashboards/-/dashboards-0.51.0-rc.0.tgz", - "integrity": "sha512-uulxQGRpOH4cbMPPMa5xQMb6+Zz9D+6LnpHwxyT6coAi05dlmIB+RJLY1gPd+TxzMbQ75qvKjrU9I2zOgpgjXQ==", + "version": "0.51.0", + "resolved": "https://registry.npmjs.org/@perses-dev/dashboards/-/dashboards-0.51.0.tgz", + "integrity": "sha512-kzHfVq0g1IT2JKobNfx2/9L6TF2uT1wbjSEcNn7RjCFk1RV0uAMpoV5Estxtf6ROn3pTZhrFtLxFFcfnWX/ezw==", "license": "Apache-2.0", "dependencies": { - "@perses-dev/components": "0.51.0-rc.0", - "@perses-dev/core": "0.51.0-rc.0", - "@perses-dev/plugin-system": "0.51.0-rc.0", + "@perses-dev/components": "0.51.0", + "@perses-dev/core": "0.51.0", + "@perses-dev/plugin-system": "0.51.0", "@types/react-grid-layout": "^1.3.2", "date-fns": "^4.1.0", "immer": "^10.1.1", @@ -4865,22 +5493,23 @@ }, "peerDependencies": { "@mui/material": "^6.1.10", - "@tanstack/react-query": "^5.64.2", + "@tanstack/react-query": "^4.39.1", "react": "^17.0.2 || ^18.0.0", "react-dom": "^17.0.2 || ^18.0.0" } }, "node_modules/@perses-dev/explore": { - "version": "0.0.0-snapshot-histogram-types-78c5104", - "resolved": "https://registry.npmjs.org/@perses-dev/explore/-/explore-0.0.0-snapshot-histogram-types-78c5104.tgz", - "integrity": "sha512-j8fqeBfkW16AJ/JPd7GH1uSmr2q5FFTZpmN6rZTpUDxBvSYYr7KQUpwz2lKQ6b2delXDXBln6lu3aUn2wledRg==", + "version": "0.51.0", + "resolved": "https://registry.npmjs.org/@perses-dev/explore/-/explore-0.51.0.tgz", + "integrity": "sha512-1Vk8itkIN9rEk3+xP/5Xyo1Cp4O6kfao975dsQmBQsFGlPF+vJQPMIBXGTYnbS/cdNSbQrnM5IURrjI/X1YZxg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@nexucis/fuzzy": "^0.5.1", - "@perses-dev/components": "0.0.0-snapshot-histogram-types-78c5104", - "@perses-dev/core": "0.0.0-snapshot-histogram-types-78c5104", - "@perses-dev/dashboards": "0.0.0-snapshot-histogram-types-78c5104", - "@perses-dev/plugin-system": "0.0.0-snapshot-histogram-types-78c5104", + "@perses-dev/components": "0.51.0", + "@perses-dev/core": "0.51.0", + "@perses-dev/dashboards": "0.51.0", + "@perses-dev/plugin-system": "0.51.0", "@types/react-grid-layout": "^1.3.2", "date-fns": "^4.1.0", "immer": "^10.1.1", @@ -4897,20 +5526,20 @@ }, "peerDependencies": { "@mui/material": "^6.1.10", - "@tanstack/react-query": "^5.64.2", + "@tanstack/react-query": "^4.39.1", "react": "^17.0.2 || ^18.0.0", "react-dom": "^17.0.2 || ^18.0.0" } }, "node_modules/@perses-dev/plugin-system": { - "version": "0.51.0-rc.0", - "resolved": "https://registry.npmjs.org/@perses-dev/plugin-system/-/plugin-system-0.51.0-rc.0.tgz", - "integrity": "sha512-ox2VZWx46ekczDaG9aopSnixUnhIi1dRKrJbBLl0Y+1/WrM77B01vZ3SsF3u3Nvx0onNqIG9t01mafi6vWG14A==", + "version": "0.51.0", + "resolved": "https://registry.npmjs.org/@perses-dev/plugin-system/-/plugin-system-0.51.0.tgz", + "integrity": "sha512-4zhRKfcmIUedxoZnwhWLvx0zzrSfTdAkA7C2WUow0HJ8LCPTI/D4uB33oW3XAi2lFIJxcW9mpUBAK4FtTSjizg==", "license": "Apache-2.0", "dependencies": { - "@module-federation/enhanced": "^0.8.9", - "@perses-dev/components": "0.51.0-rc.0", - "@perses-dev/core": "0.51.0-rc.0", + "@module-federation/enhanced": "^0.14.3", + "@perses-dev/components": "0.51.0", + "@perses-dev/core": "0.51.0", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "immer": "^10.1.1", @@ -4921,28 +5550,28 @@ }, "peerDependencies": { "@mui/material": "^6.1.10", - "@tanstack/react-query": "^5.64.2", + "@tanstack/react-query": "^4.39.1", "react": "^17.0.2 || ^18.0.0", "react-dom": "^17.0.2 || ^18.0.0" } }, "node_modules/@perses-dev/plugin-system/node_modules/@module-federation/dts-plugin": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-0.8.12.tgz", - "integrity": "sha512-BhRZsG68XtGzKM0N02/3ldiW+PTc+QaHCMNyiZnk3GcxS0qe6USu7fTQwRIPCC2GONIdoHD+GVYPN/NJWjbTFg==", + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-0.14.3.tgz", + "integrity": "sha512-QiE4wcra6dNo36028cX//QfX0uKF6UeoQoaVIIu06imF4KjCNQD3bE91D6H3DlVVD/UjnIDeUSt9AoGesLzbSA==", "license": "MIT", "dependencies": { - "@module-federation/error-codes": "0.8.12", - "@module-federation/managers": "0.8.12", - "@module-federation/sdk": "0.8.12", - "@module-federation/third-party-dts-extractor": "0.8.12", + "@module-federation/error-codes": "0.14.3", + "@module-federation/managers": "0.14.3", + "@module-federation/sdk": "0.14.3", + "@module-federation/third-party-dts-extractor": "0.14.3", "adm-zip": "^0.5.10", "ansi-colors": "^4.1.3", - "axios": "^1.7.4", + "axios": "^1.8.2", "chalk": "3.0.0", "fs-extra": "9.1.0", "isomorphic-ws": "5.0.0", - "koa": "2.15.3", + "koa": "2.16.1", "lodash.clonedeepwith": "4.5.0", "log4js": "6.9.1", "node-schedule": "2.1.1", @@ -4960,24 +5589,29 @@ } }, "node_modules/@perses-dev/plugin-system/node_modules/@module-federation/enhanced": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@module-federation/enhanced/-/enhanced-0.8.12.tgz", - "integrity": "sha512-uJODfWqL3C87LUu1E0ZLPRqE0sUt6QFH97bMFoCWYhkf8JS7J+wMc4KSc31ApaJs7CeQICXR1CYBAZDSdxkrOQ==", - "license": "MIT", - "dependencies": { - "@module-federation/bridge-react-webpack-plugin": "0.8.12", - "@module-federation/data-prefetch": "0.8.12", - "@module-federation/dts-plugin": "0.8.12", - "@module-federation/error-codes": "0.8.12", - "@module-federation/inject-external-runtime-core-plugin": "0.8.12", - "@module-federation/managers": "0.8.12", - "@module-federation/manifest": "0.8.12", - "@module-federation/rspack": "0.8.12", - "@module-federation/runtime-tools": "0.8.12", - "@module-federation/sdk": "0.8.12", + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@module-federation/enhanced/-/enhanced-0.14.3.tgz", + "integrity": "sha512-9R15Sm+hCn9yNtOTEwN1cHppC/sMb/LfoTcA94jLMB6lcyYz+uNzc5JliyrMawU1/guOQiBZkUVL/thB8DHURw==", + "license": "MIT", + "dependencies": { + "@module-federation/bridge-react-webpack-plugin": "0.14.3", + "@module-federation/cli": "0.14.3", + "@module-federation/data-prefetch": "0.14.3", + "@module-federation/dts-plugin": "0.14.3", + "@module-federation/error-codes": "0.14.3", + "@module-federation/inject-external-runtime-core-plugin": "0.14.3", + "@module-federation/managers": "0.14.3", + "@module-federation/manifest": "0.14.3", + "@module-federation/rspack": "0.14.3", + "@module-federation/runtime-tools": "0.14.3", + "@module-federation/sdk": "0.14.3", "btoa": "^1.2.1", + "schema-utils": "^4.3.0", "upath": "2.0.1" }, + "bin": { + "mf": "bin/mf.js" + }, "peerDependencies": { "typescript": "^4.9.0 || ^5.0.0", "vue-tsc": ">=1.0.24", @@ -4996,18 +5630,19 @@ } }, "node_modules/@perses-dev/plugin-system/node_modules/@module-federation/rspack": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@module-federation/rspack/-/rspack-0.8.12.tgz", - "integrity": "sha512-G73Cq7VwKdpFZwMd9hEBsE2HLF7mNQEIvLSiW6HOZG/+iWRRED1opiVkrjOmkcg770rChYSqTBiLvUS7HDT5Ow==", + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@module-federation/rspack/-/rspack-0.14.3.tgz", + "integrity": "sha512-s02E7n9CnR+IMraYwGqfSU2uScENPU+TUd45YteMKxcKOIqNRALtGMn/YT24bbnj+wZ/jhvzr7Rbcx9AkaxKhA==", "license": "MIT", "dependencies": { - "@module-federation/bridge-react-webpack-plugin": "0.8.12", - "@module-federation/dts-plugin": "0.8.12", - "@module-federation/inject-external-runtime-core-plugin": "0.8.12", - "@module-federation/managers": "0.8.12", - "@module-federation/manifest": "0.8.12", - "@module-federation/runtime-tools": "0.8.12", - "@module-federation/sdk": "0.8.12" + "@module-federation/bridge-react-webpack-plugin": "0.14.3", + "@module-federation/dts-plugin": "0.14.3", + "@module-federation/inject-external-runtime-core-plugin": "0.14.3", + "@module-federation/managers": "0.14.3", + "@module-federation/manifest": "0.14.3", + "@module-federation/runtime-tools": "0.14.3", + "@module-federation/sdk": "0.14.3", + "btoa": "1.2.1" }, "peerDependencies": { "@rspack/core": ">=0.7", @@ -5112,16 +5747,19 @@ } }, "node_modules/@perses-dev/scatter-chart-plugin": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@perses-dev/scatter-chart-plugin/-/scatter-chart-plugin-0.6.0.tgz", - "integrity": "sha512-y1pbxhBQCY7sK+OoYLRguA4tLuzZrAOJ+g8j0XCZnmGKFRE51aqhfv4BtR/QDa0SPBvz2SwBaySZSTt05Ik90Q==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@perses-dev/scatter-chart-plugin/-/scatter-chart-plugin-0.7.0.tgz", + "integrity": "sha512-l9z1qSSqqhniYgbncrbv3PMkURXHJwBPJhycjlQdkHtoPdSLw6n4BlcbJyqJXVBjubnVnENGkhcJcAbHSUskHQ==", + "dependencies": { + "react-virtuoso": "^4.12.2" + }, "peerDependencies": { "@emotion/react": "^11.7.1", "@emotion/styled": "^11.6.0", "@hookform/resolvers": "^3.2.0", - "@perses-dev/components": "^0.51.0-beta.1", - "@perses-dev/core": "^0.51.0-beta.1", - "@perses-dev/plugin-system": "^0.51.0-beta.1", + "@perses-dev/components": "^0.51.0-rc.1", + "@perses-dev/core": "^0.51.0-rc.1", + "@perses-dev/plugin-system": "^0.51.0-rc.1", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "echarts": "5.5.0", @@ -5132,29 +5770,24 @@ } }, "node_modules/@perses-dev/tempo-plugin": { - "version": "0.51.0-beta.2", - "resolved": "https://registry.npmjs.org/@perses-dev/tempo-plugin/-/tempo-plugin-0.51.0-beta.2.tgz", - "integrity": "sha512-uBlUf6joe4z25vLToSbTQjB0F20J7B7ND+rWura5TudA1IvuWzDkeTNEJJL3b1GHB+4PxHaj++YAEY4ODDfIQw==", + "version": "0.51.0-rc.3", + "resolved": "https://registry.npmjs.org/@perses-dev/tempo-plugin/-/tempo-plugin-0.51.0-rc.3.tgz", + "integrity": "sha512-mBZ95PiVrxawNDjqsfatMJywqwYSk+AjCpwTeS+V18dSf+epPvc2IqxIEST/TMReoe/KfJEKr0atzXniJUsEUw==", "dependencies": { "@codemirror/autocomplete": "^6.18.4", "@grafana/lezer-traceql": "^0.0.20", - "@lezer/highlight": "^1.2.1x", - "@perses-dev/components": "0.0.0-snapshot-histogram-types-78c5104", - "@perses-dev/core": "0.0.0-snapshot-histogram-types-78c5104", - "@perses-dev/dashboards": "0.0.0-snapshot-histogram-types-78c5104", - "@perses-dev/explore": "0.0.0-snapshot-histogram-types-78c5104", - "@perses-dev/plugin-system": "0.0.0-snapshot-histogram-types-78c5104" + "@lezer/highlight": "^1.2.1x" }, "peerDependencies": { "@emotion/react": "^11.7.1", "@emotion/styled": "^11.6.0", "@hookform/resolvers": "^3.2.0", - "@perses-dev/components": "^0.51.0-beta.1", - "@perses-dev/core": "^0.51.0-beta.1", - "@perses-dev/dashboards": "^0.51.0-beta.1", - "@perses-dev/explore": "^0.51.0-beta.1", - "@perses-dev/plugin-system": "^0.51.0-beta.1", - "@tanstack/react-query": "^5.64.2", + "@perses-dev/components": "^0.51.0-rc.1", + "@perses-dev/core": "^0.51.0-rc.1", + "@perses-dev/dashboards": "^0.51.0-rc.1", + "@perses-dev/explore": "^0.51.0-rc.1", + "@perses-dev/plugin-system": "^0.51.0-rc.1", + "@tanstack/react-query": "^4.39.1", "@uiw/react-codemirror": "^4.19.1", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", @@ -5177,52 +5810,50 @@ } }, "node_modules/@perses-dev/trace-table-plugin": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@perses-dev/trace-table-plugin/-/trace-table-plugin-0.6.0.tgz", - "integrity": "sha512-ICgaVjVHSClx7F/eF0qObhz7hfljDJ12PP1iOui7uoIi/MUj13E3wexElfnWwl2Pz0k6dhL2RV0xY96NL8n9yQ==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@perses-dev/trace-table-plugin/-/trace-table-plugin-0.7.0.tgz", + "integrity": "sha512-n+I0b9YOqhqXNF1mrR2cc6K7hoiBtYyqS5CN8WaNH5qpgE2MdRZAjfZz3Dp9024gOZl4VDwsb8LdNSGUCyD7Nw==", "dependencies": { - "@mui/x-data-grid": "^7.23.1" + "@mui/x-data-grid": "^7.20.0" }, "peerDependencies": { "@emotion/react": "^11.7.1", "@emotion/styled": "^11.6.0", "@hookform/resolvers": "^3.2.0", - "@perses-dev/components": "^0.51.0-beta.1", - "@perses-dev/core": "^0.51.0-beta.1", - "@perses-dev/plugin-system": "^0.51.0-beta.1", + "@perses-dev/components": "^0.51.0-rc.1", + "@perses-dev/core": "^0.51.0-rc.1", + "@perses-dev/plugin-system": "^0.51.0-rc.1", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "echarts": "5.5.0", "lodash": "^4.17.21", "react": "^17.0.2 || ^18.0.0", "react-dom": "^17.0.2 || ^18.0.0", - "react-router-dom": "^6.11.0", + "react-router-dom": "^5 || ^6 || ^7", "use-resize-observer": "^9.0.0" } }, "node_modules/@perses-dev/tracing-gantt-chart-plugin": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@perses-dev/tracing-gantt-chart-plugin/-/tracing-gantt-chart-plugin-0.6.0.tgz", - "integrity": "sha512-9h3jJWwht7ptmwtjMCEUML9JQ1CRbwq8ZuHPYFr0RJaB3ZGKFMUeXw19JT/TNsxpRT5ST+yAMrFM+xzXtQVZbQ==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@perses-dev/tracing-gantt-chart-plugin/-/tracing-gantt-chart-plugin-0.7.0.tgz", + "integrity": "sha512-eGdMpbna0fnm1EvwUWxU3ByCBSANJisAaDK5Zj4xuNbUPGzO206MpDkIw9VJMzx/ENIyGjg3YatRrWk/OFGK+g==", "dependencies": { - "@perses-dev/components": "0.0.0-snapshot-histogram-types-78c5104", - "@perses-dev/core": "0.0.0-snapshot-histogram-types-78c5104", - "@perses-dev/plugin-system": "0.0.0-snapshot-histogram-types-78c5104", "color-hash": "^2.0.2" }, "peerDependencies": { "@emotion/react": "^11.7.1", "@emotion/styled": "^11.6.0", "@hookform/resolvers": "^3.2.0", - "@perses-dev/components": "^0.51.0-beta.1", - "@perses-dev/core": "^0.51.0-beta.1", - "@perses-dev/plugin-system": "^0.51.0-beta.1", + "@perses-dev/components": "^0.51.0-rc.1", + "@perses-dev/core": "^0.51.0-rc.1", + "@perses-dev/plugin-system": "^0.51.0-rc.1", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "echarts": "5.5.0", "lodash": "^4.17.21", "react": "^17.0.2 || ^18.0.0", "react-dom": "^17.0.2 || ^18.0.0", + "react-router-dom": "^5 || ^6 || ^7", "use-resize-observer": "^9.0.0" } }, @@ -5260,27 +5891,27 @@ } }, "node_modules/@rspack/binding": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-1.3.10.tgz", - "integrity": "sha512-9TjO+Ig5U4VqdYWpBsv01n4d2KsgFfdXGIE7zdHXDDbry0avL0J4109ESqSeP9uC+Bi7ZCF53iaxJRvZDflNVQ==", + "version": "1.3.15", + "resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-1.3.15.tgz", + "integrity": "sha512-utNPuJglLO5lW9XbwIqjB7+2ilMo6JkuVLTVdnNVKU94FW7asn9F/qV+d+MgjUVqU1QPCGm0NuGO9xhbgeJ7pg==", "license": "MIT", "peer": true, "optionalDependencies": { - "@rspack/binding-darwin-arm64": "1.3.10", - "@rspack/binding-darwin-x64": "1.3.10", - "@rspack/binding-linux-arm64-gnu": "1.3.10", - "@rspack/binding-linux-arm64-musl": "1.3.10", - "@rspack/binding-linux-x64-gnu": "1.3.10", - "@rspack/binding-linux-x64-musl": "1.3.10", - "@rspack/binding-win32-arm64-msvc": "1.3.10", - "@rspack/binding-win32-ia32-msvc": "1.3.10", - "@rspack/binding-win32-x64-msvc": "1.3.10" + "@rspack/binding-darwin-arm64": "1.3.15", + "@rspack/binding-darwin-x64": "1.3.15", + "@rspack/binding-linux-arm64-gnu": "1.3.15", + "@rspack/binding-linux-arm64-musl": "1.3.15", + "@rspack/binding-linux-x64-gnu": "1.3.15", + "@rspack/binding-linux-x64-musl": "1.3.15", + "@rspack/binding-win32-arm64-msvc": "1.3.15", + "@rspack/binding-win32-ia32-msvc": "1.3.15", + "@rspack/binding-win32-x64-msvc": "1.3.15" } }, "node_modules/@rspack/binding-darwin-arm64": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.3.10.tgz", - "integrity": "sha512-0k/j8OeMSVm5u5Nzckp9Ie7S7hprnvNegebnGr+L6VCyD7sMqm4m+4rLHs99ZklYdH0dZtY2+LrzrtjUZCqfew==", + "version": "1.3.15", + "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.3.15.tgz", + "integrity": "sha512-f+DnVRENRdVe+ufpZeqTtWAUDSTnP48jVo7x9KWsXf8XyJHUi+eHKEPrFoy1HvL1/k5yJ3HVnFBh1Hb9cNIwSg==", "cpu": [ "arm64" ], @@ -5292,9 +5923,9 @@ "peer": true }, "node_modules/@rspack/binding-darwin-x64": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-1.3.10.tgz", - "integrity": "sha512-jOyqYW/18cgxw60wK5oqJvM194pbD4H99xaif89McNtLkH3npFvBkXBHVWWuOHGoXNX0LhRpHcI89p9b9THQZQ==", + "version": "1.3.15", + "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-1.3.15.tgz", + "integrity": "sha512-TfUvEIBqYUT2OK01BYXb2MNcZeZIhAnJy/5aj0qV0uy4KlvwW63HYcKWa1sFd4Ac7bnGShDkanvP3YEuHOFOyg==", "cpu": [ "x64" ], @@ -5306,9 +5937,9 @@ "peer": true }, "node_modules/@rspack/binding-linux-arm64-gnu": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.3.10.tgz", - "integrity": "sha512-zhF5ZNaT/7pxrm8xD3dWG1b4x+FO3LbVeZZG448CjpSo5T57kPD+SaGUU1GcPpn6mexf795x0SVS49aH7/e3Dg==", + "version": "1.3.15", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.3.15.tgz", + "integrity": "sha512-D/YjYk9snKvYm1Elotq8/GsEipB4ZJWVv/V8cZ+ohhFNOPzygENi6JfyI06TryBTQiN0/JDZqt/S9RaWBWnMqw==", "cpu": [ "arm64" ], @@ -5320,9 +5951,9 @@ "peer": true }, "node_modules/@rspack/binding-linux-arm64-musl": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.3.10.tgz", - "integrity": "sha512-o3x7IrOSCHK6lcRvdZgsSuOG1CHRQR00xiyLW7kkWmNm7t417LC9xdFWKIK62C5fKXGC5YcTbUkDMnQujespkg==", + "version": "1.3.15", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.3.15.tgz", + "integrity": "sha512-lJbBsPMOiR0hYPCSM42yp7QiZjfo0ALtX7ws2wURpsQp3BMfRVAmXU3Ixpo2XCRtG1zj8crHaCmAWOJTS0smsA==", "cpu": [ "arm64" ], @@ -5334,9 +5965,9 @@ "peer": true }, "node_modules/@rspack/binding-linux-x64-gnu": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.3.10.tgz", - "integrity": "sha512-FMSi28VZhXMr15picOHFynULhqZ/FODPxRIS6uNrvPRYcbNuiO1v+VHV6X88mhOMmJ/aVF6OwjUO/o2l1FVa9Q==", + "version": "1.3.15", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.3.15.tgz", + "integrity": "sha512-qGB8ucHklrzNg6lsAS36VrBsCbOw0acgpQNqTE5cuHWrp1Pu3GFTRiFEogenxEmzoRbohMZt0Ev5grivrcgKBQ==", "cpu": [ "x64" ], @@ -5348,9 +5979,9 @@ "peer": true }, "node_modules/@rspack/binding-linux-x64-musl": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-1.3.10.tgz", - "integrity": "sha512-e0xbY9SlbRGIFz41v1yc0HfREvmgMnLV1bLmTSPK8wI2suIEJ7iYYqsocHOAOk86qLZcxVrTnL6EjUcNaRTWlg==", + "version": "1.3.15", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-1.3.15.tgz", + "integrity": "sha512-qRn6e40fLQP+N2rQD8GAj/h4DakeTIho32VxTIaHRVuzw68ZD7VmKkwn55ssN370ejmey35ZdoNFNE12RBrMZA==", "cpu": [ "x64" ], @@ -5362,9 +5993,9 @@ "peer": true }, "node_modules/@rspack/binding-win32-arm64-msvc": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.3.10.tgz", - "integrity": "sha512-YHJPvEujWeWjU6EUF6sDpaec9rsOtKVvy16YCtGaxRpDQXqfuxibnp6Ge0ZTTrY+joRiWehRA9OUI+3McqI+QA==", + "version": "1.3.15", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.3.15.tgz", + "integrity": "sha512-7uJ7dWhO1nWXJiCss6Rslz8hoAxAhFpwpbWja3eHgRb7O4NPHg6MWw63AQSI2aFVakreenfu9yXQqYfpVWJ2dA==", "cpu": [ "arm64" ], @@ -5376,9 +6007,9 @@ "peer": true }, "node_modules/@rspack/binding-win32-ia32-msvc": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.3.10.tgz", - "integrity": "sha512-2iwSBzVBC89ZSk56MYwgirh3bda2WKmL9I3qPajiTEivChXpX7jp83jAtGE6CPqPYcccYz6nrURTHNUZhqXxVw==", + "version": "1.3.15", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.3.15.tgz", + "integrity": "sha512-UsaWTYCjDiSCB0A0qETgZk4QvhwfG8gCrO4SJvA+QSEWOmgSai1YV70prFtLLIiyT9mDt1eU3tPWl1UWPRU/EQ==", "cpu": [ "ia32" ], @@ -5390,9 +6021,9 @@ "peer": true }, "node_modules/@rspack/binding-win32-x64-msvc": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.3.10.tgz", - "integrity": "sha512-ehWJ9Y5Zezj/+uJpiWbt29RZaRIM00f91PWuabM6/sKmHJhdCEE21u5iI3B8DeW/EjJsH8zkI69YYAxJWwGn9A==", + "version": "1.3.15", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.3.15.tgz", + "integrity": "sha512-ZnDIc9Es8EF94MirPDN+hOMt7tkb8nMEbRJFKLMmNd0ElNPgsql+1cY5SqyGRH1hsKB87KfSUQlhFiKZvzbfIg==", "cpu": [ "x64" ], @@ -5404,16 +6035,15 @@ "peer": true }, "node_modules/@rspack/core": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/@rspack/core/-/core-1.3.10.tgz", - "integrity": "sha512-YomvSRGuMUQgCE2rNMdff2q1Z0YpZw/z6m5+PVTMSs9l/q69YKUzpbpSD8YyB5i1DddrRxC2RE34DkrBuwlREQ==", + "version": "1.3.15", + "resolved": "https://registry.npmjs.org/@rspack/core/-/core-1.3.15.tgz", + "integrity": "sha512-QuElIC8jXSKWAp0LSx18pmbhA7NiA5HGoVYesmai90UVxz98tud0KpMxTVCg+0lrLrnKZfCWN9kwjCxM5pGnrA==", "license": "MIT", "peer": true, "dependencies": { - "@module-federation/runtime-tools": "0.13.1", - "@rspack/binding": "1.3.10", - "@rspack/lite-tapable": "1.0.1", - "caniuse-lite": "^1.0.30001717" + "@module-federation/runtime-tools": "0.14.3", + "@rspack/binding": "1.3.15", + "@rspack/lite-tapable": "1.0.1" }, "engines": { "node": ">=16.0.0" @@ -5427,65 +6057,6 @@ } } }, - "node_modules/@rspack/core/node_modules/@module-federation/error-codes": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.13.1.tgz", - "integrity": "sha512-azgGDBnFRfqlivHOl96ZjlFUFlukESz2Rnnz/pINiSqoBBNjUE0fcAZP4X6jgrVITuEg90YkruZa7pW9I3m7Uw==", - "license": "MIT", - "peer": true - }, - "node_modules/@rspack/core/node_modules/@module-federation/runtime": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.13.1.tgz", - "integrity": "sha512-ZHnYvBquDm49LiHfv6fgagMo/cVJneijNJzfPh6S0CJrPS2Tay1bnTXzy8VA5sdIrESagYPaskKMGIj7YfnPug==", - "license": "MIT", - "peer": true, - "dependencies": { - "@module-federation/error-codes": "0.13.1", - "@module-federation/runtime-core": "0.13.1", - "@module-federation/sdk": "0.13.1" - } - }, - "node_modules/@rspack/core/node_modules/@module-federation/runtime-core": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-0.13.1.tgz", - "integrity": "sha512-TfyKfkSAentKeuvSsAItk8s5tqQSMfIRTPN2e1aoaq/kFhE+7blps719csyWSX5Lg5Es7WXKMsXHy40UgtBtuw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@module-federation/error-codes": "0.13.1", - "@module-federation/sdk": "0.13.1" - } - }, - "node_modules/@rspack/core/node_modules/@module-federation/runtime-tools": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.13.1.tgz", - "integrity": "sha512-GEF1pxqLc80osIMZmE8j9UKZSaTm2hX2lql8tgIH/O9yK4wnF06k6LL5Ah+wJt+oJv6Dj55ri/MoxMP4SXoPNA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@module-federation/runtime": "0.13.1", - "@module-federation/webpack-bundler-runtime": "0.13.1" - } - }, - "node_modules/@rspack/core/node_modules/@module-federation/sdk": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.13.1.tgz", - "integrity": "sha512-bmf2FGQ0ymZuxYnw9bIUfhV3y6zDhaqgydEjbl4msObKMLGXZqhse2pTIIxBFpIxR1oONKX/y2FAolDCTlWKiw==", - "license": "MIT", - "peer": true - }, - "node_modules/@rspack/core/node_modules/@module-federation/webpack-bundler-runtime": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.13.1.tgz", - "integrity": "sha512-QSuSIGa09S8mthbB1L6xERqrz+AzPlHR6D7RwAzssAc+IHf40U6NiTLPzUqp9mmKDhC5Tm0EISU0ZHNeJpnpBQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@module-federation/runtime": "0.13.1", - "@module-federation/sdk": "0.13.1" - } - }, "node_modules/@rspack/lite-tapable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@rspack/lite-tapable/-/lite-tapable-1.0.1.tgz", @@ -5537,23 +6108,34 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@swc/helpers": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", + "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/@tanstack/query-core": { - "version": "4.36.1", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.36.1.tgz", - "integrity": "sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA==", + "version": "4.39.2", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.39.2.tgz", + "integrity": "sha512-Crxng4rNDcN6bbppM/P8WiUR6JoGHY+5jJJjU7em1nJ6IUd88+AZpFAWz9ANQKL4GZOQ2CGzWEkuTL7o+rzvIw==", "license": "MIT", + "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" } }, "node_modules/@tanstack/react-query": { - "version": "4.36.1", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.36.1.tgz", - "integrity": "sha512-y7ySVHFyyQblPl3J3eQBWpXZkliroki3ARnBKsdJchlgt7yJLRDUcf4B8soufgiYt3pEQIkBWBx1N9/ZPIeUWw==", + "version": "4.39.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.39.2.tgz", + "integrity": "sha512-SFoKo96R5HaT9o6Y5yZ2fK02+kNQr6gCyv0GIEluGZ0pcXEnIW2E06lrGuVUtUQ/o606efOYh7nl/ePpus0Q+Q==", "license": "MIT", + "peer": true, "dependencies": { - "@tanstack/query-core": "4.36.1", + "@tanstack/query-core": "4.39.2", "use-sync-external-store": "^1.2.0" }, "funding": { @@ -5685,9 +6267,9 @@ } }, "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", "dev": true, "license": "MIT", "dependencies": { @@ -5726,24 +6308,173 @@ "@types/node": "*" } }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, "node_modules/@types/d3-array": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", "license": "MIT" }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/d3-color": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", "license": "MIT" }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz", + "integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/d3-ease": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", "license": "MIT" }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-1.2.7.tgz", + "integrity": "sha512-zySqZfnxn67RVEGWzpD9dQA0AbNIp4Rj0qGvAuUdUNfGLrwuGCbEGAGze5hEdNaHJKQT2gTqr6j+qAzncm11ew==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/d3-interpolate": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", @@ -5759,6 +6490,27 @@ "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", "license": "MIT" }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/d3-scale": { "version": "4.0.9", "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", @@ -5768,6 +6520,20 @@ "@types/d3-time": "*" } }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/d3-shape": { "version": "3.1.7", "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", @@ -5783,12 +6549,40 @@ "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", "license": "MIT" }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/d3-timer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", "license": "MIT" }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, "node_modules/@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", @@ -5810,15 +6604,15 @@ } }, "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "license": "MIT" }, "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz", + "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5854,6 +6648,13 @@ "@types/send": "*" } }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -5872,10 +6673,21 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz", + "integrity": "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", "dev": true, "license": "MIT" }, @@ -5934,9 +6746,9 @@ "license": "MIT" }, "node_modules/@types/lodash": { - "version": "4.17.16", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.16.tgz", - "integrity": "sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==", + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.17.tgz", + "integrity": "sha512-RRVJ+J3J+WmyOTqnz3PiBLA501eKwXl2noseKOrNo/6+XEHjTAxO4xHvxQB6QuNm+s4WRbn6rSiap8+EA+ykFQ==", "dev": true, "license": "MIT" }, @@ -5961,9 +6773,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "18.19.100", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.100.tgz", - "integrity": "sha512-ojmMP8SZBKprc3qGrGk8Ujpo80AXkrP7G2tOT4VWr5jlr5DHjsJF+emXJz+Wm0glmy4Js62oKMdZZ6B9Y+tEcA==", + "version": "18.19.111", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.111.tgz", + "integrity": "sha512-90sGdgA+QLJr1F9X79tQuEut0gEYIfkX9pydI4XGRgvFo9g2JWswefI+WUSUHPYVBHYSEfTEqBxA5hQvAZB3Mw==", "license": "MIT", "dependencies": { "undici-types": "~5.26.4" @@ -6001,15 +6813,15 @@ "peer": true }, "node_modules/@types/prop-types": { - "version": "15.7.14", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", - "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", "license": "MIT" }, "node_modules/@types/qs": { - "version": "6.9.18", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", - "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", "dev": true, "license": "MIT" }, @@ -6021,9 +6833,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "17.0.85", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.85.tgz", - "integrity": "sha512-5oBDUsRDsrYq4DdyHaL99gE1AJCfuDhyxqF6/55fvvOIRkp1PpKuwJ+aMiGJR+GJt7YqMNclPROTHF20vY2cXA==", + "version": "17.0.87", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.87.tgz", + "integrity": "sha512-wpg9AbtJ6agjA+BKYmhG6dRWEU/2DHYwMzCaBzsz137ft6IyuqZ5fI4ic1DWL4DrI03Zy78IyVE6ucrXl0mu4g==", "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -6040,14 +6852,37 @@ "@types/react": "*" } }, - "node_modules/@types/react-helmet": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.11.tgz", - "integrity": "sha512-0QcdGLddTERotCXo3VFlUSWO3ztraw8nZ6e3zJSgG7apwV5xt+pJUS8ewPBqT4NYB1optGLprNQzFleIY84u/g==", + "node_modules/@types/react-helmet": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.11.tgz", + "integrity": "sha512-0QcdGLddTERotCXo3VFlUSWO3ztraw8nZ6e3zJSgG7apwV5xt+pJUS8ewPBqT4NYB1optGLprNQzFleIY84u/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-measure": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@types/react-measure/-/react-measure-2.0.12.tgz", + "integrity": "sha512-Y6V11CH6bU7RhqrIdENPwEUZlPXhfXNGylMNnGwq5TAEs2wDoBA3kSVVM/EQ8u72sz5r9ja+7W8M8PIVcS841Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-redux": { + "version": "7.1.34", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.34.tgz", + "integrity": "sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/react": "*" + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" } }, "node_modules/@types/react-router": { @@ -6103,9 +6938,9 @@ "license": "MIT" }, "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "version": "0.17.5", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", + "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", "dev": true, "license": "MIT", "dependencies": { @@ -6124,9 +6959,9 @@ } }, "node_modules/@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", + "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", "dev": true, "license": "MIT", "dependencies": { @@ -6446,9 +7281,9 @@ } }, "node_modules/@uiw/codemirror-extensions-basic-setup": { - "version": "4.23.12", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.23.12.tgz", - "integrity": "sha512-l9vuiXOTFDBetYrRLDmz3jDxQHDsrVAZ2Y6dVfmrqi2AsulsDu+y7csW0JsvaMqo79rYkaIZg8yeqmDgMb7VyQ==", + "version": "4.23.13", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.23.13.tgz", + "integrity": "sha512-U1CnDFpq6ydNqrRDS5Bdnvgso8ezwwbrmKvmAD3hmoVyRDsDU6HTtmcV+w0rZ3kElUCkKI5lY0DMvTTQ4+L3RQ==", "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", @@ -6473,16 +7308,16 @@ } }, "node_modules/@uiw/react-codemirror": { - "version": "4.23.12", - "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.23.12.tgz", - "integrity": "sha512-yseqWdzoAAGAW7i/NiU8YrfSLVOEBjQvSx1KpDTFVV/nn0AlAZoDVTIPEBgdXrPlVUQoCrwgpEaj3uZCklk9QA==", + "version": "4.23.13", + "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.23.13.tgz", + "integrity": "sha512-y65ULzxOAfpxrA/8epoAOeCfmJXu9z0P62BbGOkITJTtU7WI59KfPbbwj35npSsMAkAmDE841qZo2I8jst/THg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.6", "@codemirror/commands": "^6.1.0", "@codemirror/state": "^6.1.1", "@codemirror/theme-one-dark": "^6.0.0", - "@uiw/codemirror-extensions-basic-setup": "4.23.12", + "@uiw/codemirror-extensions-basic-setup": "4.23.13", "codemirror": "^6.0.0" }, "funding": { @@ -6725,9 +7560,9 @@ } }, "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -7071,18 +7906,20 @@ "license": "MIT" }, "node_modules/array-includes": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "is-string": "^1.0.7" + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -7417,14 +8254,15 @@ } }, "node_modules/axios/node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -8025,9 +8863,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -8134,9 +8972,9 @@ "peer": true }, "node_modules/browserslist": { - "version": "4.24.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz", - "integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", + "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", "funding": [ { "type": "opencollective", @@ -8153,8 +8991,8 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001716", - "electron-to-chromium": "^1.5.149", + "caniuse-lite": "^1.0.30001718", + "electron-to-chromium": "^1.5.160", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, @@ -8367,9 +9205,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001718", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz", - "integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==", + "version": "1.0.30001722", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001722.tgz", + "integrity": "sha512-DCQHBBZtiK6JVkAGw7drvAMK0Q0POD/xZvEmDp6baiMMP6QXXk9HpD6mNYBZWhOPG6LvIDb82ITqtWjhDckHCA==", "funding": [ { "type": "opencollective", @@ -8517,6 +9355,27 @@ "node": ">= 0.8.0" } }, + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, "node_modules/cheerio-select": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", @@ -8731,12 +9590,6 @@ "node": ">=0.10.0" } }, - "node_modules/clone-stats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==", - "license": "MIT" - }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -9126,9 +9979,9 @@ } }, "node_modules/copy-webpack-plugin/node_modules/ignore": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz", - "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "license": "MIT", "engines": { "node": ">= 4" @@ -9159,14 +10012,14 @@ } }, "node_modules/core-js-compat": { - "version": "3.42.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.42.0.tgz", - "integrity": "sha512-bQasjMfyDGyaeWKBIu33lHh9qlSR0MFE/Nmc6nMjf/iU9b3rSMdAYz1Baxrv4lPdGUsTqZudHA4jIGSJy0SWZQ==", + "version": "3.43.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.43.0.tgz", + "integrity": "sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "browserslist": "^4.24.4" + "browserslist": "^4.25.0" }, "funding": { "type": "opencollective", @@ -9613,6 +10466,48 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-array": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", @@ -9625,6 +10520,46 @@ "node": ">=12" } }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-color": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", @@ -9634,6 +10569,92 @@ "node": ">=12" } }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "dev": true, + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, "node_modules/d3-ease": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", @@ -9643,6 +10664,34 @@ "node": ">=12" } }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-format": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", @@ -9652,6 +10701,29 @@ "node": ">=12" } }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/d3-interpolate": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", @@ -9673,6 +10745,36 @@ "node": ">=12" } }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/d3-scale": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", @@ -9689,6 +10791,30 @@ "node": ">=12" } }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/d3-shape": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", @@ -9734,6 +10860,43 @@ "node": ">=12" } }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -9853,9 +11016,9 @@ "license": "MIT" }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -10051,10 +11214,14 @@ } }, "node_modules/delaunator": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-4.0.1.tgz", - "integrity": "sha512-WNPWi1IRKZfCt/qIDMfERkDp93+iZEmOxN2yy4Jg+Xhv8SLk2UTqqbe1sfiipn0and9QrE914/ihdx82Y/Giag==", - "license": "ISC" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "dev": true, + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } }, "node_modules/delaunay-find": { "version": "0.0.6", @@ -10065,6 +11232,12 @@ "delaunator": "^4.0.0" } }, + "node_modules/delaunay-find/node_modules/delaunator": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-4.0.1.tgz", + "integrity": "sha512-WNPWi1IRKZfCt/qIDMfERkDp93+iZEmOxN2yy4Jg+Xhv8SLk2UTqqbe1sfiipn0and9QrE914/ihdx82Y/Giag==", + "license": "ISC" + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -10118,11 +11291,12 @@ "license": "MIT" }, "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "engines": { "node": ">=0.3.1" } @@ -10317,9 +11491,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.152", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.152.tgz", - "integrity": "sha512-xBOfg/EBaIlVsHipHl2VdTPJRSvErNUaqW8ejTq5OlOlIYx1wOllCHsAvAIrr55jD1IYEfdR86miUEt8H5IeJg==", + "version": "1.5.166", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.166.tgz", + "integrity": "sha512-QPWqHL0BglzPYyJJ1zSSmwFFL6MFXhbACOCcsCdUMCkzPdS9/OIBVxg516X/Ado2qwAq8k0nJJ7phQPCqiaFAw==", "license": "ISC" }, "node_modules/emittery": { @@ -10437,9 +11611,9 @@ } }, "node_modules/es-abstract": { - "version": "1.23.9", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", - "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", "dev": true, "license": "MIT", "dependencies": { @@ -10447,18 +11621,18 @@ "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", - "call-bound": "^1.0.3", + "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.0", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", @@ -10470,21 +11644,24 @@ "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", + "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.0", + "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.3", + "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.3", + "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", @@ -10493,7 +11670,7 @@ "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.18" + "which-typed-array": "^1.1.19" }, "engines": { "node": ">= 0.4" @@ -12239,6 +13416,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-node-dimensions": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-node-dimensions/-/get-node-dimensions-1.2.1.tgz", + "integrity": "sha512-2MSPMu7S1iOTL+BOa6K1S62hB2zUAYNF/lV0gSVlOaacd087lc6nR1H1r0e3B1CerTo+RceOmi1iJW+vp21xcQ==", + "dev": true, + "license": "MIT" + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -12408,9 +13592,9 @@ } }, "node_modules/glob-stream": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-8.0.2.tgz", - "integrity": "sha512-R8z6eTB55t3QeZMmU1C+Gv+t5UnNRkA55c5yo67fAVfxODxieTwsjNG7utxS/73NdP1NbDgCrhVEg2h00y4fFw==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-8.0.3.tgz", + "integrity": "sha512-fqZVj22LtFJkHODT+M4N1RJQ3TjnnQhfE9GwZI8qXscYarnhpip70poMldRnP8ipQ/w0B621kOhfc53/J9bd/A==", "license": "MIT", "dependencies": { "@gulpjs/to-absolute-glob": "^4.0.0", @@ -12951,6 +14135,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, "node_modules/http-assert": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz", @@ -13173,27 +14376,6 @@ "yarn": ">=1" } }, - "node_modules/i18next-parser/node_modules/cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", - "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", - "license": "MIT", - "dependencies": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", - "parse5": "^7.0.0", - "parse5-htmlparser2-tree-adapter": "^7.0.0" - }, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" - } - }, "node_modules/i18next-parser/node_modules/commander": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", @@ -13217,25 +14399,6 @@ "node": ">=14.14" } }, - "node_modules/i18next-parser/node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" - } - }, "node_modules/i18next-parser/node_modules/typescript": { "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", @@ -13899,6 +15062,19 @@ "node": ">=0.10.0" } }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-npm": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", @@ -14296,15 +15472,6 @@ "node": ">=0.10.0" } }, - "node_modules/isomorphic-rslog": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/isomorphic-rslog/-/isomorphic-rslog-0.0.7.tgz", - "integrity": "sha512-n6/XnKnZ5eLEj6VllG4XmamXG7/F69nls8dcynHyhcTpsPUYgcgx4ifEaCo4lQJ2uzwfmIT+F0KBGwBcMKmt5g==", - "license": "MIT", - "engines": { - "node": ">=14.17.6" - } - }, "node_modules/isomorphic-ws": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", @@ -16528,9 +17695,9 @@ "license": "MIT" }, "node_modules/koa": { - "version": "2.15.3", - "resolved": "https://registry.npmjs.org/koa/-/koa-2.15.3.tgz", - "integrity": "sha512-j/8tY9j5t+GVMLeioLaxweJiKUayFhlGqNTzf2ZGwL0ZCQijd2RLHK0SLW5Tsko8YyyqCZC2cojIb0/s62qTAg==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/koa/-/koa-2.16.1.tgz", + "integrity": "sha512-umfX9d3iuSxTQP4pnzLOz0HKnPg0FaUUIKcye2lOiz3KPu1Y3M3xlz76dISdFPQs37P9eJz1wUpcTS6KDPn9fA==", "license": "MIT", "dependencies": { "accepts": "^1.3.5", @@ -17574,10 +18741,70 @@ "node": ">0.9" } }, + "node_modules/mobx": { + "version": "6.13.7", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.13.7.tgz", + "integrity": "sha512-aChaVU/DO5aRPmk1GX8L+whocagUUpBQqoPtJk+cm7UOXUk87J4PeWCh6nNmTTIfEhiR9DI/+FnA8dln/hTK7g==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + } + }, + "node_modules/mobx-react": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-7.6.0.tgz", + "integrity": "sha512-+HQUNuh7AoQ9ZnU6c4rvbiVVl+wEkb9WqYsVDzGLng+Dqj1XntHu79PvEWKtSMoMj67vFp/ZPXcElosuJO8ckA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mobx-react-lite": "^3.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + }, + "peerDependencies": { + "mobx": "^6.1.0", + "react": "^16.8.0 || ^17 || ^18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/mobx-react-lite": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-3.4.3.tgz", + "integrity": "sha512-NkJREyFTSUXR772Qaai51BnE1voWx56LOL80xG7qkZr6vo8vEaLF3sz1JNUVh+rxmUzxYaqOhfuxTfqUh0FXUg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + }, + "peerDependencies": { + "mobx": "^6.1.0", + "react": "^16.8.0 || ^17 || ^18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/mocha": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.2.2.tgz", - "integrity": "sha512-VlSBxrPYHK4YNOEbFdkCxHQbZMoNzBkoPprqtZRW6311EUF/DlSxoycE2e/2NtRk4WKkIXzyrXDTrlikJMWgbw==", + "version": "11.6.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.6.0.tgz", + "integrity": "sha512-i0JVb+OUBqw63X/1pC3jCyJsqYisgxySBbsQa8TKvefpA1oEnw7JXxXnftfMHRsw7bEEVGRtVlHcDYXBa7FzVw==", "dev": true, "license": "MIT", "peer": true, @@ -17585,20 +18812,20 @@ "browser-stdout": "^1.3.1", "chokidar": "^4.0.1", "debug": "^4.3.5", - "diff": "^5.2.0", + "diff": "^7.0.0", "escape-string-regexp": "^4.0.0", "find-up": "^5.0.0", "glob": "^10.4.5", "he": "^1.2.0", "js-yaml": "^4.1.0", "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", + "minimatch": "^9.0.5", "ms": "^2.1.3", "picocolors": "^1.1.1", "serialize-javascript": "^6.0.2", "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", - "workerpool": "^6.5.1", + "workerpool": "^9.2.0", "yargs": "^17.7.2", "yargs-parser": "^21.1.1", "yargs-unparser": "^2.0.0" @@ -17629,9 +18856,9 @@ } }, "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "peer": true, @@ -17679,23 +18906,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mocha/node_modules/glob/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/mocha/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -17725,9 +18935,9 @@ } }, "node_modules/mocha/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", "peer": true, @@ -17735,7 +18945,10 @@ "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/mocha/node_modules/p-locate": { @@ -18122,6 +19335,16 @@ "dev": true, "license": "MIT" }, + "node_modules/mochawesome/node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/mochawesome/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -18913,9 +20136,9 @@ } }, "node_modules/parse5/node_modules/entities": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", - "integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -19221,6 +20444,28 @@ "node": ">=4" } }, + "node_modules/point-in-svg-path": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/point-in-svg-path/-/point-in-svg-path-1.0.2.tgz", + "integrity": "sha512-+Smsf7B9e7eRFHIwpN+4rE8inF2APbFWeywPfUgbeh02xdJSkbTz6Pqdt7A36wVCR+CnLbaNkRnBjgOpF5RMVQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8.x.x" + } + }, + "node_modules/popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", + "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -19232,9 +20477,9 @@ } }, "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "version": "8.5.5", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.5.tgz", + "integrity": "sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==", "dev": true, "funding": [ { @@ -19252,7 +20497,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.8", + "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -20904,6 +22149,7 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "license": "BSD-3-Clause", + "peer": true, "dependencies": { "side-channel": "^1.1.0" }, @@ -21230,9 +22476,9 @@ } }, "node_modules/react-hook-form": { - "version": "7.56.3", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.56.3.tgz", - "integrity": "sha512-IK18V6GVbab4TAo1/cz3kqajxbDPGofdF0w7VHdCo0Nt8PrPlOZcuuDq9YYIV1BtjcX78x0XsldbQRQnQXWXmw==", + "version": "7.57.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.57.0.tgz", + "integrity": "sha512-RbEks3+cbvTP84l/VXGUZ+JMrKOS8ykQCRYdm5aYsxnDquL0vspsyNhGRO7pcH6hsZqWlPOjLye7rJqdtdAmlg==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -21289,22 +22535,39 @@ "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==", "license": "MIT" }, + "node_modules/react-measure": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/react-measure/-/react-measure-2.5.2.tgz", + "integrity": "sha512-M+rpbTLWJ3FD6FXvYV6YEGvQ5tMayQ3fGrZhRPHrE9bVlBYfDCLuDcgNttYfk8IqfOI03jz6cbpqMRTUclQnaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.2.0", + "get-node-dimensions": "^1.2.1", + "prop-types": "^15.6.2", + "resize-observer-polyfill": "^1.5.0" + }, + "peerDependencies": { + "react": ">0.13.0", + "react-dom": ">0.13.0" + } + }, "node_modules/react-redux": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.2.tgz", - "integrity": "sha512-8+CQ1EvIVFkYL/vu6Olo7JFLWop1qRUeb46sGtIMDCSpgwPQq8fPLpirIB0iTqFe9XYEFPHssdX8/UwN6pAkEA==", + "version": "7.2.9", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", + "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.12.1", + "@babel/runtime": "^7.15.4", + "@types/react-redux": "^7.1.20", "hoist-non-react-statics": "^3.3.2", "loose-envify": "^1.4.0", "prop-types": "^15.7.2", - "react-is": "^16.13.1" + "react-is": "^17.0.2" }, "peerDependencies": { - "react": "^16.8.3 || ^17", - "redux": "^2.0.0 || ^3.0.0 || ^4.0.0-0" + "react": "^16.8.3 || ^17 || ^18" }, "peerDependenciesMeta": { "react-dom": { @@ -21316,9 +22579,9 @@ } }, "node_modules/react-redux/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, "license": "MIT" }, @@ -21452,9 +22715,9 @@ } }, "node_modules/react-virtuoso": { - "version": "4.12.7", - "resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.12.7.tgz", - "integrity": "sha512-njJp764he6Fi1p89PUW0k2kbyWu9w/y+MwdxmwK2kvdwwzVDbz2c2wMj5xdSruBFVgFTsI7Z85hxZR7aSHBrbQ==", + "version": "4.12.8", + "resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.12.8.tgz", + "integrity": "sha512-NMMKfDBr/+xZZqCQF3tN1SZsh6FwOJkYgThlfnsPLkaEhdyQo0EuWUzu3ix6qjnI7rYwJhMwRGoJBi+aiDfGsA==", "license": "MIT", "peerDependencies": { "react": ">=16 || >=17 || >= 18 || >= 19", @@ -22180,6 +23443,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/rslog": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/rslog/-/rslog-1.2.3.tgz", + "integrity": "sha512-antALPJaKBRPBU1X2q9t085K4htWDOOv/K1qhTUk7h0l1ePU/KbDqKJn19eKP0dk7PqMioeA0+fu3gyPXCsXxQ==", + "engines": { + "node": ">=14.17.6" + } + }, "node_modules/rsvp": { "version": "4.8.5", "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", @@ -22212,6 +23490,13 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", @@ -22707,9 +23992,9 @@ } }, "node_modules/shell-quote": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", - "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", "dev": true, "license": "MIT", "engines": { @@ -23111,6 +24396,20 @@ "node": ">= 0.8" } }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/stream-composer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-composer/-/stream-composer-1.0.2.tgz", @@ -23167,9 +24466,9 @@ } }, "node_modules/streamx": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz", - "integrity": "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==", + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.1.tgz", + "integrity": "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==", "license": "MIT", "dependencies": { "fast-fifo": "^1.3.2", @@ -24228,9 +25527,9 @@ } }, "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", "license": "MIT", "engines": { "node": ">=6" @@ -24548,13 +25847,13 @@ } }, "node_modules/terser": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", - "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", + "version": "5.42.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.42.0.tgz", + "integrity": "sha512-UYCvU9YQW2f/Vwl+P0GfhxJxbUGLwd+5QrrGgLajzWAtC/23AX0vcise32kkP7Eu0Wu9VlzzHAXkLObgjQfFlQ==", "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", + "acorn": "^8.14.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -25881,9 +27180,9 @@ } }, "node_modules/validator": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.0.tgz", - "integrity": "sha512-36B2ryl4+oL5QxZ3AzD0t5SsMNGvTtQHpjgFO5tbNxfXbMFkY822ktCDe1MnlqV3301QQI9SLHDNJokDI+Z9pA==", + "version": "13.15.15", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", + "integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==", "dev": true, "license": "MIT", "engines": { @@ -26287,13 +27586,12 @@ } }, "node_modules/vinyl": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.0.tgz", - "integrity": "sha512-rC2VRfAVVCGEgjnxHUnpIVh3AGuk62rP3tqVrn+yab0YH7UULisC085+NYH+mnqf3Wx4SpSi1RQMwudL89N03g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.1.tgz", + "integrity": "sha512-0QwqXteBNXgnLCdWdvPQBX6FXRHtIH3VhJPTd5Lwn28tJXc34YqSCWUmkOvtJHBmB3gGoPtrOKk3Ts8/kEZ9aA==", "license": "MIT", "dependencies": { "clone": "^2.1.2", - "clone-stats": "^1.0.0", "remove-trailing-separator": "^1.1.0", "replace-ext": "^2.0.0", "teex": "^1.0.1" @@ -26316,13 +27614,13 @@ } }, "node_modules/vinyl-fs": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-4.0.0.tgz", - "integrity": "sha512-7GbgBnYfaquMk3Qu9g22x000vbYkOex32930rBnc3qByw6HfMEAoELjCjoJv4HuEQxHAurT+nvMHm6MnJllFLw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-4.0.2.tgz", + "integrity": "sha512-XRFwBLLTl8lRAOYiBqxY279wY46tVxLaRhSwo3GzKEuLz1giffsOquWWboD/haGf5lx+JyTigCFfe7DWHoARIA==", "license": "MIT", "dependencies": { "fs-mkdirp-stream": "^2.0.1", - "glob-stream": "^8.0.0", + "glob-stream": "^8.0.3", "graceful-fs": "^4.2.11", "iconv-lite": "^0.6.3", "is-valid-glob": "^1.0.0", @@ -26333,7 +27631,7 @@ "streamx": "^2.14.0", "to-through": "^3.0.0", "value-or-function": "^4.0.0", - "vinyl": "^3.0.0", + "vinyl": "^3.0.1", "vinyl-sourcemap": "^2.0.0" }, "engines": { @@ -26410,9 +27708,9 @@ } }, "node_modules/watchpack": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", - "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", @@ -26432,6 +27730,68 @@ "minimalistic-assert": "^1.0.0" } }, + "node_modules/webcola": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/webcola/-/webcola-3.4.0.tgz", + "integrity": "sha512-4BiLXjXw3SJHo3Xd+rF+7fyClT6n7I+AR6TkBqyQ4kTsePSAMDLRCXY1f3B/kXJeP9tYn4G1TblxTO+jAt0gaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "d3-dispatch": "^1.0.3", + "d3-drag": "^1.0.4", + "d3-shape": "^1.3.5", + "d3-timer": "^1.0.5" + } + }, + "node_modules/webcola/node_modules/d3-dispatch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", + "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/webcola/node_modules/d3-drag": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.5.tgz", + "integrity": "sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "d3-dispatch": "1", + "d3-selection": "1" + } + }, + "node_modules/webcola/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/webcola/node_modules/d3-selection": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.2.tgz", + "integrity": "sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/webcola/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/webcola/node_modules/d3-timer": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz", + "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -26439,9 +27799,9 @@ "license": "BSD-2-Clause" }, "node_modules/webpack": { - "version": "5.99.8", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.8.tgz", - "integrity": "sha512-lQ3CPiSTpfOnrEGeXDwoq5hIGzSjmwD72GdfVzF7CQAI7t47rJG9eDWvcEkEn3CUQymAElVvDg3YNTlCYj+qUQ==", + "version": "5.99.9", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.9.tgz", + "integrity": "sha512-brOPwM3JnmOa+7kd3NsmOUOwbDAj8FT9xDsG3IW0MgbN9yZV7Oi/s/+MNQ/EcSMqw7qfoRyXPoeEWT8zLVdVGg==", "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.7", @@ -26694,9 +28054,9 @@ } }, "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.2.tgz", + "integrity": "sha512-ykKKus8lqlgXX/1WjudpIEjqsafjOTcOJqxnAbMLAu/KCsDCJ6GBtvscewvTkrn24HsnvFwrSCbenFrhtcCsAA==", "license": "MIT", "engines": { "node": ">=10.13.0" @@ -26727,13 +28087,6 @@ "node": ">=0.8.0" } }, - "node_modules/whatwg-fetch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", - "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==", - "dev": true, - "license": "MIT" - }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -26941,9 +28294,9 @@ } }, "node_modules/workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.2.tgz", + "integrity": "sha512-Xz4Nm9c+LiBHhDR5bDLnNzmj6+5F+cyEAWPMkbs2awq/dYazR/efelZzUAjB/y3kNHL+uzkHvxVVpaOfGCPV7A==", "dev": true, "license": "Apache-2.0", "peer": true @@ -27180,15 +28533,15 @@ "peer": true }, "node_modules/yaml": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", - "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", + "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", "license": "ISC", "bin": { "yaml": "bin.mjs" }, "engines": { - "node": ">= 14" + "node": ">= 14.6" } }, "node_modules/yargs": { @@ -27339,9 +28692,9 @@ } }, "node_modules/zod": { - "version": "3.24.4", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.4.tgz", - "integrity": "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==", + "version": "3.25.63", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.63.tgz", + "integrity": "sha512-3ttCkqhtpncYXfP0f6dsyabbYV/nEUW+Xlu89jiXbTBifUfjaSqXOG6JnQPLtqt87n7KAmnMqcjay6c0Wq0Vbw==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -27363,9 +28716,9 @@ "license": "0BSD" }, "node_modules/zustand": { - "version": "4.5.6", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.6.tgz", - "integrity": "sha512-ibr/n1hBzLLj5Y+yUcU7dYw8p6WnIVzdJbnX+1YpaScvZVF2ziugqHs+LAmHw4lWO9c/zRj+K1ncgWDQuthEdQ==", + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", "license": "MIT", "dependencies": { "use-sync-external-store": "^1.2.2" diff --git a/web/package.json b/web/package.json index dd06089..b7666c7 100644 --- a/web/package.json +++ b/web/package.json @@ -83,14 +83,13 @@ "@patternfly/react-core": "^6.2.0", "@patternfly/react-icons": "^5.4.2", "@patternfly/react-templates": "^6.2.2", - "@perses-dev/core": "0.51.0-rc.0", - "@perses-dev/dashboards": "0.51.0-rc.0", - "@perses-dev/plugin-system": "0.51.0-rc.0", - "@perses-dev/scatter-chart-plugin": "^0.6.0", - "@perses-dev/tempo-plugin": "^0.51.0-beta.2", - "@perses-dev/trace-table-plugin": "^0.6.0", - "@perses-dev/tracing-gantt-chart-plugin": "^0.6.0", - "@tanstack/react-query": "4.36.1", + "@perses-dev/core": "^0.51.0", + "@perses-dev/dashboards": "^0.51.0", + "@perses-dev/plugin-system": "^0.51.0", + "@perses-dev/scatter-chart-plugin": "^0.7.0", + "@perses-dev/tempo-plugin": "^0.51.0-rc.3", + "@perses-dev/trace-table-plugin": "^0.7.0", + "@perses-dev/tracing-gantt-chart-plugin": "^0.7.0", "copy-webpack-plugin": "^12.0.2", "i18next": "^23.10.0", "i18next-http-backend": "^2.5.0", @@ -99,14 +98,11 @@ "pluralize": "^8.0.0", "use-query-params": "^2.2.1" }, + "peerDependencies": { + "react-router-dom": "<7" + }, "overrides": { "@mui/x-data-grid": "7.20.0", - "@perses-dev/components": "0.51.0-rc.0", - "@perses-dev/core": "0.51.0-rc.0", - "@perses-dev/dashboards": "0.51.0-rc.0", - "@perses-dev/plugin-system": "0.51.0-rc.0", - "@tanstack/react-query": "4.36.1", - "cheerio":"1.0.0-rc.12", - "react-router-dom": "5.3.4" + "cheerio": "1.0.0-rc.12" } } diff --git a/web/src/hooks/useTagValues.ts b/web/src/hooks/useTagValues.ts index 5303ad8..a7b264f 100644 --- a/web/src/hooks/useTagValues.ts +++ b/web/src/hooks/useTagValues.ts @@ -20,8 +20,8 @@ export function useTagValues( const values = await client.searchTagValues({ tag, q: query, start, end }); return values.tagValues .map((tagValue) => ({ - content: tagValue.value, - value: tagValue.value, + content: tagValue.value ?? '', + value: tagValue.value ?? '', })) .sort((a, b) => a.value.localeCompare(b.value)); }, diff --git a/web/src/pages/TracesPage/Toolbar/TraceQLEditor.tsx b/web/src/pages/TracesPage/Toolbar/TraceQLEditor.tsx index 25a428c..699f906 100644 --- a/web/src/pages/TracesPage/Toolbar/TraceQLEditor.tsx +++ b/web/src/pages/TracesPage/Toolbar/TraceQLEditor.tsx @@ -1,7 +1,7 @@ import React, { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import CodeMirror, { EditorView, keymap } from '@uiw/react-codemirror'; -import { TraceQLExtension } from '@perses-dev/tempo-plugin/lib/components/TraceQLExtension'; +import { TraceQLExtension, TempoDatasource } from '@perses-dev/tempo-plugin'; import { TempoInstance } from '../../../hooks/useTempoInstance'; import { getProxyURLFor } from '../../../hooks/api'; import { usePatternFlyTheme } from '../../../components/console/utils/usePatternFlyTheme'; @@ -36,7 +36,10 @@ export function TraceQLEditor({ id, tempo, query, setQuery, runQuery }: TraceQLE const { theme } = usePatternFlyTheme(); const traceQLExtension = useMemo(() => { - return TraceQLExtension({ endpoint: tempo ? getProxyURLFor(tempo) : undefined }); + const client = tempo + ? TempoDatasource.createClient({ directUrl: getProxyURLFor(tempo) }, {}) + : undefined; + return TraceQLExtension({ client }); }, [tempo]); const keyBindings = useMemo( From 7ad8b39c6d709daa34d6c3f0d46e40541416a5bc Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Fri, 13 Jun 2025 17:49:05 +0200 Subject: [PATCH 10/35] NO-JIRA: improve API handling * extract response schema into common.go/api.ts * abort query if timeout is reached * use @tanstack/query instead of custom code for fetching Tempo CRs * cache list-tempo-resources API response * show error message if plugin backend is not reachable Signed-off-by: Andreas Gerstmayr --- pkg/api/common.go | 38 +++++++++ pkg/api/tempo.go | 40 +--------- ...n__distributed-tracing-console-plugin.json | 2 + web/src/cancellable-fetch.ts | 5 +- web/src/components/TempoInstanceDropdown.tsx | 6 +- web/src/hooks/api.ts | 7 ++ web/src/hooks/useTempoResources.ts | 80 ++++++------------- web/src/pages/TracesPage/TracesPage.tsx | 22 +++-- 8 files changed, 98 insertions(+), 102 deletions(-) create mode 100644 pkg/api/common.go diff --git a/pkg/api/common.go b/pkg/api/common.go new file mode 100644 index 0000000..d72e296 --- /dev/null +++ b/pkg/api/common.go @@ -0,0 +1,38 @@ +package api + +import ( + "encoding/json" + "fmt" + "net/http" +) + +type Response struct { + Status StatusType `json:"status"` + ErrorType string `json:"errorType,omitempty"` + Error string `json:"error,omitempty"` + Data any `json:"data,omitempty"` +} + +type StatusType string + +const ( + StatusSuccess StatusType = "success" + StatusError StatusType = "error" +) + +func writeResponse(w http.ResponseWriter, code int, r Response) { + if r.Status != StatusSuccess { + log.Error(fmt.Sprintf("type=%s, error=%s", r.ErrorType, r.Error)) + } + + bytes, err := json.Marshal(r) + if err != nil { + log.Error(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(code) + w.Write(bytes) +} diff --git a/pkg/api/tempo.go b/pkg/api/tempo.go index de224a0..2c53ae1 100644 --- a/pkg/api/tempo.go +++ b/pkg/api/tempo.go @@ -2,7 +2,6 @@ package api import ( "context" - "encoding/json" "fmt" "net/http" @@ -14,7 +13,7 @@ import ( "k8s.io/client-go/dynamic" ) -var log = logrus.WithField("module", "api.tempo") +var log = logrus.WithField("module", "api") type TempoResource struct { Kind KindType `json:"kind"` @@ -31,20 +30,6 @@ const ( KindTempoMonolithic KindType = "TempoMonolithic" ) -type ListTempoResourcesResponse struct { - Status StatusType `json:"status"` - Data []TempoResource `json:"data"` - ErrorType string `json:"errorType,omitempty"` - Error string `json:"error,omitempty"` -} - -type StatusType string - -const ( - StatusSuccess StatusType = "success" - StatusError StatusType = "error" -) - var ( tempostackGVR = schema.GroupVersionResource{ Group: "tempo.grafana.com", @@ -58,29 +43,12 @@ var ( } ) -func writeResponse(w http.ResponseWriter, code int, r ListTempoResourcesResponse) { - if r.Status == StatusError { - log.Error(fmt.Sprintf("type=%s, error=%s", r.ErrorType, r.Error)) - } - - bytes, err := json.Marshal(r) - if err != nil { - log.Error(err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(code) - w.Write(bytes) -} - func ListTempoResourcesHandler(k8sclient *dynamic.DynamicClient) http.HandlerFunc { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { resources, err := ListTempoResources(r.Context(), k8sclient) if err != nil { if apierrors.IsNotFound(err) { - writeResponse(w, http.StatusNotFound, ListTempoResourcesResponse{ + writeResponse(w, http.StatusNotFound, Response{ Status: StatusError, ErrorType: "TempoCRDNotFound", Error: err.Error(), @@ -88,14 +56,14 @@ func ListTempoResourcesHandler(k8sclient *dynamic.DynamicClient) http.HandlerFun return } - writeResponse(w, http.StatusInternalServerError, ListTempoResourcesResponse{ + writeResponse(w, http.StatusInternalServerError, Response{ Status: StatusError, Error: err.Error(), }) return } - writeResponse(w, http.StatusOK, ListTempoResourcesResponse{ + writeResponse(w, http.StatusOK, Response{ Status: StatusSuccess, Data: resources, }) diff --git a/web/locales/en/plugin__distributed-tracing-console-plugin.json b/web/locales/en/plugin__distributed-tracing-console-plugin.json index e9584cc..4153f67 100644 --- a/web/locales/en/plugin__distributed-tracing-console-plugin.json +++ b/web/locales/en/plugin__distributed-tracing-console-plugin.json @@ -43,6 +43,7 @@ "TraceQL Query": "TraceQL Query", "Query": "Query", "Run query": "Run query", + "Error connecting to the Tracing UI plugin backend": "Error connecting to the Tracing UI plugin backend", "Tempo operator isn't installed yet": "Tempo operator isn't installed yet", "To get started, install the Tempo operator and create a TempoStack or TempoMonolithic instance with multi-tenancy enabled.": "To get started, install the Tempo operator and create a TempoStack or TempoMonolithic instance with multi-tenancy enabled.", "Install Tempo operator": "Install Tempo operator", @@ -53,6 +54,7 @@ "Create a TempoMonolithic instance": "Create a TempoMonolithic instance", "View documentation": "View documentation", "Error": "Error", + "Unknown error": "Unknown error", "No results found": "No results found", "No results match this query criteria. Clear all filters and try again.": "No results match this query criteria. Clear all filters and try again.", "Clear all filters": "Clear all filters" diff --git a/web/src/cancellable-fetch.ts b/web/src/cancellable-fetch.ts index 23ac849..838a620 100644 --- a/web/src/cancellable-fetch.ts +++ b/web/src/cancellable-fetch.ts @@ -66,7 +66,10 @@ export const cancellableFetch = ( } const timeoutPromise = new Promise((_resolve, reject) => { - setTimeout(() => reject(new TimeoutError(url.toString(), timeout)), timeout); + setTimeout(() => { + abort(); + reject(new TimeoutError(url.toString(), timeout)); + }, timeout); }); const request = () => Promise.race([fetchPromise, timeoutPromise]); diff --git a/web/src/components/TempoInstanceDropdown.tsx b/web/src/components/TempoInstanceDropdown.tsx index 26abb72..875ac66 100644 --- a/web/src/components/TempoInstanceDropdown.tsx +++ b/web/src/components/TempoInstanceDropdown.tsx @@ -20,7 +20,7 @@ interface TempoResourceOption extends TypeaheadSelectOption { export const TempoInstanceDropdown = ({ tempo, setTempo }: TempoInstanceDropdownProps) => { const { t } = useTranslation('plugin__distributed-tracing-console-plugin'); - const { loading: tempoResourcesLoading, tempoResources } = useTempoResources(); + const { isLoading, data: tempoResources } = useTempoResources(); const options: TempoResourceOption[] = (tempoResources ?? []) .map((tempo) => ({ @@ -33,7 +33,7 @@ export const TempoInstanceDropdown = ({ tempo, setTempo }: TempoInstanceDropdown let selected: TempoResourceOption | undefined = undefined; if (tempo) { - if (tempoResourcesLoading) { + if (isLoading) { // Preselect the dropdown option without waiting until the list of TempoResources is loaded to prevent flickering. // To accomplish this, we'll create a slightly inaccurate TempoResourceSelectOption, // because the kind and the list of tenants is not known before the list of TempoResources is loaded. @@ -99,7 +99,7 @@ export const TempoInstanceDropdown = ({ tempo, setTempo }: TempoInstanceDropdown toggleWidth="22em" placeholder={t('Select a Tempo instance')} allowClear={false} - loading={tempoResourcesLoading} + loading={isLoading} options={options} value={selected?.value} setValue={onSelect} diff --git a/web/src/hooks/api.ts b/web/src/hooks/api.ts index 01b6bbb..bb6fcdc 100644 --- a/web/src/hooks/api.ts +++ b/web/src/hooks/api.ts @@ -2,6 +2,13 @@ import { TempoInstance } from './useTempoInstance'; export const BACKEND_URL = '/api/proxy/plugin/distributed-tracing-console-plugin/backend'; +export interface APIResponse { + status: 'success' | 'error'; + errorType?: string; + error?: string; + data?: T; +} + export function getProxyURLFor(tempo: TempoInstance) { return `${BACKEND_URL}/proxy/${encodeURIComponent(tempo.namespace)}/${encodeURIComponent( tempo.name, diff --git a/web/src/hooks/useTempoResources.ts b/web/src/hooks/useTempoResources.ts index 65c1846..aa80330 100644 --- a/web/src/hooks/useTempoResources.ts +++ b/web/src/hooks/useTempoResources.ts @@ -1,6 +1,6 @@ -import * as React from 'react'; import { cancellableFetch, FetchError } from '../cancellable-fetch'; -import { BACKEND_URL } from './api'; +import { APIResponse, BACKEND_URL } from './api'; +import { useQuery } from '@tanstack/react-query'; /** * Definition of a TempoStack or TempoMonolithic Custom Resource in the cluster. @@ -14,12 +14,7 @@ export type TempoResource = { tenants?: string[]; }; -interface ListTempoResourcesResponse { - status: 'success' | 'error'; - data: TempoResource[]; - errorType?: string; - error?: string; -} +type ListTempoResourcesResponse = APIResponse; const isTempoStackListResponse = (value: unknown): value is TempoResource => { const obj = value as TempoResource; @@ -31,51 +26,26 @@ const isTempoStackListResponse = (value: unknown): value is TempoResource => { ); }; -export const useTempoResources = () => { - const [tempoResources, setTempoResources] = React.useState>(); - const [error, setError] = React.useState<{ errorType?: string; error: string } | undefined>(); - const [loading, setLoading] = React.useState(false); - - React.useEffect(() => { - const fetchData = async () => { - try { - setLoading(true); - - const { request } = cancellableFetch( - `${BACKEND_URL}/api/v1/list-tempo-resources`, - ); - const response = await request(); - - if ( - response && - response.status === 'success' && - Array.isArray(response.data) && - response.data.every(isTempoStackListResponse) - ) { - setTempoResources(response.data); - setError(undefined); - } else { - throw new FetchError('Error retrieving Tempo resources', 500, response); - } - } catch (e) { - setTempoResources(undefined); - if (e instanceof FetchError && e.json?.error) { - setError(e.json); - } else { - setError({ error: String(e) }); - } - console.error(e); - } finally { - setLoading(false); +export function useTempoResources() { + return useQuery({ + queryKey: ['useTempoResources'], + queryFn: async function () { + const { request } = cancellableFetch( + `${BACKEND_URL}/api/v1/list-tempo-resources`, + ); + const response = await request(); + + if ( + response && + response.status === 'success' && + Array.isArray(response.data) && + response.data.every(isTempoStackListResponse) + ) { + return response.data; + } else { + throw new FetchError('Error retrieving Tempo resources', 500, response); } - }; - - fetchData(); - }, []); - - return { - loading, - error, - tempoResources, - }; -}; + }, + staleTime: 60 * 1000, // cache response for 1m + }); +} diff --git a/web/src/pages/TracesPage/TracesPage.tsx b/web/src/pages/TracesPage/TracesPage.tsx index 769fdeb..0e480a2 100644 --- a/web/src/pages/TracesPage/TracesPage.tsx +++ b/web/src/pages/TracesPage/TracesPage.tsx @@ -52,24 +52,30 @@ export default memo(TracesPage); * and shows an empty state instead of the query browser. */ function TracesPageBody() { - const { loading, error, tempoResources } = useTempoResources(); + const { t } = useTranslation('plugin__distributed-tracing-console-plugin'); + const { isLoading, error, data: tempoResources } = useTempoResources(); const [tempo] = useTempoInstance(); // show loading state (loading the list of Tempo CRs in the cluster) // only if no Tempo instance is selected (from the query params) - if (!tempo && loading) { + if (!tempo && isLoading) { return ; } if (error) { - if (error.errorType === 'TempoCRDNotFound') { + if (error.json?.errorType === 'TempoCRDNotFound') { return ; } else { - return ; + return ( + + ); } } - if (!loading && tempoResources && tempoResources.length === 0) { + if (!isLoading && tempoResources && tempoResources.length === 0) { return ; } @@ -169,7 +175,7 @@ function NoTempoInstance() { interface ErrorStateProps { errorType?: string; - error: string; + error?: string; } function ErrorState({ errorType, error }: ErrorStateProps) { @@ -180,7 +186,9 @@ function ErrorState({ errorType, error }: ErrorStateProps) { {t('Traces')} - + ); From 4ff02dd9d8c72a66c5e4c7251c65b845a8a4a8b4 Mon Sep 17 00:00:00 2001 From: Ishwar Kanse Date: Wed, 2 Jul 2025 10:56:36 +0530 Subject: [PATCH 11/35] Update Tempo fixtures after RHOSDT 3.6 release --- .../01-assert.yaml | 50 +++++++++++++++++-- .../01-install-tempo.yaml | 4 +- .../02-install-otelcol.yaml | 4 +- .../04-verify-traces.yaml | 8 +-- .../multitenancy/01-assert.yaml | 10 ++-- .../multitenancy/01-install-tempo.yaml | 13 +++++ .../multitenancy/chainsaw-test.yaml | 13 ----- 7 files changed, 74 insertions(+), 28 deletions(-) diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/01-assert.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/01-assert.yaml index 50268b7..5e8f07d 100644 --- a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/01-assert.yaml +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/01-assert.yaml @@ -1,7 +1,7 @@ apiVersion: apps/v1 kind: StatefulSet metadata: - name: tempo-monolithic-multitenancy-openshift + name: tempo-mmo status: readyReplicas: 1 @@ -9,7 +9,7 @@ status: apiVersion: v1 kind: Pod metadata: - name: tempo-monolithic-multitenancy-openshift-0 + name: tempo-mmo-0 status: containerStatuses: - name: jaeger-query @@ -33,7 +33,7 @@ status: apiVersion: v1 kind: Service metadata: - name: tempo-monolithic-multitenancy-openshift-gateway + name: tempo-mmo-gateway spec: ports: - name: public @@ -48,3 +48,47 @@ spec: port: 4317 protocol: TCP targetPort: grpc-public + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/component: gateway + app.kubernetes.io/instance: mmo + app.kubernetes.io/managed-by: tempo-operator + app.kubernetes.io/name: tempo-monolithic + app.kubernetes.io/namespace: chainsaw-monolithic-multitenancy + name: tempo-mmo-gateway-chainsaw-monolithic-multitenancy +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: gateway + app.kubernetes.io/instance: mmo + app.kubernetes.io/managed-by: tempo-operator + app.kubernetes.io/name: tempo-monolithic + app.kubernetes.io/namespace: chainsaw-monolithic-multitenancy + name: tempo-mmo-gateway-chainsaw-monolithic-multitenancy +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: tempo-mmo-gateway-chainsaw-monolithic-multitenancy +subjects: +- kind: ServiceAccount + name: tempo-mmo + namespace: chainsaw-monolithic-multitenancy diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/01-install-tempo.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/01-install-tempo.yaml index dc62d4f..4e1f0be 100644 --- a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/01-install-tempo.yaml +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/01-install-tempo.yaml @@ -1,10 +1,12 @@ apiVersion: tempo.grafana.com/v1alpha1 kind: TempoMonolithic metadata: - name: monolithic-multitenancy-openshift + name: mmo spec: jaegerui: enabled: true + route: + enabled: true multitenancy: enabled: true mode: openshift diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/02-install-otelcol.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/02-install-otelcol.yaml index 0c04001..6442bfc 100644 --- a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/02-install-otelcol.yaml +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/02-install-otelcol.yaml @@ -18,7 +18,7 @@ spec: exporters: otlp: - endpoint: tempo-monolithic-multitenancy-openshift-gateway.chainsaw-monolithic-multitenancy.svc.cluster.local:4317 + endpoint: tempo-mmo-gateway.chainsaw-monolithic-multitenancy.svc.cluster.local:4317 tls: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt auth: @@ -26,7 +26,7 @@ spec: headers: X-Scope-OrgID: dev # tenantName otlphttp: - endpoint: https://tempo-monolithic-multitenancy-openshift-gateway.chainsaw-monolithic-multitenancy.svc.cluster.local:8080/api/traces/v1/dev + endpoint: https://tempo-mmo-gateway.chainsaw-monolithic-multitenancy.svc.cluster.local:8080/api/traces/v1/dev tls: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt auth: diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/04-verify-traces.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/04-verify-traces.yaml index bed40c3..40e0b4d 100644 --- a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/04-verify-traces.yaml +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/04-verify-traces.yaml @@ -14,7 +14,7 @@ spec: curl -vG \ --header "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \ --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ - https://tempo-monolithic-multitenancy-openshift-gateway.chainsaw-monolithic-multitenancy.svc:8080/api/traces/v1/dev/api/traces \ + https://tempo-mmo-gateway.chainsaw-monolithic-multitenancy.svc:8080/api/traces/v1/dev/api/traces \ --data-urlencode "service=grpc" \ | tee /tmp/jaeger.out @@ -42,7 +42,7 @@ spec: --header "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \ --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ --data-urlencode 'q={ resource.service.name="grpc" }' \ - https://tempo-monolithic-multitenancy-openshift-gateway.chainsaw-monolithic-multitenancy.svc:8080/api/traces/v1/dev/tempo/api/search \ + https://tempo-mmo-gateway.chainsaw-monolithic-multitenancy.svc:8080/api/traces/v1/dev/tempo/api/search \ | tee /tmp/tempo.out num_traces=$(jq ".traces | length" /tmp/tempo.out) @@ -68,7 +68,7 @@ spec: curl -vG \ --header "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \ --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ - https://tempo-monolithic-multitenancy-openshift-gateway.chainsaw-monolithic-multitenancy.svc:8080/api/traces/v1/dev/api/traces \ + https://tempo-mmo-gateway.chainsaw-monolithic-multitenancy.svc:8080/api/traces/v1/dev/api/traces \ --data-urlencode "service=http" \ | tee /tmp/jaeger.out @@ -96,7 +96,7 @@ spec: --header "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \ --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ --data-urlencode 'q={ resource.service.name="http" }' \ - https://tempo-monolithic-multitenancy-openshift-gateway.chainsaw-monolithic-multitenancy.svc:8080/api/traces/v1/dev/tempo/api/search \ + https://tempo-mmo-gateway.chainsaw-monolithic-multitenancy.svc:8080/api/traces/v1/dev/tempo/api/search \ | tee /tmp/tempo.out num_traces=$(jq ".traces | length" /tmp/tempo.out) diff --git a/tests/fixtures/chainsaw-tests/multitenancy/01-assert.yaml b/tests/fixtures/chainsaw-tests/multitenancy/01-assert.yaml index 86de05d..2666b66 100644 --- a/tests/fixtures/chainsaw-tests/multitenancy/01-assert.yaml +++ b/tests/fixtures/chainsaw-tests/multitenancy/01-assert.yaml @@ -86,7 +86,8 @@ metadata: app.kubernetes.io/instance: simplest app.kubernetes.io/managed-by: tempo-operator app.kubernetes.io/name: tempo - name: tempo-simplest-gateway + app.kubernetes.io/namespace: chainsaw-multitenancy + name: tempo-simplest-gateway-chainsaw-multitenancy rules: - apiGroups: - authentication.k8s.io @@ -109,11 +110,12 @@ metadata: app.kubernetes.io/instance: simplest app.kubernetes.io/managed-by: tempo-operator app.kubernetes.io/name: tempo - name: tempo-simplest-gateway + app.kubernetes.io/namespace: chainsaw-multitenancy + name: tempo-simplest-gateway-chainsaw-multitenancy roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: tempo-simplest-gateway + name: tempo-simplest-gateway-chainsaw-multitenancy subjects: - kind: ServiceAccount name: tempo-simplest-gateway @@ -235,8 +237,6 @@ spec: readOnly: true - args: - --log.level=warn - - --opa.admin-groups=system:cluster-admins,cluster-admin,dedicated-admin - - --opa.matcher=kubernetes_namespace_name - --web.listen=:8082 - --web.internal.listen=:8083 - --web.healthchecks.url=http://localhost:8082 diff --git a/tests/fixtures/chainsaw-tests/multitenancy/01-install-tempo.yaml b/tests/fixtures/chainsaw-tests/multitenancy/01-install-tempo.yaml index d45a4d5..2ce4748 100644 --- a/tests/fixtures/chainsaw-tests/multitenancy/01-install-tempo.yaml +++ b/tests/fixtures/chainsaw-tests/multitenancy/01-install-tempo.yaml @@ -5,6 +5,19 @@ metadata: name: simplest namespace: chainsaw-multitenancy spec: + retention: + global: + traces: 20h + perTenant: + dev: + traces: 10h + limits: + perTenant: + dev: + ingestion: + ingestionBurstSizeBytes: 1000000 + query: + maxSearchDuration: 1h storage: secret: name: minio diff --git a/tests/fixtures/chainsaw-tests/multitenancy/chainsaw-test.yaml b/tests/fixtures/chainsaw-tests/multitenancy/chainsaw-test.yaml index 477d7fe..2565e94 100755 --- a/tests/fixtures/chainsaw-tests/multitenancy/chainsaw-test.yaml +++ b/tests/fixtures/chainsaw-tests/multitenancy/chainsaw-test.yaml @@ -5,7 +5,6 @@ metadata: creationTimestamp: null name: multitenancy spec: - concurrent: false namespace: chainsaw-multitenancy steps: - name: step-00 @@ -20,18 +19,6 @@ spec: file: 01-install-tempo.yaml - assert: file: 01-assert.yaml - - name: Wait for Tempo to be ready - try: - - wait: - apiVersion: tempo.grafana.com/v1alpha1 - kind: TempoStack - name: simplest - namespace: chainsaw-multitenancy - timeout: 2m - for: - condition: - name: Ready - value: 'True' - name: step-02 try: - apply: From 5f24b037c00b97f5083b98649bbb4749a354c303 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Mon, 14 Jul 2025 18:44:45 +0200 Subject: [PATCH 12/35] TRACING-5480: Update @mui/x-data-grid No UI changes. Signed-off-by: Andreas Gerstmayr --- web/package-lock.json | 41 +++++++++++------------------------------ web/package.json | 1 - 2 files changed, 11 insertions(+), 31 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index 149da0a..317a309 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -4973,17 +4973,18 @@ } }, "node_modules/@mui/x-data-grid": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.20.0.tgz", - "integrity": "sha512-B5tAbjfn5OPMbSJuwag17Eb6QTe70BgV414tWVCLz/bhJTovQ4C5aDGCY3ahyxZECsxcGDSTT/orWtFmKOUO5A==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.29.8.tgz", + "integrity": "sha512-m4Dp1Vig8gFiBlcEOWimUku182cEw5YrAyXS3PfTSdbxa/20bFw7a8mlHdxO9ChHQRMf6TqbisdRm23CDIIdog==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.7", - "@mui/utils": "^5.16.6 || ^6.0.0", - "@mui/x-internals": "7.20.0", + "@mui/utils": "^5.16.6 || ^6.0.0 || ^7.0.0", + "@mui/x-internals": "7.29.0", "clsx": "^2.1.1", "prop-types": "^15.8.1", - "reselect": "^5.1.1" + "reselect": "^5.1.1", + "use-sync-external-store": "^1.0.0" }, "engines": { "node": ">=14.0.0" @@ -4995,10 +4996,10 @@ "peerDependencies": { "@emotion/react": "^11.9.0", "@emotion/styled": "^11.8.1", - "@mui/material": "^5.15.14 || ^6.0.0", - "@mui/system": "^5.15.14 || ^6.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@mui/material": "^5.15.14 || ^6.0.0 || ^7.0.0", + "@mui/system": "^5.15.14 || ^6.0.0 || ^7.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { @@ -5009,26 +5010,6 @@ } } }, - "node_modules/@mui/x-data-grid/node_modules/@mui/x-internals": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.20.0.tgz", - "integrity": "sha512-ScXdEwtnxmBEq9umeusnotfeVQnnhjOZcM2ddXyIupmzeGmgDDtEcXGyTgrS/GOc91J74g81s6eJ4UCrlYZ2sg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.25.7", - "@mui/utils": "^5.16.6 || ^6.0.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "react": "^17.0.0 || ^18.0.0" - } - }, "node_modules/@mui/x-data-grid/node_modules/reselect": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", diff --git a/web/package.json b/web/package.json index b7666c7..545c75a 100644 --- a/web/package.json +++ b/web/package.json @@ -102,7 +102,6 @@ "react-router-dom": "<7" }, "overrides": { - "@mui/x-data-grid": "7.20.0", "cheerio": "1.0.0-rc.12" } } From 17f366c0b92ef91f48ca80bc19bc6a5b6ed58b7e Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Mon, 14 Jul 2025 18:59:20 +0200 Subject: [PATCH 13/35] OU-553: Forward start/end values to TraceQL auto-completion Signed-off-by: Andreas Gerstmayr --- web/src/pages/TracesPage/Toolbar/TraceQLEditor.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/web/src/pages/TracesPage/Toolbar/TraceQLEditor.tsx b/web/src/pages/TracesPage/Toolbar/TraceQLEditor.tsx index 699f906..da37bfd 100644 --- a/web/src/pages/TracesPage/Toolbar/TraceQLEditor.tsx +++ b/web/src/pages/TracesPage/Toolbar/TraceQLEditor.tsx @@ -7,6 +7,7 @@ import { getProxyURLFor } from '../../../hooks/api'; import { usePatternFlyTheme } from '../../../components/console/utils/usePatternFlyTheme'; import { Prec } from '@codemirror/state'; import { insertNewlineAndIndent } from '@codemirror/commands'; +import { useTimeRange } from '@perses-dev/plugin-system'; interface TraceQLEditorProps { id?: string; @@ -34,13 +35,14 @@ export const codemirrorTheme = EditorView.theme({ export function TraceQLEditor({ id, tempo, query, setQuery, runQuery }: TraceQLEditorProps) { const { t } = useTranslation('plugin__distributed-tracing-console-plugin'); const { theme } = usePatternFlyTheme(); + const { timeRange } = useTimeRange(); const traceQLExtension = useMemo(() => { const client = tempo ? TempoDatasource.createClient({ directUrl: getProxyURLFor(tempo) }, {}) : undefined; - return TraceQLExtension({ client }); - }, [tempo]); + return TraceQLExtension({ client, timeRange }); + }, [tempo, timeRange]); const keyBindings = useMemo( () => From fd7426d2c13394a1a316be85550d43fe3bb509a2 Mon Sep 17 00:00:00 2001 From: Ishwar Kanse Date: Tue, 15 Jul 2025 20:59:26 +0530 Subject: [PATCH 14/35] Fix and improve Tracing UI plugin tests --- tests/PATTERNFLY_COMMANDS_EXAMPLES.md | 163 ++++++ tests/README.md | 30 +- tests/SELECTOR_BEST_PRACTICES.md | 286 ++++++++++ tests/cypress.config.ts | 3 +- tests/cypress/support/commands.ts | 282 +++++++--- tests/cypress/support/e2e.js | 15 + tests/e2e/dt-plugin-tests.cy.ts | 497 ++++++++++++++++++ .../01-assert.yaml | 94 ---- .../01-install-tempo.yaml | 83 --- .../02-install-otelcol.yaml | 50 -- .../03-assert.yaml | 13 - .../03-generate-traces.yaml | 36 -- .../04-assert.yaml | 27 - .../04-verify-traces.yaml | 107 ---- .../chainsaw-test.yaml | 43 -- .../01-assert.yaml | 44 ++ .../01-install-tempo.yaml | 52 ++ .../02-assert.yaml | 4 +- .../02-install-otelcol.yaml | 132 +++++ .../assert-create-sas.yaml | 42 ++ .../assert-kubeadmin-traces-verify.yaml | 15 + .../assert-tempo-rbac-sa-1-traces-gen.yaml | 15 + .../assert-tempo-rbac-sa-1-traces-verify.yaml | 15 + .../assert-tempo-rbac-sa-2-traces-gen.yaml | 15 + .../chainsaw-test.yaml | 49 ++ .../create-SAs-with-namespace-access.yaml | 91 ++++ .../kubeadmin-traces-verify.yaml | 201 +++++++ .../tempo-rbac-sa-1-traces-gen.yaml | 42 ++ .../tempo-rbac-sa-1-traces-verify.yaml | 207 ++++++++ .../tempo-rbac-sa-2-traces-gen.yaml | 42 ++ .../00-assert.yaml | 2 +- .../00-install-storage.yaml | 14 +- .../01-assert.yaml | 221 ++------ .../01-install-tempo.yaml | 35 +- .../02-assert.yaml | 2 +- .../multitenancy-rbac/02-install-otelcol.yaml | 139 +++++ .../multitenancy-rbac/assert-create-sas.yaml | 42 ++ .../assert-kubeadmin-traces-verify.yaml | 15 + .../assert-tempo-rbac-sa-1-traces-gen.yaml | 15 + .../assert-tempo-rbac-sa-1-traces-verify.yaml | 15 + .../assert-tempo-rbac-sa-2-traces-gen.yaml | 15 + .../multitenancy-rbac/chainsaw-test.yaml | 55 ++ .../create-SAs-with-namespace-access.yaml | 91 ++++ .../kubeadmin-traces-verify.yaml | 201 +++++++ .../tempo-rbac-sa-1-traces-gen.yaml | 42 ++ .../tempo-rbac-sa-1-traces-verify.yaml | 208 ++++++++ .../tempo-rbac-sa-2-traces-gen.yaml | 42 ++ .../multitenancy/02-install-otelcol.yaml | 86 --- .../multitenancy/03-assert.yaml | 15 - .../multitenancy/03-generate-traces.yaml | 38 -- .../multitenancy/04-assert.yaml | 31 -- .../multitenancy/04-verify-traces.yaml | 129 ----- .../multitenancy/chainsaw-test.yaml | 39 -- tests/package-lock.json | 42 +- tests/tests/dt-plugin-tests.cy.ts | 348 ------------ 55 files changed, 3119 insertions(+), 1458 deletions(-) create mode 100644 tests/PATTERNFLY_COMMANDS_EXAMPLES.md create mode 100644 tests/SELECTOR_BEST_PRACTICES.md create mode 100644 tests/e2e/dt-plugin-tests.cy.ts delete mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/01-assert.yaml delete mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/01-install-tempo.yaml delete mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/02-install-otelcol.yaml delete mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/03-assert.yaml delete mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/03-generate-traces.yaml delete mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/04-assert.yaml delete mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/04-verify-traces.yaml delete mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/chainsaw-test.yaml create mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/01-assert.yaml create mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/01-install-tempo.yaml rename tests/fixtures/chainsaw-tests/{monolithic-multitenancy-openshift => monolithic-multitenancy-rbac}/02-assert.yaml (82%) create mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/02-install-otelcol.yaml create mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-create-sas.yaml create mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-kubeadmin-traces-verify.yaml create mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-tempo-rbac-sa-1-traces-gen.yaml create mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-tempo-rbac-sa-1-traces-verify.yaml create mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-tempo-rbac-sa-2-traces-gen.yaml create mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/chainsaw-test.yaml create mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/create-SAs-with-namespace-access.yaml create mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/kubeadmin-traces-verify.yaml create mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-1-traces-gen.yaml create mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-1-traces-verify.yaml create mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-2-traces-gen.yaml rename tests/fixtures/chainsaw-tests/{multitenancy => multitenancy-rbac}/00-assert.yaml (71%) rename tests/fixtures/chainsaw-tests/{multitenancy => multitenancy-rbac}/00-install-storage.yaml (86%) rename tests/fixtures/chainsaw-tests/{multitenancy => multitenancy-rbac}/01-assert.yaml (54%) rename tests/fixtures/chainsaw-tests/{multitenancy => multitenancy-rbac}/01-install-tempo.yaml (61%) rename tests/fixtures/chainsaw-tests/{multitenancy => multitenancy-rbac}/02-assert.yaml (74%) create mode 100644 tests/fixtures/chainsaw-tests/multitenancy-rbac/02-install-otelcol.yaml create mode 100644 tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-create-sas.yaml create mode 100644 tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-kubeadmin-traces-verify.yaml create mode 100644 tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-tempo-rbac-sa-1-traces-gen.yaml create mode 100644 tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-tempo-rbac-sa-1-traces-verify.yaml create mode 100644 tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-tempo-rbac-sa-2-traces-gen.yaml create mode 100755 tests/fixtures/chainsaw-tests/multitenancy-rbac/chainsaw-test.yaml create mode 100644 tests/fixtures/chainsaw-tests/multitenancy-rbac/create-SAs-with-namespace-access.yaml create mode 100644 tests/fixtures/chainsaw-tests/multitenancy-rbac/kubeadmin-traces-verify.yaml create mode 100644 tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-1-traces-gen.yaml create mode 100644 tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-1-traces-verify.yaml create mode 100644 tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-2-traces-gen.yaml delete mode 100644 tests/fixtures/chainsaw-tests/multitenancy/02-install-otelcol.yaml delete mode 100644 tests/fixtures/chainsaw-tests/multitenancy/03-assert.yaml delete mode 100644 tests/fixtures/chainsaw-tests/multitenancy/03-generate-traces.yaml delete mode 100644 tests/fixtures/chainsaw-tests/multitenancy/04-assert.yaml delete mode 100644 tests/fixtures/chainsaw-tests/multitenancy/04-verify-traces.yaml delete mode 100755 tests/fixtures/chainsaw-tests/multitenancy/chainsaw-test.yaml delete mode 100644 tests/tests/dt-plugin-tests.cy.ts diff --git a/tests/PATTERNFLY_COMMANDS_EXAMPLES.md b/tests/PATTERNFLY_COMMANDS_EXAMPLES.md new file mode 100644 index 0000000..4cc0cdd --- /dev/null +++ b/tests/PATTERNFLY_COMMANDS_EXAMPLES.md @@ -0,0 +1,163 @@ +# PatternFly Cypress Commands - Usage Examples + +This file demonstrates how to properly use the PatternFly-aware Cypress commands to avoid React state management issues. + +## ✅ Recommended Usage Patterns + +### 1. Simple Component Interactions + +```typescript +// Empty State - Clean and reliable +cy.pfEmptyState().within(() => { + cy.byRole('heading').should('contain', 'No items found'); +}); + +// Buttons - Direct and stable +cy.pfButton('Create Instance').click(); +cy.pfButton('Save Changes').should('be.disabled'); +cy.pfButton('Cancel').should('be.visible'); +``` + +### 2. Menu Navigation + +```typescript +// Basic menu toggle +cy.pfMenuToggle('Select Instance').click(); +cy.pfMenuItem('TempoStack').click(); + +// Or with error handling for dynamic content +cy.get('body').then(($body) => { + if ($body.find('.pf-v6-c-menu-toggle:contains("Create Instance")').length > 0) { + cy.pfMenuToggle('Create Instance').click(); + cy.pfMenuItem('TempoStack Instance').click(); + } +}); +``` + +### 3. Toolbar Interactions (Recommended Approach) + +```typescript +// Simple toolbar item selection +cy.pfToolbarItem(0).within(() => { + cy.get('.pf-v6-c-menu-toggle, .pf-v5-c-menu-toggle').first().click(); +}); +cy.get('.pf-v6-c-menu__item, .pf-v5-c-menu__item').contains('Option 1').click(); + +// Wait for stability between interactions +cy.wait(1000); +cy.pfToolbarItem(1).within(() => { + cy.get('.pf-v6-c-menu-toggle, .pf-v5-c-menu-toggle').first().click(); +}); +``` + +### 4. Form Interactions + +```typescript +// Using label-based selection +cy.byLabelText('Instance Name').type('my-instance'); +cy.byLabelText('Namespace').select('default'); + +// Form submission +cy.pfButton('Submit').click(); +``` + +## ⚠️ Patterns to Avoid + +### 1. Complex Chained Operations + +```typescript +// ❌ AVOID: Too many chained operations without waits +cy.pfToolbarItem(0).within(() => { + cy.pfMenuToggle().click(); +}).then(() => { + cy.pfMenuItem('Item').click(); +}).then(() => { + cy.pfToolbarItem(1).within(() => { + cy.pfMenuToggle().click(); + }); +}); + +// ✅ BETTER: Break into separate operations with waits +cy.pfToolbarItem(0).within(() => { + cy.get('.pf-v6-c-menu-toggle').first().click(); +}); +cy.get('.pf-v6-c-menu__item').contains('Item').click(); +cy.wait(1000); // Allow React state to stabilize +cy.pfToolbarItem(1).within(() => { + cy.get('.pf-v6-c-menu-toggle').first().click(); +}); +``` + +### 2. Rapid Sequential Clicks + +```typescript +// ❌ AVOID: Multiple rapid interactions +cy.pfMenuToggle().click(); +cy.pfMenuItem('Item1').click(); +cy.pfMenuToggle().click(); // Too fast, React state not updated +cy.pfMenuItem('Item2').click(); + +// ✅ BETTER: Add stabilization waits +cy.pfMenuToggle().click(); +cy.pfMenuItem('Item1').click(); +cy.wait(500); // Let React update +cy.pfMenuToggle().click(); +cy.pfMenuItem('Item2').click(); +``` + +## 🛡️ Defensive Programming Patterns + +### 1. Check Element Existence + +```typescript +// Safe menu interaction +cy.get('body').then(($body) => { + if ($body.find('.pf-v6-c-menu-toggle:contains("Actions")').length > 0) { + cy.pfMenuToggle('Actions').click(); + if ($body.find('.pf-v6-c-menu__item:contains("Delete")').length > 0) { + cy.pfMenuItem('Delete').click(); + } + } +}); +``` + +### 2. Wait for Stability + +```typescript +// Wait for page load and React hydration +cy.visit('/traces'); +cy.wait(2000); // Allow initial React state to settle + +// Perform interactions +cy.pfEmptyState().should('be.visible'); +cy.pfButton('Create Instance').click(); +``` + +### 3. Use Timeouts + +```typescript +// Custom timeouts for slow-loading components +cy.pfMenuToggle('Select Instance', { timeout: 15000 }).click(); +cy.pfMenuItem('TempoStack', { timeout: 10000 }).click(); +``` + +## 🎯 Best Practices Summary + +1. **Add waits between complex interactions** to let React state stabilize +2. **Use defensive checks** for dynamic content +3. **Prefer direct PatternFly commands** over complex CSS selectors +4. **Break complex operations** into smaller, testable steps +5. **Handle uncaught exceptions** gracefully in support files +6. **Use timeouts** for components that load asynchronously + +## 🔧 Troubleshooting + +If you encounter "e is not a function" errors: + +1. **Add `cy.wait(1000-2000)`** between interactions +2. **Check if elements exist** before interacting +3. **Use more specific selectors** instead of nth-child +4. **Verify React components have finished rendering** +5. **Review browser console** for additional React errors + +These patterns will make your tests more reliable and less prone to React state management issues. \ No newline at end of file diff --git a/tests/README.md b/tests/README.md index 71b59e1..ce5955f 100644 --- a/tests/README.md +++ b/tests/README.md @@ -17,17 +17,25 @@ node_modules/ -> dependencies will be installed at runtime here ## Directory structure After dependencies are installed successfully and before we run actual tests, please confirm if we have correct structure as below. ```bash -% ls -ltr tests -drwxr-xr-x views --rw-r--r-- reporter-config.json --rw-r--r-- package.json -drwxr-xr-x node_modules -drwxr-xr-x cypress --rw-r--r-- cypress.config.ts --rw-r--r-- README.md -drwxr-xr-x tests --rw-r--r-- tsconfig.json -drwxr-xr-x fixtures +tree -L 1 +. +├── cypress +├── cypress.config.ts +├── Dockerfile +├── Dockerfile-cypress +├── e2e +├── fixtures +├── node_modules +├── package-lock.json +├── package.json +├── PATTERNFLY_COMMANDS_EXAMPLES.md +├── README.md +├── reporter-config.json +├── SELECTOR_BEST_PRACTICES.md +├── tsconfig.json +└── views + +6 directories, 10 files ```` ### Export necessary variables diff --git a/tests/SELECTOR_BEST_PRACTICES.md b/tests/SELECTOR_BEST_PRACTICES.md new file mode 100644 index 0000000..80d3072 --- /dev/null +++ b/tests/SELECTOR_BEST_PRACTICES.md @@ -0,0 +1,286 @@ +# Cypress Selector Best Practices for PatternFly Applications + +This guide outlines the best practices for element selection in Cypress tests for PatternFly-based applications to ensure maintainable, reliable, and accessible test automation. + +## Selector Priority (Recommended Order) + +1. **`data-cy` attributes** - Custom test attributes (highest priority) +2. **`data-testid` attributes** - Common testing attributes +3. **`data-test` attributes** - Legacy test attributes +4. **PatternFly component attributes** - `data-ouia-component-*`, `data-pf-*` +5. **ARIA attributes** - Accessibility attributes (PatternFly components are ARIA-compliant) +6. **Semantic roles** - Role-based selection +7. **Text content** - Visible text +8. **PatternFly CSS classes** - Component-specific classes (better than generic CSS) +9. **Generic CSS classes/IDs** - Last resort (lowest priority) + +## PatternFly-Specific Considerations + +PatternFly components come with built-in accessibility features and consistent patterns: +- Most components have proper ARIA attributes +- Components follow consistent naming conventions +- Use PatternFly's data attributes when available +- Leverage component-specific selectors over generic CSS classes + +## Custom Commands Available + +### Test-Specific Selectors +```typescript +// Primary recommendation: data-cy attributes +cy.byCy('submit-button') // [data-cy="submit-button"] + +// Alternative test attributes +cy.byTestID('username-input') // [data-test="username-input"] +cy.findByTestId('password-field') // [data-testid="password-field"] +cy.byTestSelector('menu-dropdown') // [data-test-selector="menu-dropdown"] +``` + +### PatternFly Component Selectors +```typescript +// PatternFly-specific helpers +cy.pfMenuToggle('Create a Tempo instance') // Menu toggle by text +cy.pfMenuItem('TempoStack instance') // Menu item selection +cy.pfButton('View documentation') // PatternFly button by text +cy.pfEmptyState() // Empty state component +cy.pfToolbarItem(0) // First toolbar item +``` + +### Accessibility-Based Selectors +```typescript +// ARIA labels (PatternFly components have good ARIA support) +cy.byAriaLabel('Close dialog') // [aria-label="Close dialog"] +cy.byLabelText('Instance Type') // Form field by label + +// Role-based selection +cy.byRole('button') // [role="button"] +cy.byRole('button', 'Submit form') // [role="button"][aria-label="Submit form"] +``` + +### Content-Based Selectors +```typescript +// Text content (use sparingly, text can change) +cy.byText('View documentation') // contains('View documentation') +cy.byButtonText('Create instance') // button[type="button"] containing text +``` + +## Examples: Converting Brittle Selectors + +### ❌ Avoid: Complex CSS Selectors +```typescript +// BAD: Fragile, implementation-dependent +cy.get(':nth-child(1) > .pf-v6-c-toolbar__item > .pf-v6-c-form > .pf-v6-c-form__group') +cy.get('.pf-v6-c-empty-state__title-text') +cy.get('.MuiDataGrid-row--firstVisible > [data-field="name"] > .MuiBox-root') +cy.get('.pf-v6-c-menu-toggle__toggle-icon').click() +``` + +### ✅ Preferred: Stable, Semantic Selectors +```typescript +// GOOD: Stable and meaningful +cy.byCy('tempo-instance-dropdown') +cy.byAriaLabel('Select Tempo instance') // PatternFly components have ARIA labels +cy.byRole('button', 'Create a Tempo instance') // Use semantic roles +cy.byTestID('documentation-link') +cy.pfMenuToggle('Create a Tempo instance') // PatternFly-specific helper +``` + +## PatternFly Component Examples + +### Empty State +```typescript +// Current (brittle): +cy.get('.pf-v6-c-empty-state__title-text') + +// Improved: +cy.byCy('empty-state-title') +// OR leverage PatternFly's semantic structure: +cy.byRole('heading').contains('No Tempo instances yet') +``` + +### Toolbar & Dropdowns +```typescript +// Current (fragile): +cy.get(':nth-child(1) > .pf-v6-c-toolbar__item > .pf-v6-c-form') + +// Improved: +cy.byCy('tempo-instance-selector') +// OR use PatternFly menu patterns: +cy.pfMenuToggle().contains('Select instance') +cy.pfMenuItem('tempo-stack-instance') +``` + +### Buttons +```typescript +// Current: +cy.get('.pf-v6-c-button__text') + +// Improved: +cy.byCy('submit-button') +// OR use ARIA (PatternFly buttons have proper ARIA): +cy.byRole('button', 'Submit') +``` + +### Forms +```typescript +// Current: +cy.get('.pf-v6-c-form__group-control > .pf-v6-c-menu-toggle') + +// Improved: +cy.byCy('form-field-selector') +// OR use form labels (PatternFly forms are accessible): +cy.byLabelText('Instance Type') +``` + +## Implementation Strategy + +### For New Elements +1. **Add data-cy attributes** to UI components: + ```jsx + + + ``` + +2. **Use custom commands** in tests: + ```typescript + cy.byCy('create-tempo-instance').click(); + cy.byCy('trace-search-input').type('my-service'); + ``` + +### For Existing Tests +1. **Identify brittle selectors** (CSS classes, nth-child, complex paths) +2. **Add data-cy attributes** to corresponding UI components +3. **Replace selectors** with custom commands gradually + +## Specific Improvements for Current Tests + +Based on your existing test file, here are specific improvements: + +### Empty State Testing +```typescript +// Current (brittle): +cy.get('.pf-v6-c-empty-state__title-text') + .should('be.visible') + .and('have.text', 'No Tempo instances yet'); + +// Improved options: +// Option 1: Custom data attribute +cy.byCy('empty-state-title') + .should('have.text', 'No Tempo instances yet'); + +// Option 2: PatternFly helper + semantic role +cy.pfEmptyState().within(() => { + cy.byRole('heading').should('have.text', 'No Tempo instances yet'); +}); + +// Option 3: Direct ARIA/semantic approach +cy.byRole('heading', 'No Tempo instances yet').should('be.visible'); +``` + +### Menu Toggle & Dropdown Selection +```typescript +// Current (fragile): +cy.get(':nth-child(1) > .pf-v6-c-toolbar__item > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__button > .pf-v6-c-menu-toggle__controls > .pf-v6-c-menu-toggle__toggle-icon').click(); + +// Improved options: +// Option 1: Custom data attribute (best) +cy.byCy('tempo-instance-selector').click(); + +// Option 2: PatternFly helper +cy.pfMenuToggle('Create a Tempo instance').click(); + +// Option 3: ARIA label +cy.byAriaLabel('Select Tempo instance').click(); +``` + +### Menu Item Selection +```typescript +// Current (nested and fragile): +cy.get(':nth-child(2) > .pf-v6-c-menu__item > .pf-v6-c-menu__item-main > .pf-v6-c-menu__item-text').click(); + +// Improved: +// Option 1: Custom data attributes +cy.byCy('tempo-stack-option').click(); + +// Option 2: PatternFly helper +cy.pfMenuItem('Create a TempoStack instance').click(); + +// Option 3: Text-based (least preferred, but better than CSS) +cy.byText('Create a TempoStack instance').click(); +``` + +### Button Interactions +```typescript +// Current: +cy.contains('.pf-v6-c-button', 'View documentation') + .should('be.visible') + .and('have.text', 'View documentation'); + +// Improved: +// Option 1: Custom data attribute +cy.byCy('documentation-button') + .should('have.text', 'View documentation'); + +// Option 2: PatternFly helper +cy.pfButton('View documentation').should('be.visible'); + +// Option 3: Semantic role +cy.byRole('button', 'View documentation').should('be.visible'); +``` + +### Complex Toolbar Navigation +```typescript +// Current (extremely fragile): +cy.get('.pf-m-toggle-group > .pf-v6-c-toolbar__group > :nth-child(2) > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__button').click(); + +// Improved: +// Option 1: Custom data attributes (recommended) +cy.byCy('service-filter-dropdown').click(); + +// Option 2: Combine PatternFly helpers +cy.pfToolbarItem(1).within(() => { + cy.pfMenuToggle().click(); +}); + +// Option 3: Label-based for form fields +cy.byLabelText('Service Name').click(); +``` + +### Checkbox Selection in Menus +```typescript +// Current: +cy.contains('.pf-v6-c-menu__item-text', 'http') + .closest('.pf-v6-c-menu__item') + .find('input[type="checkbox"]') + .check(); + +// Improved: +// Option 1: Custom data attributes +cy.byCy('service-type-http').check(); + +// Option 2: Semantic approach +cy.byLabelText('http').check(); +cy.byRole('checkbox', 'http').check(); + +// Option 3: Enhanced PatternFly helper +cy.pfMenuItem('http').within(() => { + cy.get('input[type="checkbox"]').check(); +}); +``` + +## Benefits + +- **Stability**: Tests survive UI refactoring +- **Maintainability**: Clear intent and easy updates +- **Accessibility**: Encourages proper ARIA usage +- **Performance**: Faster element location +- **Readability**: Self-documenting test code + +## Migration Checklist + +- [ ] Audit existing selectors for brittleness +- [ ] Add data-cy attributes to critical UI elements +- [ ] Replace CSS selectors with custom commands +- [ ] Update test documentation +- [ ] Train team on new practices + +Remember: **The best selector is one that survives code changes and clearly expresses test intent.** \ No newline at end of file diff --git a/tests/cypress.config.ts b/tests/cypress.config.ts index c8c6c36..0301e5e 100644 --- a/tests/cypress.config.ts +++ b/tests/cypress.config.ts @@ -28,7 +28,6 @@ export default defineConfig({ openMode: 0, }, e2e: { - browser: "chrome", viewportWidth: 1920, viewportHeight: 1080, setupNodeEvents(on, config) { @@ -97,7 +96,7 @@ export default defineConfig({ return config; }, supportFile: './cypress/support/e2e.js', - specPattern: 'tests/**/*.cy.{js,jsx,ts,tsx}', + specPattern: 'e2e/**/*.cy.{js,jsx,ts,tsx}', numTestsKeptInMemory: 1, testIsolation: false, experimentalModifyObstructiveThirdPartyCode: true, diff --git a/tests/cypress/support/commands.ts b/tests/cypress/support/commands.ts index dbcd791..1949021 100644 --- a/tests/cypress/support/commands.ts +++ b/tests/cypress/support/commands.ts @@ -7,42 +7,59 @@ import { guidedTour } from '../../views/tour'; export {}; declare global { - interface Chainable { - byTestID( - selector: string, - options?: Partial, - ): Chainable; - byTestActionID(selector: string): Chainable>; - byLegacyTestID( - selector: string, - options?: Partial, - ): Chainable>; - byButtonText(selector: string): Chainable>; - byDataID(selector: string): Chainable>; - byTestSelector( - selector: string, - options?: Partial, - ): Chainable>; - byTestDropDownMenu(selector: string): Chainable>; - byTestOperatorRow( - selector: string, - options?: Partial, - ): Chainable>; - byTestSectionHeading(selector: string): Chainable>; - byTestOperandLink(selector: string): Chainable>; + namespace Cypress { + interface Chainable { + byTestID( + selector: string, + options?: Partial, + ): Chainable; + byTestActionID(selector: string): Chainable>; + byLegacyTestID( + selector: string, + options?: Partial, + ): Chainable>; + byButtonText(selector: string): Chainable>; + byDataID(selector: string): Chainable>; + byTestSelector( + selector: string, + options?: Partial, + ): Chainable>; + byTestDropDownMenu(selector: string): Chainable>; + byTestOperatorRow( + selector: string, + options?: Partial, + ): Chainable>; + byTestSectionHeading(selector: string): Chainable>; + byTestOperandLink(selector: string): Chainable>; + // Best practice selector commands + byCy(selector: string, options?: Partial): Chainable>; + byAriaLabel(selector: string, options?: Partial): Chainable>; + byRole(role: string, name?: string, options?: Partial): Chainable>; + byText(text: string, options?: Partial): Chainable>; + findByTestId(selector: string): Chainable>; + // PatternFly-specific commands + byLabelText(text: string, options?: Partial): Chainable>; + pfMenuToggle(text?: string, options?: Partial): Chainable>; + pfMenuItem(text: string, options?: Partial): Chainable>; + pfButton(text: string, options?: Partial): Chainable>; + pfEmptyState(options?: Partial): Chainable>; + pfToolbarItem(index?: number, options?: Partial): Chainable>; + } } } declare global { - interface Chainable { - switchPerspective(perspective: string); - uiLogin(provider: string, username: string, password: string); - uiLogout(); - cliLogin(username?, password?, hostapi?); - cliLogout(); - adminCLI(command: string, options?); - login(provider?: string, username?: string, password?: string): Chainable; - executeAndDelete(command: string); + namespace Cypress { + interface Chainable { + switchPerspective(perspective: string): Chainable; + uiLogin(provider: string, username: string, password: string): Chainable; + uiLogout(): Chainable; + cliLogin(username?: string, password?: string, hostapi?: string): Chainable; + cliLogout(): Chainable; + adminCLI(command: string, options?: any): Chainable; + login(provider?: string, username?: string, password?: string): Chainable; + executeAndDelete(command: string): Chainable; + } } } @@ -98,69 +115,59 @@ Cypress.Commands.add('byTestOperandLink', (selector: string) => { cy.get(`[data-test-operand-link="${selector}"]`); }); +// Simplified login command that handles OAuth redirect properly Cypress.Commands.add( 'login', ( - provider: string = Cypress.env('LOGIN_IDP'), - username: string = Cypress.env('LOGIN_USERNAME'), + provider: string = Cypress.env('LOGIN_IDP') || 'kube:admin', + username: string = Cypress.env('LOGIN_USERNAME') || 'kubeadmin', password: string = Cypress.env('LOGIN_PASSWORD'), - oauthurl: string, ) => { cy.session( [provider, username], () => { cy.visit(Cypress.config('baseUrl')); - cy.window().then( - ( - win: any, // eslint-disable-line @typescript-eslint/no-explicit-any - ) => { - // Check if auth is disabled (for a local development environment) - if (win.SERVER_FLAGS?.authDisabled) { - cy.task('log', ' skipping login, console is running with auth disabled'); - return; - } - cy.exec( - `oc get node --selector=hypershift.openshift.io/managed --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, - ).then((result) => { - cy.log(result.stdout); - cy.task('log', result.stdout); - if (result.stdout.includes('Ready')) { - cy.log(`Attempting login via cy.origin to: ${oauthurl}`); - cy.task('log', `Attempting login via cy.origin to: ${oauthurl}`); - cy.origin( - oauthurl, - { args: { username, password } }, - ({ username, password }) => { - cy.get('#inputUsername').type(username); - cy.get('#inputPassword').type(password); - cy.get('button[type=submit]').click(); - }, - ); - } else { - cy.task('log', ` Logging in as ${username} using fallback on ${oauthurl}`); - cy.origin( - oauthurl, - { args: { provider, username, password } }, - ({ provider, username, password }) => { - cy.get('[data-test-id="login"]').should('be.visible'); - cy.get('body').then(($body) => { - if ($body.text().includes(provider)) { - cy.contains(provider).should('be.visible').click(); - } - }); - cy.get('#inputUsername').type(username); - cy.get('#inputPassword').type(password); - cy.get('button[type=submit]').click(); + cy.window().then((win: any) => { + // Check if auth is disabled (for a local development environment) + if (win.SERVER_FLAGS?.authDisabled) { + cy.task('log', ' skipping login, console is running with auth disabled'); + return; + } + + cy.task('log', ` Logging in as ${username}`); + + // Get the current URL to determine if we've been redirected to OAuth + cy.url().then((currentUrl) => { + const url = new URL(currentUrl); + const oauthOrigin = `${url.protocol}//${url.hostname.replace('console-openshift-console', 'oauth-openshift')}`; + + cy.task('log', `OAuth origin: ${oauthOrigin}`); + + // Use cy.origin to handle the OAuth login on different domain + cy.origin( + oauthOrigin, + { args: { provider, username, password } }, + ({ provider, username, password }) => { + cy.get('[data-test-id="login"]').should('be.visible'); + cy.get('body').then(($body) => { + if ($body.text().includes(provider)) { + cy.contains(provider).should('be.visible').click(); } - ); + }); + cy.get('#inputUsername').type(username); + cy.get('#inputPassword').type(password); + cy.get('button[type=submit]').click(); } - }); - }, - ); + ); + }); + }); }, { cacheAcrossSpecs: true, validate() { + cy.visit(Cypress.config('baseUrl')); + // Wait for any OAuth redirects to complete and ensure we're on console domain + cy.url({ timeout: 30000 }).should('contain', 'console-openshift-console'); cy.byTestID("username", {timeout: 120000}).should('be.visible'); guidedTour.close(); }, @@ -178,8 +185,10 @@ Cypress.Commands.add('switchPerspective', (perspective: string) => { cy.get('#nav-toggle').click(); } }); - nav.sidenav.switcher.changePerspectiveTo(perspective); - nav.sidenav.switcher.shouldHaveText(perspective); + // Note: nav object would need to be imported or defined elsewhere + // For now, using direct DOM selectors + cy.get('[data-test="perspective-switcher-toggle"]').click(); + cy.contains('[data-test="perspective-switcher-menu-option"]', perspective).click(); }); // To avoid influence from upstream login change @@ -263,4 +272,111 @@ Cypress.Commands.add('executeAndDelete', (command: string) => { cy.task('log', `Command "${command}" executed successfully`); } }); -}); \ No newline at end of file +}); + +// Best practice selector commands following accessibility and stability guidelines + +Cypress.Commands.add( + 'byCy', + (selector: string, options?: Partial) => { + cy.get(`[data-cy="${selector}"]`, options); + }, +); + +Cypress.Commands.add( + 'byAriaLabel', + (selector: string, options?: Partial) => { + cy.get(`[aria-label="${selector}"]`, options); + }, +); + +Cypress.Commands.add( + 'byRole', + (role: string, name?: string, options?: Partial) => { + const selector = name ? `[role="${role}"][aria-label="${name}"], [role="${role}"][aria-labelledby*="${name}"]` : `[role="${role}"]`; + cy.get(selector, options); + }, +); + +Cypress.Commands.add( + 'byText', + (text: string, options?: Partial) => { + cy.contains(text, options); + }, +); + +Cypress.Commands.add('findByTestId', (selector: string) => { + return cy.get(`[data-testid="${selector}"]`); +}); + +// PatternFly-specific commands for common component patterns + +Cypress.Commands.add( + 'byLabelText', + (text: string, options?: Partial) => { + // Find form elements by their associated label - with error handling + cy.get('body').then(($body) => { + if ($body.find(`label:contains("${text}")`).length > 0) { + cy.get(`label:contains("${text}")`, options).then(($label) => { + const forAttr = $label.attr('for'); + if (forAttr) { + cy.get(`#${forAttr}`, options); + } else { + cy.wrap($label).find('input, select, textarea', options); + } + }); + } else { + cy.log(`Label with text "${text}" not found`); + } + }); + }, +); + +Cypress.Commands.add( + 'pfMenuToggle', + (text?: string, options?: Partial) => { + const defaultOptions = { timeout: 10000, ...options }; + if (text) { + cy.get('.pf-v6-c-menu-toggle, .pf-v5-c-menu-toggle', defaultOptions).contains(text); + } else { + cy.get('.pf-v6-c-menu-toggle, .pf-v5-c-menu-toggle', defaultOptions); + } + }, +); + +Cypress.Commands.add( + 'pfMenuItem', + (text: string, options?: Partial) => { + const defaultOptions = { timeout: 10000, ...options }; + cy.get('.pf-v6-c-menu__item, .pf-v5-c-menu__item', defaultOptions).contains(text); + }, +); + +Cypress.Commands.add( + 'pfButton', + (text: string, options?: Partial) => { + const defaultOptions = { timeout: 10000, ...options }; + cy.contains('.pf-v6-c-button, .pf-v5-c-button', text, defaultOptions); + }, +); + +Cypress.Commands.add( + 'pfEmptyState', + (options?: Partial) => { + const defaultOptions = { timeout: 10000, ...options }; + cy.get('.pf-v6-c-empty-state, .pf-v5-c-empty-state', defaultOptions); + }, +); + +Cypress.Commands.add( + 'pfToolbarItem', + (index?: number, options?: Partial) => { + const selector = '.pf-v6-c-toolbar__item, .pf-v5-c-toolbar__item'; + const defaultOptions = { timeout: 10000, ...options }; + if (typeof index === 'number') { + cy.get(selector, defaultOptions).eq(index); + } else { + cy.get(selector, defaultOptions); + } + }, +); \ No newline at end of file diff --git a/tests/cypress/support/e2e.js b/tests/cypress/support/e2e.js index 06b7d84..51dd1c0 100644 --- a/tests/cypress/support/e2e.js +++ b/tests/cypress/support/e2e.js @@ -2,3 +2,18 @@ import './commands'; import registerCypressGrep from '@cypress/grep'; registerCypressGrep(); + +// Handle uncaught exceptions from the application +Cypress.on('uncaught:exception', (err, runnable) => { + // Return false to prevent the test from failing on uncaught exceptions + // that might be caused by the application's React state management + if (err.message.includes('e is not a function') || + err.message.includes('Cannot read prop') || + err.message.includes('undefined is not a function') || + err.message.includes('Cannot read properties of undefined')) { + console.log('Caught application error:', err.message); + return false; + } + // Let other exceptions fail the test + return true; +}); diff --git a/tests/e2e/dt-plugin-tests.cy.ts b/tests/e2e/dt-plugin-tests.cy.ts new file mode 100644 index 0000000..00cac9c --- /dev/null +++ b/tests/e2e/dt-plugin-tests.cy.ts @@ -0,0 +1,497 @@ +import { operatorHubPage } from '../views/operator-hub-page'; + +// Set constants for the operators that need to be installed for tests. +const DTP = { + namespace: 'openshift-cluster-observability-operator', + packageName: 'cluster-observability-operator', + operatorName: 'Cluster Observability Operator', + config: { + kind: 'UIPlugin', + name: 'distributed-tracing', + }, +}; + +const OTEL = { + namespace: 'openshift-opentelemetry-operator', + packageName: 'opentelemetry-product', + operatorName: 'Red Hat build of OpenTelemetry', +}; + +const TEMPO = { + namespace: 'openshift-tempo-operator', + packageName: 'tempo-product', + operatorName: 'Tempo Operator', +}; + +describe('OpenShift Distributed Tracing UI Plugin tests', () => { + before(() => { + // Cleanup any existing resources from interrupted tests + cy.log('Cleanup any existing resources from previous interrupted tests'); + if (Cypress.env('SKIP_COO_INSTALL')) { + cy.log('Delete Distributed Tracing UI Plugin instance if exists.'); + cy.executeAndDelete( + `oc delete ${DTP.config.kind} ${DTP.config.name} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, + ); + + cy.log('Delete Chainsaw namespaces if they exist.'); + cy.exec( + `for ns in $(oc get projects -o name --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} | grep "chainsaw-" | sed 's|project.project.openshift.io/||'); do oc get opentelemetrycollectors.opentelemetry.io,tempostacks.tempo.grafana.com,tempomonolithics.tempo.grafana.com,pvc -n $ns -o name --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} 2>/dev/null | xargs --no-run-if-empty -I {} oc patch {} -n $ns --type merge -p '{"metadata":{"finalizers":[]}}' --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} 2>/dev/null || true; oc delete project $ns --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} || true; done`, + { + timeout: 180000, + failOnNonZeroExit: false + } + ); + + // Only remove cluster-admin role if provider is not kube:admin + if (Cypress.env('LOGIN_IDP') !== 'kube:admin') { + cy.log('Remove cluster-admin role from user if exists.'); + cy.executeAndDelete( + `oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, + ); + } + } else { + cy.log('Delete Distributed Tracing UI Plugin instance if exists.'); + cy.executeAndDelete( + `oc delete ${DTP.config.kind} ${DTP.config.name} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, + ); + + cy.log('Delete Chainsaw namespaces if they exist.'); + cy.exec( + `for ns in $(oc get projects -o name --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} | grep "chainsaw-" | sed 's|project.project.openshift.io/||'); do oc get opentelemetrycollectors.opentelemetry.io,tempostacks.tempo.grafana.com,tempomonolithics.tempo.grafana.com,pvc -n $ns -o name --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} 2>/dev/null | xargs --no-run-if-empty -I {} oc patch {} -n $ns --type merge -p '{"metadata":{"finalizers":[]}}' --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} 2>/dev/null || true; oc delete project $ns --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} || true; done`, + { + timeout: 180000, + failOnNonZeroExit: false + } + ); + + cy.log('Remove Cluster Observability Operator if exists'); + cy.executeAndDelete(`oc delete namespace ${DTP.namespace} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`); + + cy.log('Remove OpenTelemetry Operator if exists'); + cy.executeAndDelete(`oc delete namespace ${OTEL.namespace} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`); + + cy.log('Remove Tempo Operator if exists'); + cy.executeAndDelete(`oc delete namespace ${TEMPO.namespace} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`); + + // Only remove cluster-admin role if provider is not kube:admin + if (Cypress.env('LOGIN_IDP') !== 'kube:admin') { + cy.log('Remove cluster-admin role from user if exists.'); + cy.executeAndDelete( + `oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, + ); + } + } + // Only add cluster-admin role if provider is not kube:admin + if (Cypress.env('LOGIN_IDP') !== 'kube:admin') { + cy.adminCLI( + `oc adm policy add-cluster-role-to-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`, + ); + } + // Simplified login without OAuth URL complexity + cy.login( + Cypress.env('LOGIN_IDP'), + Cypress.env('LOGIN_USERNAME'), + Cypress.env('LOGIN_PASSWORD'), + ); + + if (Cypress.env('SKIP_COO_INSTALL')) { + cy.log('SKIP_COO_INSTALL is set. Skipping Cluster Observability Operator installation.'); + } else if (Cypress.env('COO_UI_INSTALL')) { + cy.log('COO_UI_INSTALL is set. COO, Tempo and OpenTelemetry operators will be installed from redhat-operators catalog source'); + cy.log('Install Cluster Observability Operator'); + operatorHubPage.installOperator(DTP.packageName, 'redhat-operators'); + cy.get('.co-clusterserviceversion-install__heading', { timeout: 5 * 60 * 1000 }).should(($el) => { + const text = $el.text(); + expect(text).to.satisfy((t) => + t.includes('ready for use') || t.includes('Operator installed successfully') + ); + }); + cy.log('Install OpenTelemetry Operator'); + operatorHubPage.installOperator(OTEL.packageName, 'redhat-operators'); + cy.get('.co-clusterserviceversion-install__heading', { timeout: 5 * 60 * 1000 }).should(($el) => { + const text = $el.text(); + expect(text).to.satisfy((t) => + t.includes('ready for use') || t.includes('Operator installed successfully') + ); + }); + cy.log('Install Tempo Operator'); + operatorHubPage.installOperator(TEMPO.packageName, 'redhat-operators'); + cy.get('.co-clusterserviceversion-install__heading', { timeout: 5 * 60 * 1000 }).should(($el) => { + const text = $el.text(); + expect(text).to.satisfy((t) => + t.includes('ready for use') || t.includes('Operator installed successfully') + ); + }); + } else if (Cypress.env('KONFLUX_COO_BUNDLE_IMAGE')) { + cy.log('KONFLUX_COO_BUNDLE_IMAGE is set. COO operator will be installed from Konflux bundle. Tempo and OpenTelemetry operators will be installed from redhat-operators catalog source'); + cy.log('Install Cluster Observability Operator'); + cy.exec( + `oc --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} apply -f ./fixtures/coo-imagecontentsourcepolicy.yaml` , + ); + cy.exec( + `oc create namespace ${DTP.namespace} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, + ); + cy.exec( + `oc label namespaces ${DTP.namespace} openshift.io/cluster-monitoring=true --overwrite=true --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, + ); + cy.exec( + `operator-sdk run bundle --timeout=10m --namespace ${DTP.namespace} ${Cypress.env('KONFLUX_COO_BUNDLE_IMAGE')} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} --verbose `, + { timeout: 6 * 60 * 1000 }, + ); + cy.log('Install OpenTelemetry Operator'); + operatorHubPage.installOperator(OTEL.packageName, 'redhat-operators'); + cy.get('.co-clusterserviceversion-install__heading', { timeout: 5 * 60 * 1000 }).should(($el) => { + const text = $el.text(); + expect(text).to.satisfy((t) => + t.includes('ready for use') || t.includes('Operator installed successfully') + ); + }); + cy.log('Install Tempo Operator'); + operatorHubPage.installOperator(TEMPO.packageName, 'redhat-operators'); + cy.get('.co-clusterserviceversion-install__heading', { timeout: 5 * 60 * 1000 }).should(($el) => { + const text = $el.text(); + expect(text).to.satisfy((t) => + t.includes('ready for use') || t.includes('Operator installed successfully') + ); + }); + } else if (Cypress.env('CUSTOM_COO_BUNDLE_IMAGE')) { + cy.log('CUSTOM_COO_BUNDLE_IMAGE is set. COO operator will be installed from custom built bundle. Tempo and OpenTelemetry operators will be installed from redhat-operators catalog source'); + cy.log('Install Cluster Observability Operator'); + cy.exec( + `oc --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} apply -f ./fixtures/coo-imagecontentsourcepolicy.yaml` , + ); + cy.exec( + `oc create namespace ${DTP.namespace} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, + ); + cy.exec( + `oc label namespaces ${DTP.namespace} openshift.io/cluster-monitoring=true --overwrite=true --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, + ); + cy.exec( + `operator-sdk run bundle --timeout=10m --namespace ${DTP.namespace} ${Cypress.env('CUSTOM_COO_BUNDLE_IMAGE')} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} --verbose `, + { timeout: 6 * 60 * 1000 }, + ); + cy.log('Install OpenTelemetry Operator'); + operatorHubPage.installOperator(OTEL.packageName, 'redhat-operators'); + cy.get('.co-clusterserviceversion-install__heading', { timeout: 5 * 60 * 1000 }).should(($el) => { + const text = $el.text(); + expect(text).to.satisfy((t) => + t.includes('ready for use') || t.includes('Operator installed successfully') + ); + }); + cy.log('Install Tempo Operator'); + operatorHubPage.installOperator(TEMPO.packageName, 'redhat-operators'); + cy.get('.co-clusterserviceversion-install__heading', { timeout: 5 * 60 * 1000 }).should(($el) => { + const text = $el.text(); + expect(text).to.satisfy((t) => + t.includes('ready for use') || t.includes('Operator installed successfully') + ); + }); + } else { + throw new Error('No CYPRESS env set for operator installation, check the README for more details.'); + } + + cy.log('Set Distributed Tracing Console Plugin image in operator CSV'); + if (Cypress.env('DT_CONSOLE_IMAGE')) { + cy.log('DT_CONSOLE_IMAGE is set. the image will be patched in COO operator CSV'); + cy.exec( + './fixtures/update-plugin-image.sh', + { + env: { + DT_CONSOLE_IMAGE: Cypress.env('DT_CONSOLE_IMAGE'), + KUBECONFIG: Cypress.env('KUBECONFIG_PATH'), + DTP_NAMESPACE: `${DTP.namespace}` + }, + timeout: 120000, + failOnNonZeroExit: true + } + ) .then((result) => { + expect(result.code).to.eq(0); + cy.log(`COO CSV updated successfully with Distributed Tracing Console Plugin image: ${result.stdout}`); + }); + } else { + cy.log('DT_CONSOLE_IMAGE is NOT set. Skipping patching the image in COO operator CSV.'); + } + + cy.log('Create Distributed Tracing UI Plugin instance.'); + cy.exec(`oc apply -f ./fixtures/tracing-ui-plugin.yaml --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`); + cy.exec( + `sleep 15 && oc wait --for=condition=Ready pods --selector=app.kubernetes.io/instance=distributed-tracing -n ${DTP.namespace} --timeout=60s --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, + { + timeout: 80000, + failOnNonZeroExit: true + } + ).then((result) => { + expect(result.code).to.eq(0); + cy.log(`Distributed Tracing Console plugin pod is now running in namespace: ${DTP.namespace}`); + }); + // Check for web console update alert, if not found, go to traces page + cy.get('body').then(($body) => { + if ($body.find('.pf-v5-c-alert, .pf-v6-c-alert').length > 0 && + $body.text().includes('Web console update is available')) { + cy.get('.pf-v5-c-alert, .pf-v6-c-alert') + .contains('Web console update is available') + .should('exist'); + } else { + cy.visit('/observe/traces'); + cy.url().should('include', '/observe/traces'); + } + }); + + }); + + after(() => { + if (Cypress.env('SKIP_COO_INSTALL')) { + cy.log('Delete Distributed Tracing UI Plugin instance.'); + cy.executeAndDelete( + `oc delete ${DTP.config.kind} ${DTP.config.name} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, + ); + + cy.log('Delete Chainsaw namespaces.'); + cy.exec( + `for ns in $(oc get projects -o name --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} | grep "chainsaw-" | sed 's|project.project.openshift.io/||'); do oc get opentelemetrycollectors.opentelemetry.io,tempostacks.tempo.grafana.com,tempomonolithics.tempo.grafana.com,pvc -n $ns -o name --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} 2>/dev/null | xargs --no-run-if-empty -I {} oc patch {} -n $ns --type merge -p '{"metadata":{"finalizers":[]}}' --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} 2>/dev/null || true; oc delete project $ns --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} || true; done`, + { + timeout: 300000, + failOnNonZeroExit: false + } + ); + + // Only remove cluster-admin role if provider is not kube:admin + if (Cypress.env('LOGIN_IDP') !== 'kube:admin') { + cy.log('Remove cluster-admin role from user.'); + cy.executeAndDelete( + `oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, + ); + } + } else { + cy.log('Delete Distributed Tracing UI Plugin instance.'); + cy.executeAndDelete( + `oc delete ${DTP.config.kind} ${DTP.config.name} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, + ); + + cy.log('Delete Chainsaw namespaces.'); + cy.exec( + `for ns in $(oc get projects -o name --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} | grep "chainsaw-" | sed 's|project.project.openshift.io/||'); do oc get opentelemetrycollectors.opentelemetry.io,tempostacks.tempo.grafana.com,tempomonolithics.tempo.grafana.com,pvc -n $ns -o name --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} 2>/dev/null | xargs --no-run-if-empty -I {} oc patch {} -n $ns --type merge -p '{"metadata":{"finalizers":[]}}' --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} 2>/dev/null || true; oc delete project $ns --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} || true; done`, + { + timeout: 300000, + failOnNonZeroExit: false + } + ); + + cy.log('Remove Cluster Observability Operator'); + cy.executeAndDelete(`oc delete namespace ${DTP.namespace} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`); + + cy.log('Remove OpenTelemetry Operator'); + cy.executeAndDelete(`oc delete namespace ${OTEL.namespace} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`); + + cy.log('Remove Tempo Operator'); + cy.executeAndDelete(`oc delete namespace ${TEMPO.namespace} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`); + + // Only remove cluster-admin role if provider is not kube:admin + if (Cypress.env('LOGIN_IDP') !== 'kube:admin') { + cy.log('Remove cluster-admin role from user.'); + cy.executeAndDelete( + `oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, + ); + } + } + }); + + // Tests start from here. + + it('Test Distributed Tracing UI plugin page without any Tempo instances', () => { + cy.log('Navigate to the observe/traces page'); + cy.visit('/observe/traces'); + + cy.log('Assert that the Traces page shows the empty state.'); + // Using PatternFly empty state helper with heading selector + cy.pfEmptyState().within(() => { + cy.get('h1, h2, h3, h4, h5, h6').should('contain.text', 'No Tempo instances yet'); + }); + + cy.log('Assert that the View documentation button is visible.'); + // Using PatternFly button helper + cy.pfButton('View documentation') + .should('be.visible') + .and('have.text', 'View documentation'); + + cy.log('Assert create a tempo instance toggle visibility and text.'); + // Using PatternFly menu toggle helper + cy.pfMenuToggle('Create a Tempo instance').should('be.visible'); + + cy.log('Click the toggle to show creation options.'); + cy.pfMenuToggle('Create a Tempo instance').click(); + + cy.log('Assert dropdown items for Tempo instance creation are visible.'); + // Using PatternFly menu item helpers + cy.pfMenuItem('Create a TempoStack instance') + .should('be.visible') + .and('have.text', 'Create a TempoStack instance'); + + cy.pfMenuItem('Create a TempoMonolithic instance') + .should('be.visible') + .and('have.text', 'Create a TempoMonolithic instance'); + }); + + it('Test Distributed Tracing UI plugin with Tempo instances and verify traces using user having cluster-admin role', function () { + cy.log('Create TempoStack and TempoMonolithic instances'); + cy.exec( + 'chainsaw test --config ./fixtures/.chainsaw.yaml --skip-delete ./fixtures/chainsaw-tests', + { + env: { + KUBECONFIG: Cypress.env('KUBECONFIG_PATH'), + }, + timeout: 1800000, + failOnNonZeroExit: true + } + ) .then((result) => { + expect(result.code).to.eq(0); + cy.log(`Chainsaw test ran successfully: ${result.stdout}`); + }); + cy.log('Navigate to the observe/traces page'); + cy.visit('/observe/traces'); + + cy.log('Assert traces in TempoStack instance.'); + cy.get(':nth-child(1) > .pf-v6-c-toolbar__item > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__button > .pf-v6-c-menu-toggle__controls > .pf-v6-c-menu-toggle__toggle-icon').click(); + cy.get(':nth-child(2) > .pf-v6-c-menu__item > .pf-v6-c-menu__item-main > .pf-v6-c-menu__item-text').click(); + cy.get(':nth-child(1) > :nth-child(2) > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__button').click(); + cy.get(':nth-child(2) > .pf-v6-c-menu__item > .pf-v6-c-menu__item-main > .pf-v6-c-menu__item-text').click(); + cy.get(':nth-child(2) > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__controls > .pf-v6-c-menu-toggle__toggle-icon').click(); + cy.get(':nth-child(1) > .pf-v6-c-menu__item > .pf-v6-c-menu__item-main > .pf-v6-c-menu__item-text').click(); + cy.get(':nth-child(1) > :nth-child(2) > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__button > .pf-v6-c-menu-toggle__controls > .pf-v6-c-menu-toggle__toggle-icon').click(); + cy.get(':nth-child(1) > .pf-v6-c-menu__item > .pf-v6-c-menu__item-main > .pf-v6-c-menu__item-text').click(); + cy.get('.pf-v6-c-toolbar__group > :nth-child(1) > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__controls > .pf-v6-c-menu-toggle__toggle-icon').click(); + cy.get(':nth-child(1) > .pf-v6-c-menu__item > .pf-v6-c-menu__item-main > .pf-v6-c-menu__item-text').click(); + cy.get('.pf-m-toggle-group > .pf-v6-c-toolbar__group > :nth-child(2) > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__button').click(); + cy.contains('.pf-v6-c-menu__item-text', 'http-rbac-1') + .closest('.pf-v6-c-menu__item') + .find('input[type="checkbox"]') + .check(); + cy.contains('.pf-v6-c-menu__item-text', 'http-rbac-2') + .closest('.pf-v6-c-menu__item') + .find('input[type="checkbox"]') + .check(); + cy.contains('.pf-v6-c-menu__item-text', 'grpc-rbac-1') + .closest('.pf-v6-c-menu__item') + .find('input[type="checkbox"]') + .check(); + cy.contains('.pf-v6-c-menu__item-text', 'grpc-rbac-2') + .closest('.pf-v6-c-menu__item') + .find('input[type="checkbox"]') + .check(); + cy.get('.MuiDataGrid-row--firstVisible > [data-field="name"] > .MuiBox-root > .MuiTypography-root').click(); + cy.contains('div', 'okey-dokey').click({ force: true }); + cy.get('.css-1bmckj4').then(($el) => { + cy.log(`Actual text in .css-1bmckj4 (TempoStack): ${$el.text()}`); + expect($el.text()).to.satisfy((text) => + text === 'http-rbac-1' || + text === 'http-rbac-2' || + text === 'grpc-rbac-1' || + text === 'grpc-rbac-2' + ); + }); + cy.get('.MuiTypography-h2').should('have.text', 'okey-dokey'); + cy.get('.MuiTabs-list > .MuiButtonBase-root').should('be.visible'); + + // Check for net.peer.ip + cy.contains('.MuiTypography-h5', 'net.peer.ip').next('.MuiTypography-body1').should('have.text', '1.2.3.4'); + + // Check for peer.service + cy.contains('.MuiTypography-h5', 'peer.service').next('.MuiTypography-body1').should('have.text', 'telemetrygen-client'); + + // Check for k8s.container.name if present + cy.get('body').then(($body) => { + if ($body.find('.MuiTypography-h5:contains("k8s.container.name")').length > 0) { + cy.contains('.MuiTypography-h5', 'k8s.container.name').next('.MuiTypography-body1').should('have.text', 'telemetrygen'); + } + }); + + // Check for k8s.namespace.name if present + cy.get('body').then(($body) => { + if ($body.find('.MuiTypography-h5:contains("k8s.namespace.name")').length > 0) { + cy.contains('.MuiTypography-h5', 'k8s.namespace.name').next('.MuiTypography-body1').then(($el) => { + cy.log(`Actual text in k8s.namespace.name (TempoStack): ${$el.text()}`); + expect($el.text()).to.satisfy((text) => + text === 'chainsaw-test-rbac-1' || + text === 'chainsaw-test-rbac-2' || + text === 'chainsaw-mono-rbac-1' || + text === 'chainsaw-mono-rbac-2' + ); + }); + } + }); + + // Check for service.name + cy.contains('.MuiTypography-h5', 'service.name').next('.MuiTypography-body1').then(($el) => { + cy.log(`Actual text in service.name (TempoStack): ${$el.text()}`); + expect($el.text()).to.satisfy((text) => + text === 'http-rbac-1' || + text === 'http-rbac-2' || + text === 'grpc-rbac-1' || + text === 'grpc-rbac-2' + ); + }); + cy.get('.pf-v6-c-breadcrumb__list > :nth-child(1) > a').click(); + + cy.log('Assert traces in TempoMonolithic instance.'); + cy.get(':nth-child(1) > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__button').click(); + cy.get(':nth-child(1) > .pf-v6-c-menu__item > .pf-v6-c-menu__item-main > .pf-v6-c-menu__item-text').click(); + cy.get(':nth-child(2) > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle').click(); + cy.get(':nth-child(3) > .pf-v6-c-menu__item > .pf-v6-c-menu__item-main > .pf-v6-c-menu__item-text').click(); + cy.get('.pf-v6-c-form__group-control > .pf-v6-c-button > .pf-v6-c-button__text').click(); + cy.get('.pf-m-toggle-group > .pf-m-action-group > .pf-v6-c-toolbar__item > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-button > .pf-v6-c-button__text').click(); + cy.get('.MuiDataGrid-row--firstVisible > [data-field="name"] > .MuiBox-root > .MuiTypography-root').click(); + cy.contains('div', 'okey-dokey').click({ force: true }); + cy.get('.css-1bmckj4').then(($el) => { + cy.log(`Actual text in .css-1bmckj4 (TempoMonolithic): ${$el.text()}`); + expect($el.text()).to.satisfy((text) => + text === 'http-rbac-1' || + text === 'http-rbac-2' || + text === 'grpc-rbac-1' || + text === 'grpc-rbac-2' + ); + }); + cy.get('.MuiTypography-h2').should('have.text', 'okey-dokey'); + cy.get('.MuiTabs-list > .MuiButtonBase-root').should('be.visible'); + // Check for span details with more flexible approach + // Check for net.peer.ip + cy.contains('.MuiTypography-h5', 'net.peer.ip').next('.MuiTypography-body1').should('have.text', '1.2.3.4'); + + // Check for peer.service + cy.contains('.MuiTypography-h5', 'peer.service').next('.MuiTypography-body1').should('have.text', 'telemetrygen-client'); + + // Check for k8s.container.name if present + cy.get('body').then(($body) => { + if ($body.find('.MuiTypography-h5:contains("k8s.container.name")').length > 0) { + cy.contains('.MuiTypography-h5', 'k8s.container.name').next('.MuiTypography-body1').should('have.text', 'telemetrygen'); + } + }); + + // Check for k8s.namespace.name if present + cy.get('body').then(($body) => { + if ($body.find('.MuiTypography-h5:contains("k8s.namespace.name")').length > 0) { + cy.contains('.MuiTypography-h5', 'k8s.namespace.name').next('.MuiTypography-body1').then(($el) => { + cy.log(`Actual text in k8s.namespace.name (TempoMonolithic): ${$el.text()}`); + expect($el.text()).to.satisfy((text) => + text === 'chainsaw-test-rbac-1' || + text === 'chainsaw-test-rbac-2' || + text === 'chainsaw-mono-rbac-1' || + text === 'chainsaw-mono-rbac-2' + ); + }); + } + }); + + // Check for service.name + cy.contains('.MuiTypography-h5', 'service.name').next('.MuiTypography-body1').then(($el) => { + cy.log(`Actual text in service.name (TempoMonolithic): ${$el.text()}`); + expect($el.text()).to.satisfy((text) => + text === 'http-rbac-1' || + text === 'http-rbac-2' || + text === 'grpc-rbac-1' || + text === 'grpc-rbac-2' + ); + }); + cy.get('.pf-v6-c-breadcrumb__list > :nth-child(1) > a').click(); + }); + +}); diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/01-assert.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/01-assert.yaml deleted file mode 100644 index 5e8f07d..0000000 --- a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/01-assert.yaml +++ /dev/null @@ -1,94 +0,0 @@ -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: tempo-mmo -status: - readyReplicas: 1 - ---- -apiVersion: v1 -kind: Pod -metadata: - name: tempo-mmo-0 -status: - containerStatuses: - - name: jaeger-query - ready: true - started: true - - name: tempo - ready: true - started: true - - name: tempo-gateway - ready: true - started: true - - name: tempo-gateway-opa - ready: true - started: true - - name: tempo-query - ready: true - started: true - phase: Running - ---- -apiVersion: v1 -kind: Service -metadata: - name: tempo-mmo-gateway -spec: - ports: - - name: public - port: 8080 - protocol: TCP - targetPort: public - - name: internal - port: 8081 - protocol: TCP - targetPort: internal - - name: otlp-grpc - port: 4317 - protocol: TCP - targetPort: grpc-public - ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/component: gateway - app.kubernetes.io/instance: mmo - app.kubernetes.io/managed-by: tempo-operator - app.kubernetes.io/name: tempo-monolithic - app.kubernetes.io/namespace: chainsaw-monolithic-multitenancy - name: tempo-mmo-gateway-chainsaw-monolithic-multitenancy -rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - app.kubernetes.io/component: gateway - app.kubernetes.io/instance: mmo - app.kubernetes.io/managed-by: tempo-operator - app.kubernetes.io/name: tempo-monolithic - app.kubernetes.io/namespace: chainsaw-monolithic-multitenancy - name: tempo-mmo-gateway-chainsaw-monolithic-multitenancy -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: tempo-mmo-gateway-chainsaw-monolithic-multitenancy -subjects: -- kind: ServiceAccount - name: tempo-mmo - namespace: chainsaw-monolithic-multitenancy diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/01-install-tempo.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/01-install-tempo.yaml deleted file mode 100644 index 4e1f0be..0000000 --- a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/01-install-tempo.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: tempo.grafana.com/v1alpha1 -kind: TempoMonolithic -metadata: - name: mmo -spec: - jaegerui: - enabled: true - route: - enabled: true - multitenancy: - enabled: true - mode: openshift - authentication: - - tenantName: dev - tenantId: "1610b0c3-c509-4592-a256-a1871353dbfa" - - tenantName: prod - tenantId: "1610b0c3-c509-4592-a256-a1871353dbfb" ---- - -# Grant the dev-collector Service Account permission to write traces to the 'dev' tenant -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: allow-write-traces-dev-tenant -rules: -- apiGroups: [tempo.grafana.com] - resources: [dev] # tenantName - resourceNames: [traces] - verbs: [create] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: allow-write-traces-dev-tenant -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: allow-write-traces-dev-tenant -subjects: -- kind: ServiceAccount - name: dev-collector - namespace: chainsaw-monolithic-multitenancy ---- - -# Grant the default Service Account (used by the verify-traces pod) permission to read traces of the 'dev' tenant -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: allow-read-traces-dev-tenant -rules: -- apiGroups: [tempo.grafana.com] - resources: [dev] # tenantName - resourceNames: [traces] - verbs: [get] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: allow-read-traces-dev-tenant -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: allow-read-traces-dev-tenant -subjects: -- kind: ServiceAccount - name: default - namespace: chainsaw-monolithic-multitenancy ---- -# Grant the default ServiceAccount (used by the verify-traces pod) view permissions of the chainsaw-monolithic-multitenancy namespace. -# If the ServiceAccount cannot access any namespaces, every 'get' request will be denied: -# https://github.com/observatorium/opa-openshift/pull/18/files -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: view -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: view -subjects: -- kind: ServiceAccount - name: default - namespace: chainsaw-monolithic-multitenancy diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/02-install-otelcol.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/02-install-otelcol.yaml deleted file mode 100644 index 6442bfc..0000000 --- a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/02-install-otelcol.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: opentelemetry.io/v1alpha1 -kind: OpenTelemetryCollector -metadata: - name: dev -spec: - config: | - extensions: - bearertokenauth: - filename: /var/run/secrets/kubernetes.io/serviceaccount/token - - receivers: - otlp/grpc: - protocols: - grpc: - otlp/http: - protocols: - http: - - exporters: - otlp: - endpoint: tempo-mmo-gateway.chainsaw-monolithic-multitenancy.svc.cluster.local:4317 - tls: - ca_file: /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt - auth: - authenticator: bearertokenauth - headers: - X-Scope-OrgID: dev # tenantName - otlphttp: - endpoint: https://tempo-mmo-gateway.chainsaw-monolithic-multitenancy.svc.cluster.local:8080/api/traces/v1/dev - tls: - ca_file: /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt - auth: - authenticator: bearertokenauth - headers: - X-Scope-OrgID: dev # tenantName - - service: - telemetry: - logs: - level: "DEBUG" - development: true - encoding: "json" - extensions: [bearertokenauth] - pipelines: - traces/grpc: - receivers: [otlp/grpc] - exporters: [otlp] - traces/http: - receivers: [otlp/http] - exporters: [otlphttp] diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/03-assert.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/03-assert.yaml deleted file mode 100644 index 1fafdf1..0000000 --- a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/03-assert.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: generate-traces-grpc -status: - succeeded: 1 ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: generate-traces-http -status: - succeeded: 1 \ No newline at end of file diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/03-generate-traces.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/03-generate-traces.yaml deleted file mode 100644 index 15d57ce..0000000 --- a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/03-generate-traces.yaml +++ /dev/null @@ -1,36 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: generate-traces-grpc -spec: - template: - spec: - containers: - - name: telemetrygen - image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.92.0 - args: - - traces - - --otlp-endpoint=dev-collector:4317 - - --service=grpc - - --otlp-insecure - - --traces=10 - restartPolicy: Never ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: generate-traces-http -spec: - template: - spec: - containers: - - name: telemetrygen - image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.92.0 - args: - - traces - - --otlp-endpoint=dev-collector:4318 - - --otlp-http - - --otlp-insecure - - --service=http - - --traces=10 - restartPolicy: Never \ No newline at end of file diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/04-assert.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/04-assert.yaml deleted file mode 100644 index ff19437..0000000 --- a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/04-assert.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: verify-traces-jaegerui-grpc -status: - succeeded: 1 ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: verify-traces-traceql-grpc -status: - succeeded: 1 ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: verify-traces-jaegerui-http -status: - succeeded: 1 ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: verify-traces-traceql-http -status: - succeeded: 1 \ No newline at end of file diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/04-verify-traces.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/04-verify-traces.yaml deleted file mode 100644 index 40e0b4d..0000000 --- a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/04-verify-traces.yaml +++ /dev/null @@ -1,107 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: verify-traces-jaegerui-grpc -spec: - template: - spec: - containers: - - name: verify-traces - image: ghcr.io/grafana/tempo-operator/test-utils:main - command: ["/bin/bash", "-eux", "-c"] - args: - - | - curl -vG \ - --header "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \ - --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ - https://tempo-mmo-gateway.chainsaw-monolithic-multitenancy.svc:8080/api/traces/v1/dev/api/traces \ - --data-urlencode "service=grpc" \ - | tee /tmp/jaeger.out - - num_traces=$(jq ".data | length" /tmp/jaeger.out) - if [[ "$num_traces" != "10" ]]; then - echo && echo "The Jaeger API returned $num_traces instead of 10 traces." - exit 1 - fi - restartPolicy: Never ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: verify-traces-traceql-grpc -spec: - template: - spec: - containers: - - name: verify-traces - image: ghcr.io/grafana/tempo-operator/test-utils:main - command: ["/bin/bash", "-eux", "-c"] - args: - - | - curl -sS -G \ - --header "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \ - --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ - --data-urlencode 'q={ resource.service.name="grpc" }' \ - https://tempo-mmo-gateway.chainsaw-monolithic-multitenancy.svc:8080/api/traces/v1/dev/tempo/api/search \ - | tee /tmp/tempo.out - - num_traces=$(jq ".traces | length" /tmp/tempo.out) - if [[ "$num_traces" != "10" ]]; then - echo && echo "The Tempo API returned $num_traces instead of 10 traces." - exit 1 - fi - restartPolicy: Never ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: verify-traces-jaegerui-http -spec: - template: - spec: - containers: - - name: verify-traces - image: ghcr.io/grafana/tempo-operator/test-utils:main - command: ["/bin/bash", "-eux", "-c"] - args: - - | - curl -vG \ - --header "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \ - --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ - https://tempo-mmo-gateway.chainsaw-monolithic-multitenancy.svc:8080/api/traces/v1/dev/api/traces \ - --data-urlencode "service=http" \ - | tee /tmp/jaeger.out - - num_traces=$(jq ".data | length" /tmp/jaeger.out) - if [[ "$num_traces" != "10" ]]; then - echo && echo "The Jaeger API returned $num_traces instead of 10 traces." - exit 1 - fi - restartPolicy: Never ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: verify-traces-traceql-http -spec: - template: - spec: - containers: - - name: verify-traces - image: ghcr.io/grafana/tempo-operator/test-utils:main - command: ["/bin/bash", "-eux", "-c"] - args: - - | - curl -sS -G \ - --header "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \ - --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ - --data-urlencode 'q={ resource.service.name="http" }' \ - https://tempo-mmo-gateway.chainsaw-monolithic-multitenancy.svc:8080/api/traces/v1/dev/tempo/api/search \ - | tee /tmp/tempo.out - - num_traces=$(jq ".traces | length" /tmp/tempo.out) - if [[ "$num_traces" != "10" ]]; then - echo && echo "The Tempo API returned $num_traces instead of 10 traces." - exit 1 - fi - restartPolicy: Never diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/chainsaw-test.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/chainsaw-test.yaml deleted file mode 100644 index 1de146c..0000000 --- a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/chainsaw-test.yaml +++ /dev/null @@ -1,43 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/kyverno/chainsaw/main/.schemas/json/test-chainsaw-v1alpha1.json -apiVersion: chainsaw.kyverno.io/v1alpha1 -kind: Test -metadata: - name: monolithic-multitenancy-openshift -spec: - # this test must use a known namespace because of the CN field of the TLS certificate and the ClusterRoleBinding - namespace: chainsaw-monolithic-multitenancy - steps: - - name: step-01 - try: - - apply: - file: 01-install-tempo.yaml - - assert: - file: 01-assert.yaml - - name: step-02 - try: - - apply: - file: 02-install-otelcol.yaml - - assert: - file: 02-assert.yaml - - name: step-03 - try: - - apply: - file: 03-generate-traces.yaml - - assert: - file: 03-assert.yaml - - name: step-04 - try: - - apply: - file: 04-verify-traces.yaml - - assert: - file: 04-assert.yaml - catch: - - events: {} - - podLogs: - selector: job-name=verify-traces-jaegerui - tail: 50 - - podLogs: - selector: job-name=verify-traces-traceql - tail: 50 - - podLogs: - selector: app.kubernetes.io/name=tempo-monolithic diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/01-assert.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/01-assert.yaml new file mode 100644 index 0000000..3cbeb36 --- /dev/null +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/01-assert.yaml @@ -0,0 +1,44 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: tempo-mmo-rbac +status: + readyReplicas: 1 + +--- +apiVersion: v1 +kind: Pod +metadata: + name: tempo-mmo-rbac-0 +status: + containerStatuses: + - name: tempo + ready: true + started: true + - name: tempo-gateway + ready: true + started: true + - name: tempo-gateway-opa + ready: true + started: true + phase: Running + +--- +apiVersion: v1 +kind: Service +metadata: + name: tempo-mmo-rbac-gateway +spec: + ports: + - name: public + port: 8080 + protocol: TCP + targetPort: public + - name: internal + port: 8081 + protocol: TCP + targetPort: internal + - name: otlp-grpc + port: 4317 + protocol: TCP + targetPort: grpc-public diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/01-install-tempo.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/01-install-tempo.yaml new file mode 100644 index 0000000..88925f5 --- /dev/null +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/01-install-tempo.yaml @@ -0,0 +1,52 @@ +apiVersion: tempo.grafana.com/v1alpha1 +kind: TempoMonolithic +metadata: + name: mmo-rbac +spec: + query: + rbac: + enabled: true + multitenancy: + enabled: true + mode: openshift + authentication: + - tenantName: dev + tenantId: "1610b0c3-c509-4592-a256-a1871353dbfa" + - tenantName: prod + tenantId: "1610b0c3-c509-4592-a256-a1871353dbfb" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: allow-read-traces-dev-tenant-rbac +rules: +- apiGroups: [tempo.grafana.com] + resources: [dev] + resourceNames: [traces] + verbs: [get] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: allow-read-traces-dev-tenant-rbac +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: allow-read-traces-dev-tenant-rbac +subjects: + - kind: Group + apiGroup: rbac.authorization.k8s.io + name: system:authenticated +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: view +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: view +subjects: +- kind: ServiceAccount + name: default + namespace: chainsaw-mmo-rbac diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/02-assert.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/02-assert.yaml similarity index 82% rename from tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/02-assert.yaml rename to tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/02-assert.yaml index 8e4e81a..4062bd9 100644 --- a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-openshift/02-assert.yaml +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/02-assert.yaml @@ -11,7 +11,7 @@ apiVersion: v1 kind: Service metadata: name: dev-collector - namespace: chainsaw-monolithic-multitenancy + namespace: chainsaw-mmo-rbac spec: ports: - appProtocol: grpc @@ -26,6 +26,6 @@ spec: targetPort: 4318 selector: app.kubernetes.io/component: opentelemetry-collector - app.kubernetes.io/instance: chainsaw-monolithic-multitenancy.dev + app.kubernetes.io/instance: chainsaw-mmo-rbac.dev app.kubernetes.io/managed-by: opentelemetry-operator app.kubernetes.io/part-of: opentelemetry \ No newline at end of file diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/02-install-otelcol.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/02-install-otelcol.yaml new file mode 100644 index 0000000..0ce5aed --- /dev/null +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/02-install-otelcol.yaml @@ -0,0 +1,132 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: otel-collector-deployment + namespace: chainsaw-mmo-rbac + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: chainsaw-mono-rbac-clusterrole +rules: +- apiGroups: [""] + resources: ["pods", "namespaces", "nodes"] + verbs: ["get", "watch", "list"] +- apiGroups: ["apps"] + resources: ["replicasets"] + verbs: ["get", "list", "watch"] +- apiGroups: ["extensions"] + resources: ["replicasets"] + verbs: ["get", "list", "watch"] + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: chainsaw-mono-rbac-clusterrole-binding +subjects: +- kind: ServiceAccount + name: otel-collector-deployment + namespace: chainsaw-mmo-rbac +roleRef: + kind: ClusterRole + name: chainsaw-mono-rbac-clusterrole + apiGroup: rbac.authorization.k8s.io + +--- +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: dev +spec: + serviceAccount: otel-collector-deployment + config: | + extensions: + bearertokenauth: + filename: /var/run/secrets/kubernetes.io/serviceaccount/token + + receivers: + otlp/grpc: + protocols: + grpc: + otlp/http: + protocols: + http: + + processors: + k8sattributes: + extract: + metadata: + - k8s.pod.name + - k8s.pod.uid + - k8s.deployment.name + - k8s.namespace.name + - k8s.node.name + pod_association: + - sources: + - from: resource_attribute + name: k8s.pod.ip + - sources: + - from: connection + + exporters: + debug: + verbosity: detailed + otlp: + endpoint: tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc.cluster.local:4317 + tls: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt + auth: + authenticator: bearertokenauth + headers: + X-Scope-OrgID: dev # tenantName + otlphttp: + endpoint: https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc.cluster.local:8080/api/traces/v1/dev + tls: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt + auth: + authenticator: bearertokenauth + headers: + X-Scope-OrgID: dev # tenantName + + service: + telemetry: + logs: + level: "DEBUG" + development: true + extensions: [bearertokenauth] + pipelines: + traces/grpc: + receivers: [otlp/grpc] + processors: [k8sattributes] + exporters: [otlp,debug] + traces/http: + receivers: [otlp/http] + processors: [k8sattributes] + exporters: [otlphttp,debug] + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: allow-write-traces-dev-tenant-rbac +rules: +- apiGroups: [tempo.grafana.com] + resources: [dev] + resourceNames: [traces] + verbs: [create] + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: allow-write-traces-dev-tenant-rbac +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: allow-write-traces-dev-tenant-rbac +subjects: +- kind: ServiceAccount + name: otel-collector-deployment + namespace: chainsaw-mmo-rbac diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-create-sas.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-create-sas.yaml new file mode 100644 index 0000000..28ac05b --- /dev/null +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-create-sas.yaml @@ -0,0 +1,42 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: tempo-rbac-sa-1 + namespace: chainsaw-mono-rbac-1 + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: tempo-rbac-sa-2 + namespace: chainsaw-mono-rbac-2 + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: chainsaw-mono-rbac-1-admin + namespace: chainsaw-mono-rbac-1 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: admin +subjects: +- kind: ServiceAccount + name: tempo-rbac-sa-1 + namespace: chainsaw-mono-rbac-1 + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: chainsaw-mono-rbac-2-admin + namespace: chainsaw-mono-rbac-2 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: admin +subjects: +- kind: ServiceAccount + name: tempo-rbac-sa-2 + namespace: chainsaw-mono-rbac-2 \ No newline at end of file diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-kubeadmin-traces-verify.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-kubeadmin-traces-verify.yaml new file mode 100644 index 0000000..080b35a --- /dev/null +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-kubeadmin-traces-verify.yaml @@ -0,0 +1,15 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: verify-traces-kubeadmin-grpc + namespace: chainsaw-mmo-rbac +status: + succeeded: 1 +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: verify-traces-kubeadmin-http + namespace: chainsaw-mmo-rbac +status: + succeeded: 1 \ No newline at end of file diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-tempo-rbac-sa-1-traces-gen.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-tempo-rbac-sa-1-traces-gen.yaml new file mode 100644 index 0000000..06d42a2 --- /dev/null +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-tempo-rbac-sa-1-traces-gen.yaml @@ -0,0 +1,15 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-traces-grpc-sa-1 + namespace: chainsaw-mono-rbac-1 +status: + succeeded: 1 +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-traces-http-sa-1 + namespace: chainsaw-mono-rbac-1 +status: + succeeded: 1 \ No newline at end of file diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-tempo-rbac-sa-1-traces-verify.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-tempo-rbac-sa-1-traces-verify.yaml new file mode 100644 index 0000000..0ec3a1e --- /dev/null +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-tempo-rbac-sa-1-traces-verify.yaml @@ -0,0 +1,15 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: verify-traces-traceql-grpc-sa-1 + namespace: chainsaw-mono-rbac-1 +status: + succeeded: 1 +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: verify-traces-traceql-http-sa-1 + namespace: chainsaw-mono-rbac-1 +status: + succeeded: 1 \ No newline at end of file diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-tempo-rbac-sa-2-traces-gen.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-tempo-rbac-sa-2-traces-gen.yaml new file mode 100644 index 0000000..8079cc8 --- /dev/null +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-tempo-rbac-sa-2-traces-gen.yaml @@ -0,0 +1,15 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-traces-grpc-sa-2 + namespace: chainsaw-mono-rbac-2 +status: + succeeded: 1 +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-traces-http-sa-2 + namespace: chainsaw-mono-rbac-2 +status: + succeeded: 1 \ No newline at end of file diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/chainsaw-test.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/chainsaw-test.yaml new file mode 100644 index 0000000..5fcc0ac --- /dev/null +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/chainsaw-test.yaml @@ -0,0 +1,49 @@ +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + name: monolithic-multitenancy-rbac +spec: + namespace: chainsaw-mmo-rbac + steps: + - name: step-01 + try: + - apply: + file: 01-install-tempo.yaml + - assert: + file: 01-assert.yaml + - name: step-02 + try: + - apply: + file: 02-install-otelcol.yaml + - assert: + file: 02-assert.yaml + - name: Create non-admin SAs with namespace level access + try: + - apply: + file: create-SAs-with-namespace-access.yaml + - assert: + file: assert-create-sas.yaml + - name: Generate traces from namespace chainsaw-mono-rbac-1 + try: + - apply: + file: tempo-rbac-sa-1-traces-gen.yaml + - assert: + file: assert-tempo-rbac-sa-1-traces-gen.yaml + - name: Generate traces from namespace chainsaw-mono-rbac-2 + try: + - apply: + file: tempo-rbac-sa-2-traces-gen.yaml + - assert: + file: assert-tempo-rbac-sa-2-traces-gen.yaml + - name: Assert tracess using RBAC + try: + - apply: + file: tempo-rbac-sa-1-traces-verify.yaml + - assert: + file: assert-tempo-rbac-sa-1-traces-verify.yaml + - name: Verify kubeadmin can view traces from all projects + try: + - apply: + file: kubeadmin-traces-verify.yaml + - assert: + file: assert-kubeadmin-traces-verify.yaml diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/create-SAs-with-namespace-access.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/create-SAs-with-namespace-access.yaml new file mode 100644 index 0000000..ecd013d --- /dev/null +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/create-SAs-with-namespace-access.yaml @@ -0,0 +1,91 @@ +apiVersion: project.openshift.io/v1 +kind: Project +metadata: + name: chainsaw-mono-rbac-1 +spec: {} + +--- +apiVersion: project.openshift.io/v1 +kind: Project +metadata: + name: chainsaw-mono-rbac-2 +spec: {} + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: tempo-rbac-sa-1 + namespace: chainsaw-mono-rbac-1 + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: tempo-rbac-sa-2 + namespace: chainsaw-mono-rbac-2 + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: tempo-rbac-cluster-admin + namespace: chainsaw-mmo-rbac + +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: chainsaw-mono-rbac-1-admin + namespace: chainsaw-mono-rbac-1 +subjects: + - kind: ServiceAccount + name: tempo-rbac-sa-1 + namespace: chainsaw-mono-rbac-1 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: admin + +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: chainsaw-mono-rbac-2-admin + namespace: chainsaw-mono-rbac-2 +subjects: + - kind: ServiceAccount + name: tempo-rbac-sa-2 + namespace: chainsaw-mono-rbac-2 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: admin + +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: tempo-rbac-cluster-admin-binding-monolithic +subjects: + - kind: ServiceAccount + name: tempo-rbac-cluster-admin + namespace: chainsaw-mmo-rbac +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin + +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: chainsaw-test-rbac-1-testuser + namespace: chainsaw-mono-rbac-1 +subjects: + - kind: User + name: testuser-1 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: admin diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/kubeadmin-traces-verify.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/kubeadmin-traces-verify.yaml new file mode 100644 index 0000000..a5d31da --- /dev/null +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/kubeadmin-traces-verify.yaml @@ -0,0 +1,201 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: verify-traces-kubeadmin-grpc + namespace: chainsaw-mmo-rbac +spec: + template: + spec: + serviceAccountName: tempo-rbac-cluster-admin + containers: + - name: verify-traces + image: ghcr.io/grafana/tempo-operator/test-utils:main + command: + - /bin/bash + - -eux + - -c + args: + - | + # Get the cluster-admin service account token + token=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) + + # First, verify traces from chainsaw-mono-rbac-1 (grpc-rbac-1 service) + curl \ + -G \ + --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="grpc-rbac-1" }' \ + | tee /tmp/jaeger-rbac-1.out + num_traces=$(jq ".traces | length" /tmp/jaeger-rbac-1.out) + if [[ "$num_traces" != "2" ]]; then + echo && echo "The Jaeger API returned $num_traces instead of 2 traces for grpc-rbac-1." + exit 1 + fi + + echo "Fetch the first trace ID and store it in a variable" + traceID=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="grpc-rbac-1" }' | jq -r '.traces[0].traceID') + + echo "Use the trace ID to fetch the complete trace" + traceOutput=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/traces/$traceID) + + echo "Check for the strings in the trace output - cluster-admin should see complete traces" + stringsToSearch=( + "\"key\":\"net.peer.ip\"" + "\"stringValue\":\"1.2.3.4\"" + "\"key\":\"peer.service\"" + "\"stringValue\":\"telemetrygen-client\"" + "\"key\":\"k8s.pod.ip\"" + "\"key\":\"k8s.container.name\"" + ) + for searchString in "${stringsToSearch[@]}"; do + if echo "$traceOutput" | grep -q "$searchString"; then + echo "Cluster-admin: Trace output for service grpc-rbac-1 contains: $searchString" + else + echo "Cluster-admin: Trace output for service grpc-rbac-1 does not contain: $searchString" + exit 1 + fi + done + + # Now verify traces from chainsaw-mono-rbac-2 (grpc-rbac-2 service) + # cluster-admin should be able to see complete traces from this project too + curl \ + -G \ + --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="grpc-rbac-2" }' \ + | tee /tmp/jaeger-rbac-2.out + num_traces=$(jq ".traces | length" /tmp/jaeger-rbac-2.out) + if [[ "$num_traces" != "2" ]]; then + echo && echo "The Jaeger API returned $num_traces instead of 2 traces for grpc-rbac-2." + exit 1 + fi + + echo "Fetch the first trace ID and store it in a variable" + traceID=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="grpc-rbac-2" }' | jq -r '.traces[0].traceID') + + echo "Use the trace ID to fetch the complete trace" + traceOutput=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/traces/$traceID) + + echo "Check for the strings in the trace output - cluster-admin should see complete traces" + for searchString in "${stringsToSearch[@]}"; do + if echo "$traceOutput" | grep -q "$searchString"; then + echo "Cluster-admin: Trace output for service grpc-rbac-2 contains: $searchString" + else + echo "Cluster-admin: Trace output for service grpc-rbac-2 does not contain: $searchString" + exit 1 + fi + done + restartPolicy: Never +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: verify-traces-kubeadmin-http + namespace: chainsaw-mmo-rbac +spec: + template: + spec: + serviceAccountName: tempo-rbac-cluster-admin + containers: + - name: verify-traces + image: ghcr.io/grafana/tempo-operator/test-utils:main + command: + - /bin/bash + - -eux + - -c + args: + - | + # Get the cluster-admin service account token + token=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) + + # First, verify traces from chainsaw-mono-rbac-1 (http-rbac-1 service) + curl \ + -G \ + --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="http-rbac-1" }' \ + | tee /tmp/jaeger-rbac-1.out + num_traces=$(jq ".traces | length" /tmp/jaeger-rbac-1.out) + if [[ "$num_traces" != "2" ]]; then + echo && echo "The Jaeger API returned $num_traces instead of 2 traces for http-rbac-1." + exit 1 + fi + + echo "Fetch the first trace ID and store it in a variable" + traceID=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="http-rbac-1" }' | jq -r '.traces[0].traceID') + + echo "Use the trace ID to fetch the complete trace" + traceOutput=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/traces/$traceID) + + echo "Check for the strings in the trace output - cluster-admin should see complete traces" + stringsToSearch=( + "\"key\":\"net.peer.ip\"" + "\"stringValue\":\"1.2.3.4\"" + "\"key\":\"peer.service\"" + "\"stringValue\":\"telemetrygen-client\"" + "\"key\":\"k8s.pod.ip\"" + "\"key\":\"k8s.container.name\"" + ) + for searchString in "${stringsToSearch[@]}"; do + if echo "$traceOutput" | grep -q "$searchString"; then + echo "Cluster-admin: Trace output for service http-rbac-1 contains: $searchString" + else + echo "Cluster-admin: Trace output for service http-rbac-1 does not contain: $searchString" + exit 1 + fi + done + + # Now verify traces from chainsaw-mono-rbac-2 (http-rbac-2 service) + # cluster-admin should be able to see complete traces from this project too + curl \ + -G \ + --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="http-rbac-2" }' \ + | tee /tmp/jaeger-rbac-2.out + num_traces=$(jq ".traces | length" /tmp/jaeger-rbac-2.out) + if [[ "$num_traces" != "2" ]]; then + echo && echo "The Jaeger API returned $num_traces instead of 2 traces for http-rbac-2." + exit 1 + fi + + echo "Fetch the first trace ID and store it in a variable" + traceID=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="http-rbac-2" }' | jq -r '.traces[0].traceID') + + echo "Use the trace ID to fetch the complete trace" + traceOutput=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/traces/$traceID) + + echo "Check for the strings in the trace output - cluster-admin should see complete traces" + for searchString in "${stringsToSearch[@]}"; do + if echo "$traceOutput" | grep -q "$searchString"; then + echo "Cluster-admin: Trace output for service http-rbac-2 contains: $searchString" + else + echo "Cluster-admin: Trace output for service http-rbac-2 does not contain: $searchString" + exit 1 + fi + done + restartPolicy: Never \ No newline at end of file diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-1-traces-gen.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-1-traces-gen.yaml new file mode 100644 index 0000000..f8bb17f --- /dev/null +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-1-traces-gen.yaml @@ -0,0 +1,42 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-traces-grpc-sa-1 + namespace: chainsaw-mono-rbac-1 +spec: + template: + spec: + containers: + - name: telemetrygen + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.92.0 + args: + - traces + - --otlp-endpoint=dev-collector.chainsaw-mmo-rbac.svc:4317 + - --service=grpc-rbac-1 + - --otlp-insecure + - --traces=2 + - --otlp-attributes=k8s.container.name="telemetrygen" + - --otlp-attributes=k8s.namespace.name="chainsaw-mono-rbac-1" + restartPolicy: Never +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-traces-http-sa-1 + namespace: chainsaw-mono-rbac-1 +spec: + template: + spec: + containers: + - name: telemetrygen + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.92.0 + args: + - traces + - --otlp-endpoint=dev-collector.chainsaw-mmo-rbac.svc:4318 + - --otlp-http + - --otlp-insecure + - --service=http-rbac-1 + - --traces=2 + - --otlp-attributes=k8s.container.name="telemetrygen" + - --otlp-attributes=k8s.namespace.name="chainsaw-mono-rbac-1" + restartPolicy: Never diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-1-traces-verify.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-1-traces-verify.yaml new file mode 100644 index 0000000..f8fb3dd --- /dev/null +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-1-traces-verify.yaml @@ -0,0 +1,207 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: verify-traces-traceql-grpc-sa-1 + namespace: chainsaw-mono-rbac-1 +spec: + template: + spec: + serviceAccountName: tempo-rbac-sa-1 + containers: + - name: verify-traces + image: ghcr.io/grafana/tempo-operator/test-utils:main + command: + - /bin/bash + - -eux + - -c + args: + - | + token=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) + curl \ + -v -G \ + --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="grpc-rbac-1" }' \ + | tee /tmp/jaeger.out + num_traces=$(jq ".traces | length" /tmp/jaeger.out) + if [[ "$num_traces" != "2" ]]; then + echo && echo "The Jaeger API returned $num_traces instead of 2 traces." + exit 1 + fi + + echo "Fetch the first trace ID and store it in a variable" + traceID=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="grpc-rbac-1" }' | jq -r '.traces[0].traceID') + + echo "Use the trace ID to fetch the complete trace" + traceOutput=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/traces/$traceID) + + echo "Check for the strings in the trace output" + stringsToSearch=( + "\"key\":\"net.peer.ip\"" + "\"stringValue\":\"1.2.3.4\"" + "\"key\":\"peer.service\"" + "\"stringValue\":\"telemetrygen-client\"" + "\"key\":\"k8s.pod.ip\"" + "\"key\":\"k8s.container.name\"" + ) + for searchString in "${stringsToSearch[@]}"; do + if echo "$traceOutput" | grep -q "$searchString"; then + echo "Trace output for service grpc-rbac-1 contains: $searchString" + else + echo "Trace output for service grpc-rbac-1 does not contain: $searchString" + exit 1 + fi + done + + curl \ + -v -G \ + --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="grpc-rbac-2" }' \ + | tee /tmp/jaeger.out + num_traces=$(jq ".traces | length" /tmp/jaeger.out) + if [[ "$num_traces" != "2" ]]; then + echo && echo "The Jaeger API returned $num_traces instead of 2 traces." + exit 1 + fi + + echo "Fetch the first trace ID and store it in a variable" + traceID=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="grpc-rbac-2" }' | jq -r '.traces[0].traceID') + + echo "Use the trace ID to fetch the complete trace" + traceOutput=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/traces/$traceID) + + echo "Check for the strings in the trace output" + stringsToSearch=( + "\"key\":\"net.peer.ip\"" + "\"stringValue\":\"1.2.3.4\"" + "\"key\":\"peer.service\"" + "\"stringValue\":\"telemetrygen-client\"" + "\"key\":\"k8s.pod.ip\"" + "\"key\":\"k8s.container.name\"" + ) + for searchString in "${stringsToSearch[@]}"; do + if echo "$traceOutput" | grep -q "$searchString"; then + echo "Trace output for service grpc-rbac-2 contains: $searchString" + exit 1 + else + echo "Trace output for service grpc-rbac-2 does not contain: $searchString" + fi + done + restartPolicy: Never +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: verify-traces-traceql-http-sa-1 + namespace: chainsaw-mono-rbac-1 +spec: + template: + spec: + serviceAccountName: tempo-rbac-sa-1 + containers: + - name: verify-traces + image: ghcr.io/grafana/tempo-operator/test-utils:main + command: + - /bin/bash + - -eux + - -c + args: + - | + token=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) + curl \ + -v -G \ + --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="http-rbac-1" }' \ + | tee /tmp/jaeger.out + num_traces=$(jq ".traces | length" /tmp/jaeger.out) + if [[ "$num_traces" != "2" ]]; then + echo && echo "The Jaeger API returned $num_traces instead of 2 traces." + exit 1 + fi + + echo "Fetch the first trace ID and store it in a variable" + traceID=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="http-rbac-1" }' | jq -r '.traces[0].traceID') + + echo "Use the trace ID to fetch the complete trace" + traceOutput=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/traces/$traceID) + + echo "Check for the strings in the trace output" + stringsToSearch=( + "\"key\":\"net.peer.ip\"" + "\"stringValue\":\"1.2.3.4\"" + "\"key\":\"peer.service\"" + "\"stringValue\":\"telemetrygen-client\"" + "\"key\":\"k8s.pod.ip\"" + "\"key\":\"k8s.container.name\"" + ) + for searchString in "${stringsToSearch[@]}"; do + if echo "$traceOutput" | grep -q "$searchString"; then + echo "Trace output for service http-rbac-1 contains: $searchString" + else + echo "Trace output for service http-rbac-1 does not contain: $searchString" + exit 1 + fi + done + + curl \ + -v -G \ + --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="http-rbac-2" }' \ + | tee /tmp/jaeger.out + num_traces=$(jq ".traces | length" /tmp/jaeger.out) + if [[ "$num_traces" != "2" ]]; then + echo && echo "The Jaeger API returned $num_traces instead of 2 traces." + exit 1 + fi + + echo "Fetch the first trace ID and store it in a variable" + traceID=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="http-rbac-2" }' | jq -r '.traces[0].traceID') + + echo "Use the trace ID to fetch the complete trace" + traceOutput=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-mmo-rbac-gateway.chainsaw-mmo-rbac.svc:8080/api/traces/v1/dev/tempo/api/traces/$traceID) + + echo "Check for the strings in the trace output" + stringsToSearch=( + "\"key\":\"net.peer.ip\"" + "\"stringValue\":\"1.2.3.4\"" + "\"key\":\"peer.service\"" + "\"stringValue\":\"telemetrygen-client\"" + "\"key\":\"k8s.pod.ip\"" + "\"key\":\"k8s.container.name\"" + ) + for searchString in "${stringsToSearch[@]}"; do + if echo "$traceOutput" | grep -q "$searchString"; then + echo "Trace output for service http-rbac-2 contains: $searchString" + exit 1 + else + echo "Trace output for service http-rbac-2 does not contain: $searchString" + fi + done + restartPolicy: Never diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-2-traces-gen.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-2-traces-gen.yaml new file mode 100644 index 0000000..85f5a4c --- /dev/null +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-2-traces-gen.yaml @@ -0,0 +1,42 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-traces-grpc-sa-2 + namespace: chainsaw-mono-rbac-2 +spec: + template: + spec: + containers: + - name: telemetrygen + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.92.0 + args: + - traces + - --otlp-endpoint=dev-collector.chainsaw-mmo-rbac.svc:4317 + - --service=grpc-rbac-2 + - --otlp-insecure + - --traces=2 + - --otlp-attributes=k8s.container.name="telemetrygen" + - --otlp-attributes=k8s.namespace.name="chainsaw-mono-rbac-2" + restartPolicy: Never +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-traces-http-sa-2 + namespace: chainsaw-mono-rbac-2 +spec: + template: + spec: + containers: + - name: telemetrygen + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.92.0 + args: + - traces + - --otlp-endpoint=dev-collector.chainsaw-mmo-rbac.svc:4318 + - --otlp-http + - --otlp-insecure + - --service=http-rbac-2 + - --traces=2 + - --otlp-attributes=k8s.container.name="telemetrygen" + - --otlp-attributes=k8s.namespace.name="chainsaw-mono-rbac-2" + restartPolicy: Never diff --git a/tests/fixtures/chainsaw-tests/multitenancy/00-assert.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/00-assert.yaml similarity index 71% rename from tests/fixtures/chainsaw-tests/multitenancy/00-assert.yaml rename to tests/fixtures/chainsaw-tests/multitenancy-rbac/00-assert.yaml index 4854234..c4aa69f 100644 --- a/tests/fixtures/chainsaw-tests/multitenancy/00-assert.yaml +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/00-assert.yaml @@ -2,6 +2,6 @@ apiVersion: apps/v1 kind: Deployment metadata: name: minio - namespace: chainsaw-multitenancy + namespace: chainsaw-rbac status: readyReplicas: 1 diff --git a/tests/fixtures/chainsaw-tests/multitenancy/00-install-storage.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/00-install-storage.yaml similarity index 86% rename from tests/fixtures/chainsaw-tests/multitenancy/00-install-storage.yaml rename to tests/fixtures/chainsaw-tests/multitenancy-rbac/00-install-storage.yaml index 8dcbb1f..a48ae90 100644 --- a/tests/fixtures/chainsaw-tests/multitenancy/00-install-storage.yaml +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/00-install-storage.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Namespace metadata: - name: chainsaw-multitenancy + name: chainsaw-rbac --- apiVersion: v1 kind: PersistentVolumeClaim @@ -10,19 +10,19 @@ metadata: labels: app.kubernetes.io/name: minio name: minio - namespace: chainsaw-multitenancy + namespace: chainsaw-rbac spec: accessModes: - ReadWriteOnce resources: requests: - storage: 2Gi + storage: 1Gi --- apiVersion: apps/v1 kind: Deployment metadata: name: minio - namespace: chainsaw-multitenancy + namespace: chainsaw-rbac spec: selector: matchLabels: @@ -46,7 +46,7 @@ spec: value: tempo - name: MINIO_SECRET_KEY value: supersecret - image: quay.io/minio/minio:latest + image: quay.io/minio/minio:RELEASE.2024-10-02T17-50-41Z name: minio ports: - containerPort: 9000 @@ -62,7 +62,7 @@ apiVersion: v1 kind: Service metadata: name: minio - namespace: chainsaw-multitenancy + namespace: chainsaw-rbac spec: ports: - port: 9000 @@ -76,7 +76,7 @@ apiVersion: v1 kind: Secret metadata: name: minio - namespace: chainsaw-multitenancy + namespace: chainsaw-rbac stringData: endpoint: http://minio:9000 bucket: tempo diff --git a/tests/fixtures/chainsaw-tests/multitenancy/01-assert.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/01-assert.yaml similarity index 54% rename from tests/fixtures/chainsaw-tests/multitenancy/01-assert.yaml rename to tests/fixtures/chainsaw-tests/multitenancy-rbac/01-assert.yaml index 2666b66..ac896e4 100644 --- a/tests/fixtures/chainsaw-tests/multitenancy/01-assert.yaml +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/01-assert.yaml @@ -6,17 +6,17 @@ kind: Secret metadata: labels: app.kubernetes.io/component: gateway - app.kubernetes.io/instance: simplest + app.kubernetes.io/instance: simplst app.kubernetes.io/managed-by: tempo-operator app.kubernetes.io/name: tempo - name: tempo-simplest-gateway - namespace: chainsaw-multitenancy + name: tempo-simplst-gateway + namespace: chainsaw-rbac ownerReferences: - apiVersion: tempo.grafana.com/v1alpha1 blockOwnerDeletion: true controller: true kind: TempoStack - name: simplest + name: simplst type: Opaque --- apiVersion: v1 @@ -26,68 +26,67 @@ kind: ConfigMap metadata: labels: app.kubernetes.io/component: gateway - app.kubernetes.io/instance: simplest + app.kubernetes.io/instance: simplst app.kubernetes.io/managed-by: tempo-operator app.kubernetes.io/name: tempo - name: tempo-simplest-gateway - namespace: chainsaw-multitenancy + name: tempo-simplst-gateway + namespace: chainsaw-rbac ownerReferences: - apiVersion: tempo.grafana.com/v1alpha1 blockOwnerDeletion: true controller: true kind: TempoStack - name: simplest + name: simplst --- apiVersion: v1 kind: ConfigMap metadata: labels: app.kubernetes.io/component: gateway - app.kubernetes.io/instance: simplest + app.kubernetes.io/instance: simplst app.kubernetes.io/managed-by: tempo-operator app.kubernetes.io/name: tempo annotations: service.beta.openshift.io/inject-cabundle: "true" - name: tempo-simplest-gateway-cabundle - namespace: chainsaw-multitenancy + name: tempo-simplst-gateway-cabundle + namespace: chainsaw-rbac ownerReferences: - apiVersion: tempo.grafana.com/v1alpha1 blockOwnerDeletion: true controller: true kind: TempoStack - name: simplest + name: simplst --- apiVersion: v1 automountServiceAccountToken: true kind: ServiceAccount metadata: annotations: - serviceaccounts.openshift.io/oauth-redirectreference.dev: '{"kind":"OAuthRedirectReference","apiVersion":"v1","reference":{"kind":"Route","name":"tempo-simplest-gateway"}}' - serviceaccounts.openshift.io/oauth-redirectreference.prod: '{"kind":"OAuthRedirectReference","apiVersion":"v1","reference":{"kind":"Route","name":"tempo-simplest-gateway"}}' + serviceaccounts.openshift.io/oauth-redirectreference.dev: '{"kind":"OAuthRedirectReference","apiVersion":"v1","reference":{"kind":"Route","name":"tempo-simplst-gateway"}}' + serviceaccounts.openshift.io/oauth-redirectreference.prod: '{"kind":"OAuthRedirectReference","apiVersion":"v1","reference":{"kind":"Route","name":"tempo-simplst-gateway"}}' labels: app.kubernetes.io/component: gateway - app.kubernetes.io/instance: simplest + app.kubernetes.io/instance: simplst app.kubernetes.io/managed-by: tempo-operator app.kubernetes.io/name: tempo - name: tempo-simplest-gateway - namespace: chainsaw-multitenancy + name: tempo-simplst-gateway + namespace: chainsaw-rbac ownerReferences: - apiVersion: tempo.grafana.com/v1alpha1 blockOwnerDeletion: true controller: true kind: TempoStack - name: simplest + name: simplst --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: labels: app.kubernetes.io/component: gateway - app.kubernetes.io/instance: simplest + app.kubernetes.io/instance: simplst app.kubernetes.io/managed-by: tempo-operator app.kubernetes.io/name: tempo - app.kubernetes.io/namespace: chainsaw-multitenancy - name: tempo-simplest-gateway-chainsaw-multitenancy + name: tempo-simplst-gateway-chainsaw-rbac rules: - apiGroups: - authentication.k8s.io @@ -107,43 +106,40 @@ kind: ClusterRoleBinding metadata: labels: app.kubernetes.io/component: gateway - app.kubernetes.io/instance: simplest + app.kubernetes.io/instance: simplst app.kubernetes.io/managed-by: tempo-operator app.kubernetes.io/name: tempo - app.kubernetes.io/namespace: chainsaw-multitenancy - name: tempo-simplest-gateway-chainsaw-multitenancy + name: tempo-simplst-gateway-chainsaw-rbac roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: tempo-simplest-gateway-chainsaw-multitenancy + name: tempo-simplst-gateway-chainsaw-rbac subjects: - kind: ServiceAccount - name: tempo-simplest-gateway - namespace: chainsaw-multitenancy + name: tempo-simplst-gateway --- apiVersion: apps/v1 kind: Deployment metadata: labels: app.kubernetes.io/component: gateway - app.kubernetes.io/instance: simplest + app.kubernetes.io/instance: simplst app.kubernetes.io/managed-by: tempo-operator app.kubernetes.io/name: tempo - name: tempo-simplest-gateway - namespace: chainsaw-multitenancy + name: tempo-simplst-gateway + namespace: chainsaw-rbac spec: - replicas: 1 selector: matchLabels: app.kubernetes.io/component: gateway - app.kubernetes.io/instance: simplest + app.kubernetes.io/instance: simplst app.kubernetes.io/managed-by: tempo-operator app.kubernetes.io/name: tempo template: metadata: labels: app.kubernetes.io/component: gateway - app.kubernetes.io/instance: simplest + app.kubernetes.io/instance: simplst app.kubernetes.io/managed-by: tempo-operator app.kubernetes.io/name: tempo spec: @@ -152,10 +148,10 @@ spec: - --traces.tenant-header=x-scope-orgid - --web.listen=0.0.0.0:8080 - --web.internal.listen=0.0.0.0:8081 - - --traces.write.otlpgrpc.endpoint=tempo-simplest-distributor.chainsaw-multitenancy.svc.cluster.local:4317 - - --traces.write.otlphttp.endpoint=https://tempo-simplest-distributor.chainsaw-multitenancy.svc.cluster.local:4318 + - --traces.write.otlpgrpc.endpoint=tempo-simplst-distributor.chainsaw-rbac.svc.cluster.local:4317 + - --traces.write.otlphttp.endpoint=https://tempo-simplst-distributor.chainsaw-rbac.svc.cluster.local:4318 - --traces.write-timeout=30s - - --traces.tempo.endpoint=https://tempo-simplest-query-frontend.chainsaw-multitenancy.svc.cluster.local:3200 + - --traces.tempo.endpoint=https://tempo-simplst-query-frontend.chainsaw-rbac.svc.cluster.local:3200 - --grpc.listen=0.0.0.0:8090 - --rbac.config=/etc/tempo-gateway/cm/rbac.yaml - --tenants.config=/etc/tempo-gateway/secret/tenants.yaml @@ -169,19 +165,10 @@ spec: - --tls.server.cert-file=/etc/tempo-gateway/serving-certs/tls.crt - --tls.server.key-file=/etc/tempo-gateway/serving-certs/tls.key - --tls.healthchecks.server-ca-file=/etc/tempo-gateway/cabundle/service-ca.crt - - --tls.healthchecks.server-name=tempo-simplest-gateway.chainsaw-multitenancy.svc.cluster.local + - --tls.healthchecks.server-name=tempo-simplst-gateway.chainsaw-rbac.svc.cluster.local - --web.healthchecks.url=https://localhost:8080 - --tls.client-auth-type=NoClientCert - - --traces.read.endpoint=https://tempo-simplest-query-frontend.chainsaw-multitenancy.svc.cluster.local:16686 - livenessProbe: - failureThreshold: 10 - httpGet: - path: /live - port: internal - scheme: HTTPS - periodSeconds: 30 - successThreshold: 1 - timeoutSeconds: 2 + - --traces.query-rbac=true name: tempo-gateway ports: - containerPort: 8090 @@ -193,65 +180,15 @@ spec: - containerPort: 8080 name: public protocol: TCP - readinessProbe: - failureThreshold: 12 - httpGet: - path: /ready - port: internal - scheme: HTTPS - periodSeconds: 5 - successThreshold: 1 - timeoutSeconds: 1 - resources: - limits: - cpu: 120m - memory: "161061280" - requests: - cpu: 36m - memory: "48318384" - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - readOnlyRootFilesystem: true - terminationMessagePath: /dev/termination-log - terminationMessagePolicy: File - volumeMounts: - - mountPath: /etc/tempo-gateway/cm - name: rbac - readOnly: true - - mountPath: /etc/tempo-gateway/secret/tenants.yaml - name: tenant - readOnly: true - subPath: tenants.yaml - - mountPath: /var/run/ca - name: tempo-simplest-ca-bundle - - mountPath: /var/run/tls/server - name: tempo-simplest-gateway-mtls - - mountPath: /etc/tempo-gateway/serving-certs - name: serving-certs - readOnly: true - - mountPath: /etc/tempo-gateway/cabundle - name: cabundle - readOnly: true - args: - --log.level=warn - --web.listen=:8082 - --web.internal.listen=:8083 - --web.healthchecks.url=http://localhost:8082 - --opa.package=tempostack + - --opa.matcher=kubernetes_namespace_name - --openshift.mappings=dev=tempo.grafana.com - --openshift.mappings=prod=tempo.grafana.com - livenessProbe: - failureThreshold: 10 - httpGet: - path: /live - port: 8083 - scheme: HTTP - periodSeconds: 30 - successThreshold: 1 - timeoutSeconds: 2 name: tempo-gateway-opa ports: - containerPort: 8082 @@ -260,52 +197,6 @@ spec: - containerPort: 8083 name: opa-metrics protocol: TCP - readinessProbe: - failureThreshold: 12 - httpGet: - path: /ready - port: 8083 - scheme: HTTP - periodSeconds: 5 - successThreshold: 1 - timeoutSeconds: 1 - resources: {} - terminationMessagePath: /dev/termination-log - terminationMessagePolicy: File - serviceAccount: tempo-simplest-gateway - serviceAccountName: tempo-simplest-gateway - terminationGracePeriodSeconds: 30 - volumes: - - configMap: - defaultMode: 420 - items: - - key: rbac.yaml - path: rbac.yaml - name: tempo-simplest-gateway - name: rbac - - name: tenant - secret: - defaultMode: 420 - items: - - key: tenants.yaml - path: tenants.yaml - secretName: tempo-simplest-gateway - - configMap: - defaultMode: 420 - name: tempo-simplest-ca-bundle - name: tempo-simplest-ca-bundle - - name: tempo-simplest-gateway-mtls - secret: - defaultMode: 420 - secretName: tempo-simplest-gateway-mtls - - name: serving-certs - secret: - defaultMode: 420 - secretName: tempo-simplest-gateway-tls - - configMap: - defaultMode: 420 - name: tempo-simplest-gateway-cabundle - name: cabundle status: availableReplicas: 1 readyReplicas: 1 @@ -316,17 +207,17 @@ kind: Route metadata: labels: app.kubernetes.io/component: gateway - app.kubernetes.io/instance: simplest + app.kubernetes.io/instance: simplst app.kubernetes.io/managed-by: tempo-operator app.kubernetes.io/name: tempo - name: tempo-simplest-gateway - namespace: chainsaw-multitenancy + name: tempo-simplst-gateway + namespace: chainsaw-rbac ownerReferences: - apiVersion: tempo.grafana.com/v1alpha1 blockOwnerDeletion: true controller: true kind: TempoStack - name: simplest + name: simplst spec: port: targetPort: public @@ -334,7 +225,7 @@ spec: termination: reencrypt to: kind: Service - name: tempo-simplest-gateway + name: tempo-simplst-gateway weight: 100 wildcardPolicy: None --- @@ -342,20 +233,20 @@ apiVersion: v1 kind: Service metadata: annotations: - service.beta.openshift.io/serving-cert-secret-name: tempo-simplest-gateway-tls + service.beta.openshift.io/serving-cert-secret-name: tempo-simplst-gateway-tls labels: app.kubernetes.io/component: gateway - app.kubernetes.io/instance: simplest + app.kubernetes.io/instance: simplst app.kubernetes.io/managed-by: tempo-operator app.kubernetes.io/name: tempo - name: tempo-simplest-gateway - namespace: chainsaw-multitenancy + name: tempo-simplst-gateway + namespace: chainsaw-rbac ownerReferences: - apiVersion: tempo.grafana.com/v1alpha1 blockOwnerDeletion: true controller: true kind: TempoStack - name: simplest + name: simplst spec: ports: - name: grpc-public @@ -372,7 +263,7 @@ spec: targetPort: public selector: app.kubernetes.io/component: gateway - app.kubernetes.io/instance: simplest + app.kubernetes.io/instance: simplst app.kubernetes.io/managed-by: tempo-operator app.kubernetes.io/name: tempo type: ClusterIP @@ -380,39 +271,39 @@ spec: apiVersion: apps/v1 kind: Deployment metadata: - name: tempo-simplest-compactor - namespace: chainsaw-multitenancy + name: tempo-simplst-compactor + namespace: chainsaw-rbac status: readyReplicas: 1 --- apiVersion: apps/v1 kind: Deployment metadata: - name: tempo-simplest-distributor - namespace: chainsaw-multitenancy + name: tempo-simplst-distributor + namespace: chainsaw-rbac status: readyReplicas: 1 --- apiVersion: apps/v1 kind: Deployment metadata: - name: tempo-simplest-querier - namespace: chainsaw-multitenancy + name: tempo-simplst-querier + namespace: chainsaw-rbac status: readyReplicas: 1 --- apiVersion: apps/v1 kind: Deployment metadata: - name: tempo-simplest-query-frontend - namespace: chainsaw-multitenancy + name: tempo-simplst-query-frontend + namespace: chainsaw-rbac status: readyReplicas: 1 --- apiVersion: apps/v1 kind: StatefulSet metadata: - name: tempo-simplest-ingester - namespace: chainsaw-multitenancy + name: tempo-simplst-ingester + namespace: chainsaw-rbac status: readyReplicas: 1 diff --git a/tests/fixtures/chainsaw-tests/multitenancy/01-install-tempo.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/01-install-tempo.yaml similarity index 61% rename from tests/fixtures/chainsaw-tests/multitenancy/01-install-tempo.yaml rename to tests/fixtures/chainsaw-tests/multitenancy-rbac/01-install-tempo.yaml index 2ce4748..85b9184 100644 --- a/tests/fixtures/chainsaw-tests/multitenancy/01-install-tempo.yaml +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/01-install-tempo.yaml @@ -1,23 +1,9 @@ -# based on config/samples/openshift/tempo_v1alpha1_multitenancy.yaml apiVersion: tempo.grafana.com/v1alpha1 kind: TempoStack metadata: - name: simplest - namespace: chainsaw-multitenancy + name: simplst + namespace: chainsaw-rbac spec: - retention: - global: - traces: 20h - perTenant: - dev: - traces: 10h - limits: - perTenant: - dev: - ingestion: - ingestionBurstSizeBytes: 1000000 - query: - maxSearchDuration: 1h storage: secret: name: minio @@ -26,7 +12,7 @@ spec: resources: total: limits: - memory: 3Gi + memory: 4Gi cpu: 2000m tenants: mode: openshift @@ -38,14 +24,13 @@ spec: template: gateway: enabled: true - queryFrontend: - jaegerQuery: + rbac: enabled: true --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: tempostack-traces-reader + name: tempostack-traces-reader-rbac rules: - apiGroups: - 'tempo.grafana.com' @@ -59,23 +44,21 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: tempostack-traces-reader + name: tempostack-traces-reader-rbac roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: tempostack-traces-reader + name: tempostack-traces-reader-rbac subjects: - kind: Group apiGroup: rbac.authorization.k8s.io name: system:authenticated --- -# grant the default serviceaccount in the chainsaw-multitenancy namespace -# access to view resource in chainsaw-multitenancy namespace apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: view - namespace: chainsaw-multitenancy + namespace: chainsaw-rbac roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -83,4 +66,4 @@ roleRef: subjects: - kind: ServiceAccount name: default - namespace: chainsaw-multitenancy + namespace: chainsaw-rbac diff --git a/tests/fixtures/chainsaw-tests/multitenancy/02-assert.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/02-assert.yaml similarity index 74% rename from tests/fixtures/chainsaw-tests/multitenancy/02-assert.yaml rename to tests/fixtures/chainsaw-tests/multitenancy-rbac/02-assert.yaml index ac725d5..6115475 100644 --- a/tests/fixtures/chainsaw-tests/multitenancy/02-assert.yaml +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/02-assert.yaml @@ -3,6 +3,6 @@ apiVersion: apps/v1 kind: Deployment metadata: name: dev-collector - namespace: chainsaw-multitenancy + namespace: chainsaw-rbac status: readyReplicas: 1 diff --git a/tests/fixtures/chainsaw-tests/multitenancy-rbac/02-install-otelcol.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/02-install-otelcol.yaml new file mode 100644 index 0000000..496526d --- /dev/null +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/02-install-otelcol.yaml @@ -0,0 +1,139 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: otel-collector-deployment + namespace: chainsaw-rbac + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: chainsaw-rbac-clusterrole +rules: +- apiGroups: [""] + resources: ["pods", "namespaces", "nodes"] + verbs: ["get", "watch", "list"] +- apiGroups: ["apps"] + resources: ["replicasets"] + verbs: ["get", "list", "watch"] +- apiGroups: ["extensions"] + resources: ["replicasets"] + verbs: ["get", "list", "watch"] + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: chainsaw-rbac-clusterrole-binding +subjects: +- kind: ServiceAccount + name: otel-collector-deployment + namespace: chainsaw-rbac +roleRef: + kind: ClusterRole + name: chainsaw-rbac-clusterrole + apiGroup: rbac.authorization.k8s.io + +--- +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: dev + namespace: chainsaw-rbac +spec: + serviceAccount: otel-collector-deployment + config: | + extensions: + bearertokenauth: + filename: "/var/run/secrets/kubernetes.io/serviceaccount/token" + + receivers: + otlp/grpc: + protocols: + grpc: + otlp/http: + protocols: + http: + + processors: + k8sattributes: + extract: + metadata: + - k8s.pod.name + - k8s.pod.uid + - k8s.deployment.name + - k8s.namespace.name + - k8s.node.name + pod_association: + - sources: + - from: resource_attribute + name: k8s.pod.ip + - sources: + - from: connection + + exporters: + debug: + verbosity: detailed + otlp: + endpoint: tempo-simplst-gateway.chainsaw-rbac.svc.cluster.local:8090 + tls: + insecure: false + ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt" + auth: + authenticator: bearertokenauth + headers: + X-Scope-OrgID: "dev" + otlphttp: + endpoint: https://tempo-simplst-gateway.chainsaw-rbac.svc.cluster.local:8080/api/traces/v1/dev + tls: + insecure: false + ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt" + auth: + authenticator: bearertokenauth + headers: + X-Scope-OrgID: "dev" + + service: + telemetry: + logs: + level: "DEBUG" + development: true + extensions: [bearertokenauth] + pipelines: + traces/grpc: + receivers: [otlp/grpc] + processors: [k8sattributes] + exporters: [otlp,debug] + traces/http: + receivers: [otlp/http] + processors: [k8sattributes] + exporters: [otlphttp,debug] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: tempostack-traces-write-rbac +rules: + - apiGroups: + - 'tempo.grafana.com' + # this needs to match tenant name in the CR/tenants.yaml and the tenant has be sent in X-Scope-OrgID + # The API gateway sends the tenantname as resource (res) to OPA sidecar + resources: + - dev + resourceNames: + - traces + verbs: + - 'create' +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: tempostack-traces-rbac +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: tempostack-traces-write-rbac +subjects: + - kind: ServiceAccount + name: otel-collector-deployment + namespace: chainsaw-rbac diff --git a/tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-create-sas.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-create-sas.yaml new file mode 100644 index 0000000..c318323 --- /dev/null +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-create-sas.yaml @@ -0,0 +1,42 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: tempo-rbac-sa-1 + namespace: chainsaw-test-rbac-1 + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: tempo-rbac-sa-2 + namespace: chainsaw-test-rbac-2 + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: chainsaw-test-rbac-1-admin + namespace: chainsaw-test-rbac-1 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: admin +subjects: +- kind: ServiceAccount + name: tempo-rbac-sa-1 + namespace: chainsaw-test-rbac-1 + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: chainsaw-test-rbac-2-admin + namespace: chainsaw-test-rbac-2 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: admin +subjects: +- kind: ServiceAccount + name: tempo-rbac-sa-2 + namespace: chainsaw-test-rbac-2 \ No newline at end of file diff --git a/tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-kubeadmin-traces-verify.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-kubeadmin-traces-verify.yaml new file mode 100644 index 0000000..ffc45b3 --- /dev/null +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-kubeadmin-traces-verify.yaml @@ -0,0 +1,15 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: verify-traces-kubeadmin-grpc + namespace: chainsaw-rbac +status: + succeeded: 1 +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: verify-traces-kubeadmin-http + namespace: chainsaw-rbac +status: + succeeded: 1 \ No newline at end of file diff --git a/tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-tempo-rbac-sa-1-traces-gen.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-tempo-rbac-sa-1-traces-gen.yaml new file mode 100644 index 0000000..2bcaff2 --- /dev/null +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-tempo-rbac-sa-1-traces-gen.yaml @@ -0,0 +1,15 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-traces-grpc-sa-1 + namespace: chainsaw-test-rbac-1 +status: + succeeded: 1 +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-traces-http-sa-1 + namespace: chainsaw-test-rbac-1 +status: + succeeded: 1 \ No newline at end of file diff --git a/tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-tempo-rbac-sa-1-traces-verify.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-tempo-rbac-sa-1-traces-verify.yaml new file mode 100644 index 0000000..fca3fa4 --- /dev/null +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-tempo-rbac-sa-1-traces-verify.yaml @@ -0,0 +1,15 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: verify-traces-traceql-grpc-sa-1 + namespace: chainsaw-test-rbac-1 +status: + succeeded: 1 +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: verify-traces-traceql-http-sa-1 + namespace: chainsaw-test-rbac-1 +status: + succeeded: 1 \ No newline at end of file diff --git a/tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-tempo-rbac-sa-2-traces-gen.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-tempo-rbac-sa-2-traces-gen.yaml new file mode 100644 index 0000000..e7a0d51 --- /dev/null +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-tempo-rbac-sa-2-traces-gen.yaml @@ -0,0 +1,15 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-traces-grpc-sa-2 + namespace: chainsaw-test-rbac-2 +status: + succeeded: 1 +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-traces-http-sa-2 + namespace: chainsaw-test-rbac-2 +status: + succeeded: 1 \ No newline at end of file diff --git a/tests/fixtures/chainsaw-tests/multitenancy-rbac/chainsaw-test.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/chainsaw-test.yaml new file mode 100755 index 0000000..5a0b118 --- /dev/null +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/chainsaw-test.yaml @@ -0,0 +1,55 @@ +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + name: multitenancy-rbac +spec: + namespace: chainsaw-rbac + steps: + - name: step-00 + try: + - apply: + file: 00-install-storage.yaml + - assert: + file: 00-assert.yaml + - name: step-01 + try: + - apply: + file: 01-install-tempo.yaml + - assert: + file: 01-assert.yaml + - name: step-02 + try: + - apply: + file: 02-install-otelcol.yaml + - assert: + file: 02-assert.yaml + - name: Create non-admin SAs with namespace level access + try: + - apply: + file: create-SAs-with-namespace-access.yaml + - assert: + file: assert-create-sas.yaml + - name: Generate traces from namespace chainsaw-test-rbac-1 + try: + - apply: + file: tempo-rbac-sa-1-traces-gen.yaml + - assert: + file: assert-tempo-rbac-sa-1-traces-gen.yaml + - name: Generate traces from namespace chainsaw-test-rbac-2 + try: + - apply: + file: tempo-rbac-sa-2-traces-gen.yaml + - assert: + file: assert-tempo-rbac-sa-2-traces-gen.yaml + - name: Assert tracess using RBAC + try: + - apply: + file: tempo-rbac-sa-1-traces-verify.yaml + - assert: + file: assert-tempo-rbac-sa-1-traces-verify.yaml + - name: Verify kubeadmin can view traces from all projects + try: + - apply: + file: kubeadmin-traces-verify.yaml + - assert: + file: assert-kubeadmin-traces-verify.yaml \ No newline at end of file diff --git a/tests/fixtures/chainsaw-tests/multitenancy-rbac/create-SAs-with-namespace-access.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/create-SAs-with-namespace-access.yaml new file mode 100644 index 0000000..20ce77a --- /dev/null +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/create-SAs-with-namespace-access.yaml @@ -0,0 +1,91 @@ +apiVersion: project.openshift.io/v1 +kind: Project +metadata: + name: chainsaw-test-rbac-1 +spec: {} + +--- +apiVersion: project.openshift.io/v1 +kind: Project +metadata: + name: chainsaw-test-rbac-2 +spec: {} + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: tempo-rbac-sa-1 + namespace: chainsaw-test-rbac-1 + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: tempo-rbac-sa-2 + namespace: chainsaw-test-rbac-2 + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: tempo-rbac-cluster-admin + namespace: chainsaw-rbac + +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: chainsaw-test-rbac-1-admin + namespace: chainsaw-test-rbac-1 +subjects: + - kind: ServiceAccount + name: tempo-rbac-sa-1 + namespace: chainsaw-test-rbac-1 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: admin + +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: chainsaw-test-rbac-2-admin + namespace: chainsaw-test-rbac-2 +subjects: + - kind: ServiceAccount + name: tempo-rbac-sa-2 + namespace: chainsaw-test-rbac-2 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: admin + +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: tempo-rbac-cluster-admin-binding +subjects: + - kind: ServiceAccount + name: tempo-rbac-cluster-admin + namespace: chainsaw-rbac +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin + +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: chainsaw-test-rbac-1-testuser + namespace: chainsaw-test-rbac-1 +subjects: + - kind: User + name: testuser-1 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: admin diff --git a/tests/fixtures/chainsaw-tests/multitenancy-rbac/kubeadmin-traces-verify.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/kubeadmin-traces-verify.yaml new file mode 100644 index 0000000..57a62f6 --- /dev/null +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/kubeadmin-traces-verify.yaml @@ -0,0 +1,201 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: verify-traces-kubeadmin-grpc + namespace: chainsaw-rbac +spec: + template: + spec: + serviceAccountName: tempo-rbac-cluster-admin + containers: + - name: verify-traces + image: ghcr.io/grafana/tempo-operator/test-utils:main + command: + - /bin/bash + - -eux + - -c + args: + - | + # Get the cluster-admin service account token + token=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) + + # First, verify traces from chainsaw-test-rbac-1 (grpc-rbac-1 service) + curl \ + -G \ + --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="grpc-rbac-1" }' \ + | tee /tmp/jaeger-rbac-1.out + num_traces=$(jq ".traces | length" /tmp/jaeger-rbac-1.out) + if [[ "$num_traces" != "2" ]]; then + echo && echo "The Jaeger API returned $num_traces instead of 2 traces for grpc-rbac-1." + exit 1 + fi + + echo "Fetch the first trace ID and store it in a variable" + traceID=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="grpc-rbac-1" }' | jq -r '.traces[0].traceID') + + echo "Use the trace ID to fetch the complete trace" + traceOutput=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/traces/$traceID) + + echo "Check for the strings in the trace output - cluster-admin should see complete traces" + stringsToSearch=( + "\"key\":\"net.peer.ip\"" + "\"stringValue\":\"1.2.3.4\"" + "\"key\":\"peer.service\"" + "\"stringValue\":\"telemetrygen-client\"" + "\"key\":\"k8s.pod.ip\"" + "\"key\":\"k8s.container.name\"" + ) + for searchString in "${stringsToSearch[@]}"; do + if echo "$traceOutput" | grep -q "$searchString"; then + echo "Cluster-admin: Trace output for service grpc-rbac-1 contains: $searchString" + else + echo "Cluster-admin: Trace output for service grpc-rbac-1 does not contain: $searchString" + exit 1 + fi + done + + # Now verify traces from chainsaw-test-rbac-2 (grpc-rbac-2 service) + # cluster-admin should be able to see complete traces from this project too + curl \ + -G \ + --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="grpc-rbac-2" }' \ + | tee /tmp/jaeger-rbac-2.out + num_traces=$(jq ".traces | length" /tmp/jaeger-rbac-2.out) + if [[ "$num_traces" != "2" ]]; then + echo && echo "The Jaeger API returned $num_traces instead of 2 traces for grpc-rbac-2." + exit 1 + fi + + echo "Fetch the first trace ID and store it in a variable" + traceID=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="grpc-rbac-2" }' | jq -r '.traces[0].traceID') + + echo "Use the trace ID to fetch the complete trace" + traceOutput=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/traces/$traceID) + + echo "Check for the strings in the trace output - cluster-admin should see complete traces" + for searchString in "${stringsToSearch[@]}"; do + if echo "$traceOutput" | grep -q "$searchString"; then + echo "Cluster-admin: Trace output for service grpc-rbac-2 contains: $searchString" + else + echo "Cluster-admin: Trace output for service grpc-rbac-2 does not contain: $searchString" + exit 1 + fi + done + restartPolicy: Never +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: verify-traces-kubeadmin-http + namespace: chainsaw-rbac +spec: + template: + spec: + serviceAccountName: tempo-rbac-cluster-admin + containers: + - name: verify-traces + image: ghcr.io/grafana/tempo-operator/test-utils:main + command: + - /bin/bash + - -eux + - -c + args: + - | + # Get the cluster-admin service account token + token=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) + + # First, verify traces from chainsaw-test-rbac-1 (http-rbac-1 service) + curl \ + -G \ + --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="http-rbac-1" }' \ + | tee /tmp/jaeger-rbac-1.out + num_traces=$(jq ".traces | length" /tmp/jaeger-rbac-1.out) + if [[ "$num_traces" != "2" ]]; then + echo && echo "The Jaeger API returned $num_traces instead of 2 traces for http-rbac-1." + exit 1 + fi + + echo "Fetch the first trace ID and store it in a variable" + traceID=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="http-rbac-1" }' | jq -r '.traces[0].traceID') + + echo "Use the trace ID to fetch the complete trace" + traceOutput=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/traces/$traceID) + + echo "Check for the strings in the trace output - cluster-admin should see complete traces" + stringsToSearch=( + "\"key\":\"net.peer.ip\"" + "\"stringValue\":\"1.2.3.4\"" + "\"key\":\"peer.service\"" + "\"stringValue\":\"telemetrygen-client\"" + "\"key\":\"k8s.pod.ip\"" + "\"key\":\"k8s.container.name\"" + ) + for searchString in "${stringsToSearch[@]}"; do + if echo "$traceOutput" | grep -q "$searchString"; then + echo "Cluster-admin: Trace output for service http-rbac-1 contains: $searchString" + else + echo "Cluster-admin: Trace output for service http-rbac-1 does not contain: $searchString" + exit 1 + fi + done + + # Now verify traces from chainsaw-test-rbac-2 (http-rbac-2 service) + # cluster-admin should be able to see complete traces from this project too + curl \ + -G \ + --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="http-rbac-2" }' \ + | tee /tmp/jaeger-rbac-2.out + num_traces=$(jq ".traces | length" /tmp/jaeger-rbac-2.out) + if [[ "$num_traces" != "2" ]]; then + echo && echo "The Jaeger API returned $num_traces instead of 2 traces for http-rbac-2." + exit 1 + fi + + echo "Fetch the first trace ID and store it in a variable" + traceID=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="http-rbac-2" }' | jq -r '.traces[0].traceID') + + echo "Use the trace ID to fetch the complete trace" + traceOutput=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/traces/$traceID) + + echo "Check for the strings in the trace output - cluster-admin should see complete traces" + for searchString in "${stringsToSearch[@]}"; do + if echo "$traceOutput" | grep -q "$searchString"; then + echo "Cluster-admin: Trace output for service http-rbac-2 contains: $searchString" + else + echo "Cluster-admin: Trace output for service http-rbac-2 does not contain: $searchString" + exit 1 + fi + done + restartPolicy: Never \ No newline at end of file diff --git a/tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-1-traces-gen.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-1-traces-gen.yaml new file mode 100644 index 0000000..d64e939 --- /dev/null +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-1-traces-gen.yaml @@ -0,0 +1,42 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-traces-grpc-sa-1 + namespace: chainsaw-test-rbac-1 +spec: + template: + spec: + containers: + - name: telemetrygen + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.92.0 + args: + - traces + - --otlp-endpoint=dev-collector.chainsaw-rbac.svc:4317 + - --service=grpc-rbac-1 + - --otlp-insecure + - --traces=2 + - --otlp-attributes=k8s.container.name="telemetrygen" + - --otlp-attributes=k8s.namespace.name="chainsaw-test-rbac-1" + restartPolicy: Never +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-traces-http-sa-1 + namespace: chainsaw-test-rbac-1 +spec: + template: + spec: + containers: + - name: telemetrygen + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.92.0 + args: + - traces + - --otlp-endpoint=dev-collector.chainsaw-rbac.svc:4318 + - --otlp-http + - --otlp-insecure + - --service=http-rbac-1 + - --traces=2 + - --otlp-attributes=k8s.container.name="telemetrygen" + - --otlp-attributes=k8s.namespace.name="chainsaw-test-rbac-1" + restartPolicy: Never diff --git a/tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-1-traces-verify.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-1-traces-verify.yaml new file mode 100644 index 0000000..bc0ed2f --- /dev/null +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-1-traces-verify.yaml @@ -0,0 +1,208 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: verify-traces-traceql-grpc-sa-1 + namespace: chainsaw-test-rbac-1 +spec: + template: + spec: + serviceAccountName: tempo-rbac-sa-1 + containers: + - name: verify-traces + image: ghcr.io/grafana/tempo-operator/test-utils:main + command: + - /bin/bash + - -eux + - -c + args: + - | + token=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) + curl \ + -G \ + --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="grpc-rbac-1" }' \ + | tee /tmp/jaeger.out + num_traces=$(jq ".traces | length" /tmp/jaeger.out) + if [[ "$num_traces" != "2" ]]; then + echo && echo "The Jaeger API returned $num_traces instead of 2 traces." + exit 1 + fi + + echo "Fetch the first trace ID and store it in a variable" + traceID=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="grpc-rbac-1" }' | jq -r '.traces[0].traceID') + + echo "Use the trace ID to fetch the complete trace" + traceOutput=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/traces/$traceID) + + echo "Check for the strings in the trace output" + stringsToSearch=( + "\"key\":\"net.peer.ip\"" + "\"stringValue\":\"1.2.3.4\"" + "\"key\":\"peer.service\"" + "\"stringValue\":\"telemetrygen-client\"" + "\"key\":\"k8s.pod.ip\"" + "\"key\":\"k8s.container.name\"" + ) + for searchString in "${stringsToSearch[@]}"; do + if echo "$traceOutput" | grep -q "$searchString"; then + echo "Trace output for service grpc-rbac-1 contains: $searchString" + else + echo "Trace output for service grpc-rbac-1 does not contain: $searchString" + exit 1 + fi + done + + + curl \ + -G \ + --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="grpc-rbac-2" }' \ + | tee /tmp/jaeger.out + num_traces=$(jq ".traces | length" /tmp/jaeger.out) + if [[ "$num_traces" != "2" ]]; then + echo && echo "The Jaeger API returned $num_traces instead of 2 traces." + exit 1 + fi + + echo "Fetch the first trace ID and store it in a variable" + traceID=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="grpc-rbac-2" }' | jq -r '.traces[0].traceID') + + echo "Use the trace ID to fetch the complete trace" + traceOutput=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/traces/$traceID) + + echo "Check for the strings in the trace output" + stringsToSearch=( + "\"key\":\"net.peer.ip\"" + "\"stringValue\":\"1.2.3.4\"" + "\"key\":\"peer.service\"" + "\"stringValue\":\"telemetrygen-client\"" + "\"key\":\"k8s.pod.ip\"" + "\"key\":\"k8s.container.name\"" + ) + for searchString in "${stringsToSearch[@]}"; do + if echo "$traceOutput" | grep -q "$searchString"; then + echo "Trace output for service grpc-rbac-2 contains: $searchString" + exit 1 + else + echo "Trace output for service grpc-rbac-2 does not contain: $searchString" + fi + done + restartPolicy: Never +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: verify-traces-traceql-http-sa-1 + namespace: chainsaw-test-rbac-1 +spec: + template: + spec: + serviceAccountName: tempo-rbac-sa-1 + containers: + - name: verify-traces + image: ghcr.io/grafana/tempo-operator/test-utils:main + command: + - /bin/bash + - -eux + - -c + args: + - | + token=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) + curl \ + -G \ + --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="http-rbac-1" }' \ + | tee /tmp/jaeger.out + num_traces=$(jq ".traces | length" /tmp/jaeger.out) + if [[ "$num_traces" != "2" ]]; then + echo && echo "The Jaeger API returned $num_traces instead of 2 traces." + exit 1 + fi + + echo "Fetch the first trace ID and store it in a variable" + traceID=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="http-rbac-1" }' | jq -r '.traces[0].traceID') + + echo "Use the trace ID to fetch the complete trace" + traceOutput=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/traces/$traceID) + + echo "Check for the strings in the trace output" + stringsToSearch=( + "\"key\":\"net.peer.ip\"" + "\"stringValue\":\"1.2.3.4\"" + "\"key\":\"peer.service\"" + "\"stringValue\":\"telemetrygen-client\"" + "\"key\":\"k8s.pod.ip\"" + "\"key\":\"k8s.container.name\"" + ) + for searchString in "${stringsToSearch[@]}"; do + if echo "$traceOutput" | grep -q "$searchString"; then + echo "Trace output for service http-rbac-1 contains: $searchString" + else + echo "Trace output for service http-rbac-1 does not contain: $searchString" + exit 1 + fi + done + + curl \ + -G \ + --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="http-rbac-2" }' \ + | tee /tmp/jaeger.out + num_traces=$(jq ".traces | length" /tmp/jaeger.out) + if [[ "$num_traces" != "2" ]]; then + echo && echo "The Jaeger API returned $num_traces instead of 2 traces." + exit 1 + fi + + echo "Fetch the first trace ID and store it in a variable" + traceID=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/search \ + --data-urlencode 'q={ resource.service.name="http-rbac-2" }' | jq -r '.traces[0].traceID') + + echo "Use the trace ID to fetch the complete trace" + traceOutput=$(curl -G --header "Authorization: Bearer $token" \ + --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ + https://tempo-simplst-gateway.chainsaw-rbac.svc:8080/api/traces/v1/dev/tempo/api/traces/$traceID) + + echo "Check for the strings in the trace output" + stringsToSearch=( + "\"key\":\"net.peer.ip\"" + "\"stringValue\":\"1.2.3.4\"" + "\"key\":\"peer.service\"" + "\"stringValue\":\"telemetrygen-client\"" + "\"key\":\"k8s.pod.ip\"" + "\"key\":\"k8s.container.name\"" + ) + for searchString in "${stringsToSearch[@]}"; do + if echo "$traceOutput" | grep -q "$searchString"; then + echo "Trace output for service http-rbac-2 contains: $searchString" + exit 1 + else + echo "Trace output for service http-rbac-2 does not contain: $searchString" + fi + done + restartPolicy: Never diff --git a/tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-2-traces-gen.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-2-traces-gen.yaml new file mode 100644 index 0000000..3633d50 --- /dev/null +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-2-traces-gen.yaml @@ -0,0 +1,42 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-traces-grpc-sa-2 + namespace: chainsaw-test-rbac-2 +spec: + template: + spec: + containers: + - name: telemetrygen + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.92.0 + args: + - traces + - --otlp-endpoint=dev-collector.chainsaw-rbac.svc:4317 + - --service=grpc-rbac-2 + - --otlp-insecure + - --traces=2 + - --otlp-attributes=k8s.container.name="telemetrygen" + - --otlp-attributes=k8s.namespace.name="chainsaw-test-rbac-2" + restartPolicy: Never +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-traces-http-sa-2 + namespace: chainsaw-test-rbac-2 +spec: + template: + spec: + containers: + - name: telemetrygen + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.92.0 + args: + - traces + - --otlp-endpoint=dev-collector.chainsaw-rbac.svc:4318 + - --otlp-http + - --otlp-insecure + - --service=http-rbac-2 + - --traces=2 + - --otlp-attributes=k8s.container.name="telemetrygen" + - --otlp-attributes=k8s.namespace.name="chainsaw-test-rbac-2" + restartPolicy: Never diff --git a/tests/fixtures/chainsaw-tests/multitenancy/02-install-otelcol.yaml b/tests/fixtures/chainsaw-tests/multitenancy/02-install-otelcol.yaml deleted file mode 100644 index b555512..0000000 --- a/tests/fixtures/chainsaw-tests/multitenancy/02-install-otelcol.yaml +++ /dev/null @@ -1,86 +0,0 @@ -# based on config/samples/otelcol_v1alpha1_openshift.yaml ---- -apiVersion: opentelemetry.io/v1alpha1 -kind: OpenTelemetryCollector -metadata: - name: dev - namespace: chainsaw-multitenancy -spec: - config: | - extensions: - bearertokenauth: - filename: "/var/run/secrets/kubernetes.io/serviceaccount/token" - - receivers: - otlp/grpc: - protocols: - grpc: - otlp/http: - protocols: - http: - - processors: - - exporters: - otlp: - endpoint: tempo-simplest-gateway.chainsaw-multitenancy.svc.cluster.local:8090 - tls: - insecure: false - ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt" - auth: - authenticator: bearertokenauth - headers: - X-Scope-OrgID: "dev" - otlphttp: - endpoint: https://tempo-simplest-gateway.chainsaw-multitenancy.svc.cluster.local:8080/api/traces/v1/dev - tls: - insecure: false - ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt" - auth: - authenticator: bearertokenauth - headers: - X-Scope-OrgID: "dev" - - service: - telemetry: - logs: - level: "DEBUG" - development: true - encoding: "json" - extensions: [bearertokenauth] - pipelines: - traces/grpc: - receivers: [otlp/grpc] - exporters: [otlp] - traces/http: - receivers: [otlp/http] - exporters: [otlphttp] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: tempostack-traces-write -rules: - - apiGroups: - - 'tempo.grafana.com' - # this needs to match tenant name in the CR/tenants.yaml and the tenant has be sent in X-Scope-OrgID - # The API gateway sends the tenantname as resource (res) to OPA sidecar - resources: - - dev - resourceNames: - - traces - verbs: - - 'create' ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: tempostack-traces -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: tempostack-traces-write -subjects: - - kind: ServiceAccount - name: dev-collector - namespace: chainsaw-multitenancy diff --git a/tests/fixtures/chainsaw-tests/multitenancy/03-assert.yaml b/tests/fixtures/chainsaw-tests/multitenancy/03-assert.yaml deleted file mode 100644 index b851c15..0000000 --- a/tests/fixtures/chainsaw-tests/multitenancy/03-assert.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: generate-traces-grpc - namespace: chainsaw-multitenancy -status: - succeeded: 1 ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: generate-traces-http - namespace: chainsaw-multitenancy -status: - succeeded: 1 \ No newline at end of file diff --git a/tests/fixtures/chainsaw-tests/multitenancy/03-generate-traces.yaml b/tests/fixtures/chainsaw-tests/multitenancy/03-generate-traces.yaml deleted file mode 100644 index 26804d6..0000000 --- a/tests/fixtures/chainsaw-tests/multitenancy/03-generate-traces.yaml +++ /dev/null @@ -1,38 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: generate-traces-grpc - namespace: chainsaw-multitenancy -spec: - template: - spec: - containers: - - name: telemetrygen - image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.92.0 - args: - - traces - - --otlp-endpoint=dev-collector:4317 - - --service=grpc - - --otlp-insecure - - --traces=10 - restartPolicy: Never ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: generate-traces-http - namespace: chainsaw-multitenancy -spec: - template: - spec: - containers: - - name: telemetrygen - image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.92.0 - args: - - traces - - --otlp-endpoint=dev-collector:4318 - - --otlp-http - - --otlp-insecure - - --service=http - - --traces=10 - restartPolicy: Never diff --git a/tests/fixtures/chainsaw-tests/multitenancy/04-assert.yaml b/tests/fixtures/chainsaw-tests/multitenancy/04-assert.yaml deleted file mode 100644 index aaadeeb..0000000 --- a/tests/fixtures/chainsaw-tests/multitenancy/04-assert.yaml +++ /dev/null @@ -1,31 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: verify-traces-grpc - namespace: chainsaw-multitenancy -status: - succeeded: 1 ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: verify-traces-traceql-grpc - namespace: chainsaw-multitenancy -status: - succeeded: 1 ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: verify-traces-http - namespace: chainsaw-multitenancy -status: - succeeded: 1 ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: verify-traces-traceql-http - namespace: chainsaw-multitenancy -status: - succeeded: 1 \ No newline at end of file diff --git a/tests/fixtures/chainsaw-tests/multitenancy/04-verify-traces.yaml b/tests/fixtures/chainsaw-tests/multitenancy/04-verify-traces.yaml deleted file mode 100644 index 85711af..0000000 --- a/tests/fixtures/chainsaw-tests/multitenancy/04-verify-traces.yaml +++ /dev/null @@ -1,129 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: verify-traces-grpc - namespace: chainsaw-multitenancy -spec: - template: - spec: - containers: - - name: verify-traces - image: ghcr.io/grafana/tempo-operator/test-utils:main - command: - - /bin/bash - - -eux - - -c - args: - - | - token=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) - curl \ - -v -G \ - --header "Authorization: Bearer $token" \ - --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ - https://tempo-simplest-gateway.chainsaw-multitenancy.svc:8080/api/traces/v1/dev/api/traces \ - --data-urlencode "service=grpc" \ - | tee /tmp/jaeger.out - - num_traces=$(jq ".data | length" /tmp/jaeger.out) - if [[ "$num_traces" != "10" ]]; then - echo && echo "The Jaeger API returned $num_traces instead of 10 traces." - exit 1 - fi - restartPolicy: Never ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: verify-traces-traceql-grpc - namespace: chainsaw-multitenancy -spec: - template: - spec: - containers: - - name: verify-traces - image: ghcr.io/grafana/tempo-operator/test-utils:main - command: - - /bin/bash - - -eux - - -c - args: - - | - token=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) - curl \ - -v -G \ - --header "Authorization: Bearer $token" \ - --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ - https://tempo-simplest-gateway.chainsaw-multitenancy.svc:8080/api/traces/v1/dev/tempo/api/search \ - --data-urlencode 'q={ resource.service.name="grpc" }' \ - | tee /tmp/jaeger.out - num_traces=$(jq ".traces | length" /tmp/jaeger.out) - if [[ "$num_traces" != "10" ]]; then - echo && echo "The Jaeger API returned $num_traces instead of 10 traces." - exit 1 - fi - restartPolicy: Never ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: verify-traces-http - namespace: chainsaw-multitenancy -spec: - template: - spec: - containers: - - name: verify-traces - image: ghcr.io/grafana/tempo-operator/test-utils:main - command: - - /bin/bash - - -eux - - -c - args: - - | - token=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) - curl \ - -v -G \ - --header "Authorization: Bearer $token" \ - --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ - https://tempo-simplest-gateway.chainsaw-multitenancy.svc:8080/api/traces/v1/dev/api/traces \ - --data-urlencode "service=http" \ - | tee /tmp/jaeger.out - - num_traces=$(jq ".data | length" /tmp/jaeger.out) - if [[ "$num_traces" != "10" ]]; then - echo && echo "The Jaeger API returned $num_traces instead of 10 traces." - exit 1 - fi - restartPolicy: Never ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: verify-traces-traceql-http - namespace: chainsaw-multitenancy -spec: - template: - spec: - containers: - - name: verify-traces - image: ghcr.io/grafana/tempo-operator/test-utils:main - command: - - /bin/bash - - -eux - - -c - args: - - | - token=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) - curl \ - -v -G \ - --header "Authorization: Bearer $token" \ - --cacert /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt \ - https://tempo-simplest-gateway.chainsaw-multitenancy.svc:8080/api/traces/v1/dev/tempo/api/search \ - --data-urlencode 'q={ resource.service.name="http" }' \ - | tee /tmp/jaeger.out - num_traces=$(jq ".traces | length" /tmp/jaeger.out) - if [[ "$num_traces" != "10" ]]; then - echo && echo "The Jaeger API returned $num_traces instead of 10 traces." - exit 1 - fi - restartPolicy: Never diff --git a/tests/fixtures/chainsaw-tests/multitenancy/chainsaw-test.yaml b/tests/fixtures/chainsaw-tests/multitenancy/chainsaw-test.yaml deleted file mode 100755 index 2565e94..0000000 --- a/tests/fixtures/chainsaw-tests/multitenancy/chainsaw-test.yaml +++ /dev/null @@ -1,39 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/kyverno/chainsaw/main/.schemas/json/test-chainsaw-v1alpha1.json -apiVersion: chainsaw.kyverno.io/v1alpha1 -kind: Test -metadata: - creationTimestamp: null - name: multitenancy -spec: - namespace: chainsaw-multitenancy - steps: - - name: step-00 - try: - - apply: - file: 00-install-storage.yaml - - assert: - file: 00-assert.yaml - - name: step-01 - try: - - apply: - file: 01-install-tempo.yaml - - assert: - file: 01-assert.yaml - - name: step-02 - try: - - apply: - file: 02-install-otelcol.yaml - - assert: - file: 02-assert.yaml - - name: step-03 - try: - - apply: - file: 03-generate-traces.yaml - - assert: - file: 03-assert.yaml - - name: step-04 - try: - - apply: - file: 04-verify-traces.yaml - - assert: - file: 04-assert.yaml diff --git a/tests/package-lock.json b/tests/package-lock.json index 20bae2d..1943a6d 100644 --- a/tests/package-lock.json +++ b/tests/package-lock.json @@ -2231,9 +2231,9 @@ } }, "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "dependencies": { "balanced-match": "^1.0.0", @@ -2303,9 +2303,9 @@ "dev": true }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "dependencies": { "balanced-match": "^1.0.0", @@ -4456,9 +4456,9 @@ "dev": true }, "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dependencies": { "balanced-match": "^1.0.0" } @@ -6614,9 +6614,9 @@ } }, "node_modules/eslint-plugin-react/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "dependencies": { "balanced-match": "^1.0.0", @@ -6696,9 +6696,9 @@ } }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "dependencies": { "balanced-match": "^1.0.0", @@ -9825,9 +9825,9 @@ } }, "node_modules/matcher-collection/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "dependencies": { "balanced-match": "^1.0.0", @@ -15336,9 +15336,9 @@ } }, "node_modules/walk-sync/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "dependencies": { "balanced-match": "^1.0.0", diff --git a/tests/tests/dt-plugin-tests.cy.ts b/tests/tests/dt-plugin-tests.cy.ts deleted file mode 100644 index f800196..0000000 --- a/tests/tests/dt-plugin-tests.cy.ts +++ /dev/null @@ -1,348 +0,0 @@ -import { operatorHubPage } from '../views/operator-hub-page'; -import { Pages } from '../views/pages'; -import { searchPage } from '../views/search'; - -// Set constants for the operators that need to be installed for tests. -const DTP = { - namespace: 'openshift-cluster-observability-operator', - packageName: 'cluster-observability-operator', - operatorName: 'Cluster Observability Operator', - config: { - kind: 'UIPlugin', - name: 'distributed-tracing', - }, -}; - -const OTEL = { - namespace: 'openshift-opentelemetry-operator', - packageName: 'opentelemetry-product', - operatorName: 'Red Hat build of OpenTelemetry', -}; - -const TEMPO = { - namespace: 'openshift-tempo-operator', - packageName: 'tempo-product', - operatorName: 'Tempo Operator', -}; - -describe('OpenShift Distributed Tracing UI Plugin tests', () => { - before(() => { - cy.adminCLI( - `oc adm policy add-cluster-role-to-user cluster-admin ${Cypress.env('LOGIN_USERNAME')}`, - ); - // Getting the oauth url for hypershift cluster login - cy.exec( - `oc get oauthclient openshift-browser-client -o go-template --template="{{index .redirectURIs 0}}" --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, - ).then((result) => { - if (expect(result.stderr).to.be.empty) { - const oauth = result.stdout; - // Trimming the origin part of the url - const oauthurl = new URL(oauth); - const oauthorigin = oauthurl.origin; - cy.log(oauthorigin); - cy.wrap(oauthorigin).as('oauthorigin'); - } else { - throw new Error(`Execution of oc get oauthclient failed - Exit code: ${result.code} - Stdout:\n${result.stdout} - Stderr:\n${result.stderr}`); - } - }); - cy.get('@oauthorigin').then((oauthorigin) => { - cy.login( - Cypress.env('LOGIN_IDP'), - Cypress.env('LOGIN_USERNAME'), - Cypress.env('LOGIN_PASSWORD'), - oauthorigin, - ); - }); - - if (Cypress.env('SKIP_COO_INSTALL')) { - cy.log('SKIP_COO_INSTALL is set. Skipping Cluster Observability Operator installation.'); - } else if (Cypress.env('COO_UI_INSTALL')) { - cy.log('COO_UI_INSTALL is set. COO, Tempo and OpenTelemetry operators will be installed from redhat-operators catalog source'); - cy.log('Install Cluster Observability Operator'); - operatorHubPage.installOperator(DTP.packageName, 'redhat-operators'); - cy.get('.co-clusterserviceversion-install__heading', { timeout: 5 * 60 * 1000 }).should( - 'include.text', - 'ready for use', - ); - cy.log('Install OpenTelemetry Operator'); - operatorHubPage.installOperator(OTEL.packageName, 'redhat-operators'); - cy.get('.co-clusterserviceversion-install__heading', { timeout: 5 * 60 * 1000 }).should( - 'include.text', - 'ready for use', - ); - cy.log('Install Tempo Operator'); - operatorHubPage.installOperator(TEMPO.packageName, 'redhat-operators'); - cy.get('.co-clusterserviceversion-install__heading', { timeout: 5 * 60 * 1000 }).should( - 'include.text', - 'ready for use', - ); - } else if (Cypress.env('KONFLUX_COO_BUNDLE_IMAGE')) { - cy.log('KONFLUX_COO_BUNDLE_IMAGE is set. COO operator will be installed from Konflux bundle. Tempo and OpenTelemetry operators will be installed from redhat-operators catalog source'); - cy.log('Install Cluster Observability Operator'); - cy.exec( - `oc --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} apply -f ./fixtures/coo-imagecontentsourcepolicy.yaml` , - ); - cy.exec( - `oc create namespace ${DTP.namespace} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, - ); - cy.exec( - `oc label namespaces ${DTP.namespace} openshift.io/cluster-monitoring=true --overwrite=true --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, - ); - cy.exec( - `operator-sdk run bundle --timeout=10m --namespace ${DTP.namespace} ${Cypress.env('KONFLUX_COO_BUNDLE_IMAGE')} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} --verbose `, - { timeout: 6 * 60 * 1000 }, - ); - cy.log('Install OpenTelemetry Operator'); - operatorHubPage.installOperator(OTEL.packageName, 'redhat-operators'); - cy.get('.co-clusterserviceversion-install__heading', { timeout: 5 * 60 * 1000 }).should( - 'include.text', - 'ready for use', - ); - cy.log('Install Tempo Operator'); - operatorHubPage.installOperator(TEMPO.packageName, 'redhat-operators'); - cy.get('.co-clusterserviceversion-install__heading', { timeout: 5 * 60 * 1000 }).should( - 'include.text', - 'ready for use', - ); - } else if (Cypress.env('CUSTOM_COO_BUNDLE_IMAGE')) { - cy.log('CUSTOM_COO_BUNDLE_IMAGE is set. COO operator will be installed from custom built bundle. Tempo and OpenTelemetry operators will be installed from redhat-operators catalog source'); - cy.log('Install Cluster Observability Operator'); - cy.exec( - `oc --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} apply -f ./fixtures/coo-imagecontentsourcepolicy.yaml` , - ); - cy.exec( - `oc create namespace ${DTP.namespace} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, - ); - cy.exec( - `oc label namespaces ${DTP.namespace} openshift.io/cluster-monitoring=true --overwrite=true --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, - ); - cy.exec( - `operator-sdk run bundle --timeout=10m --namespace ${DTP.namespace} ${Cypress.env('CUSTOM_COO_BUNDLE_IMAGE')} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')} --verbose `, - { timeout: 6 * 60 * 1000 }, - ); - cy.log('Install OpenTelemetry Operator'); - operatorHubPage.installOperator(OTEL.packageName, 'redhat-operators'); - cy.get('.co-clusterserviceversion-install__heading', { timeout: 5 * 60 * 1000 }).should( - 'include.text', - 'ready for use', - ); - cy.log('Install Tempo Operator'); - operatorHubPage.installOperator(TEMPO.packageName, 'redhat-operators'); - cy.get('.co-clusterserviceversion-install__heading', { timeout: 5 * 60 * 1000 }).should( - 'include.text', - 'ready for use', - ); - } else { - throw new Error('No CYPRESS env set for operator installation, check the README for more details.'); - } - - cy.log('Set Distributed Tracing Console Plugin image in operator CSV'); - if (Cypress.env('DT_CONSOLE_IMAGE')) { - cy.log('DT_CONSOLE_IMAGE is set. the image will be patched in COO operator CSV'); - cy.exec( - './fixtures/update-plugin-image.sh', - { - env: { - DT_CONSOLE_IMAGE: Cypress.env('DT_CONSOLE_IMAGE'), - KUBECONFIG: Cypress.env('KUBECONFIG_PATH'), - DTP_NAMESPACE: `${DTP.namespace}` - }, - timeout: 120000, - failOnNonZeroExit: true - } - ) .then((result) => { - expect(result.code).to.eq(0); - cy.log(`COO CSV updated successfully with Distributed Tracing Console Plugin image: ${result.stdout}`); - }); - } else { - cy.log('DT_CONSOLE_IMAGE is NOT set. Skipping patching the image in COO operator CSV.'); - } - - cy.log('Create Distributed Tracing UI Plugin instance.'); - cy.exec(`oc apply -f ./fixtures/tracing-ui-plugin.yaml --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`); - cy.exec( - `sleep 15 && oc wait --for=condition=Ready pods --selector=app.kubernetes.io/instance=distributed-tracing -n ${DTP.namespace} --timeout=60s --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, - { - timeout: 80000, - failOnNonZeroExit: true - } - ).then((result) => { - expect(result.code).to.eq(0); - cy.log(`Distributed Tracing Console plugin pod is now running in namespace: ${DTP.namespace}`); - }); - cy.get('.pf-v5-c-alert, .pf-v6-c-alert', { timeout: 120000 }) - .contains('Web console update is available') - .then(($alert) => { - // If the alert is found, assert that it exists - expect($alert).to.exist; - }, () => { - // If the alert is not found within the timeout, visit and assert the /observe/traces page - cy.visit('/observe/traces'); - cy.url().should('include', '/observe/traces'); - }); - - }); - - after(() => { - if (Cypress.env('SKIP_COO_INSTALL')) { - cy.log('Delete Distributed Tracing UI Plugin instance.'); - cy.executeAndDelete( - `oc delete ${DTP.config.kind} ${DTP.config.name} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, - ); - - cy.log('Delete Chainsaw namespaces.'); - cy.executeAndDelete( - `oc delete project chainsaw-multitenancy chainsaw-monolithic-multitenancy --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, - ); - - cy.log('Remove cluster-admin role from user.'); - cy.executeAndDelete( - `oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, - ); - } else { - cy.log('Delete Distributed Tracing UI Plugin instance.'); - cy.executeAndDelete( - `oc delete ${DTP.config.kind} ${DTP.config.name} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, - ); - - cy.log('Delete Chainsaw namespaces.'); - cy.executeAndDelete( - `oc delete project chainsaw-multitenancy chainsaw-monolithic-multitenancy --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, - ); - - cy.log('Remove Cluster Observability Operator'); - cy.executeAndDelete(`oc delete namespace ${DTP.namespace} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`); - - cy.log('Remove OpenTelemetry Operator'); - cy.executeAndDelete(`oc delete namespace ${OTEL.namespace} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`); - - cy.log('Remove Tempo Operator'); - cy.executeAndDelete(`oc delete namespace ${TEMPO.namespace} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`); - - cy.log('Remove cluster-admin role from user.'); - cy.executeAndDelete( - `oc adm policy remove-cluster-role-from-user cluster-admin ${Cypress.env('LOGIN_USERNAME')} --kubeconfig ${Cypress.env('KUBECONFIG_PATH')}`, - ); - } - }); - - // Tests start from here. - it('Test Distributed Tracing UI plugin page without any Tempo instances', () => { - cy.log('Navigate to the observe/traces page'); - cy.visit('/observe/traces'); - - cy.log('Assert that the Traces page shows the empty state.'); - cy.get('.pf-v6-c-empty-state__title-text') - .should('be.visible') - .and('have.text', 'No Tempo instances yet'); - - cy.log('Assert that the View documentation button is visible.'); - cy.contains('.pf-v6-c-button', 'View documentation') - .should('be.visible') - .and('have.text', 'View documentation'); - - cy.log('Assert create a tempo instance toggle visibility and text.'); - const createTempoToggle = cy.contains('.pf-v6-c-menu-toggle', 'Create a Tempo instance'); - createTempoToggle.should('be.visible'); - - cy.log('Click the toggle to show creation options.'); - createTempoToggle.click(); - - cy.log('Assert dropdown items for Tempo instance creation are visible.'); - cy.contains('.pf-v6-c-menu__item-text', 'Create a TempoStack instance') - .should('be.visible') - .and('have.text', 'Create a TempoStack instance'); - - cy.contains('.pf-v6-c-menu__item-text', 'Create a TempoMonolithic instance') - .should('be.visible') - .and('have.text', 'Create a TempoMonolithic instance'); - }); - - it('(Test Distributed Tracing UI plugin with Tempo instances and verify traces)', function () { - cy.log('Create TempoStack and TempoMonolithic instances'); - cy.exec( - 'chainsaw test --config ./fixtures/.chainsaw.yaml --skip-delete ./fixtures/chainsaw-tests/multitenancy ./fixtures/chainsaw-tests/monolithic-multitenancy-openshift', - { - env: { - KUBECONFIG: Cypress.env('KUBECONFIG_PATH'), - }, - timeout: 1800000, - failOnNonZeroExit: true - } - ) .then((result) => { - expect(result.code).to.eq(0); - cy.log(`Chainsaw test ran successfully: ${result.stdout}`); - }); - cy.log('Navigate to the observe/traces page'); - cy.visit('/observe/traces'); - - cy.log('Assert traces in TempoStack instance.'); - cy.get(':nth-child(1) > .pf-v6-c-toolbar__item > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__button > .pf-v6-c-menu-toggle__controls > .pf-v6-c-menu-toggle__toggle-icon').click(); - cy.get(':nth-child(2) > .pf-v6-c-menu__item > .pf-v6-c-menu__item-main > .pf-v6-c-menu__item-text').click(); - cy.get(':nth-child(1) > :nth-child(2) > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__button').click(); - cy.get(':nth-child(2) > .pf-v6-c-menu__item > .pf-v6-c-menu__item-main > .pf-v6-c-menu__item-text').click(); - cy.get(':nth-child(2) > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__controls > .pf-v6-c-menu-toggle__toggle-icon').click(); - cy.get(':nth-child(1) > .pf-v6-c-menu__item > .pf-v6-c-menu__item-main > .pf-v6-c-menu__item-text').click(); - cy.get(':nth-child(1) > :nth-child(2) > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__button > .pf-v6-c-menu-toggle__controls > .pf-v6-c-menu-toggle__toggle-icon').click(); - cy.get(':nth-child(1) > .pf-v6-c-menu__item > .pf-v6-c-menu__item-main > .pf-v6-c-menu__item-text').click(); - cy.get('.pf-v6-c-toolbar__group > :nth-child(1) > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__controls > .pf-v6-c-menu-toggle__toggle-icon').click(); - cy.get(':nth-child(1) > .pf-v6-c-menu__item > .pf-v6-c-menu__item-main > .pf-v6-c-menu__item-text').click(); - cy.get('.pf-m-toggle-group > .pf-v6-c-toolbar__group > :nth-child(2) > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__button').click(); - cy.contains('.pf-v6-c-menu__item-text', 'http') - .closest('.pf-v6-c-menu__item') - .find('input[type="checkbox"]') - .check(); - cy.contains('.pf-v6-c-menu__item-text', 'grpc') - .closest('.pf-v6-c-menu__item') - .find('input[type="checkbox"]') - .check(); - cy.get('.MuiDataGrid-row--firstVisible > [data-field="name"] > .MuiBox-root > .MuiTypography-root').click(); - cy.contains('div', 'okey-dokey').click({ force: true }); - cy.get('.css-1bmckj4').then(($el) => { - cy.log(`Actual text in .css-1bmckj4 (TempoMonolithic): ${$el.text()}`); - expect($el.text()).to.satisfy((text) => text === 'http' || text === 'grpc'); - }); - cy.get('.MuiTypography-h2').should('have.text', 'okey-dokey'); - cy.get('.MuiTabs-list > .MuiButtonBase-root').should('be.visible'); - cy.get(':nth-child(5) > :nth-child(1) > .MuiListItemText-root > .MuiTypography-h5').should('have.text', 'net.peer.ip'); - cy.get(':nth-child(5) > :nth-child(1) > .MuiListItemText-root > .MuiTypography-body1').should('have.text', '1.2.3.4'); - cy.get(':nth-child(2) > .MuiListItemText-root > .MuiTypography-h5').should('have.text', 'peer.service'); - cy.get(':nth-child(2) > .MuiListItemText-root > .MuiTypography-body1').should('have.text', 'telemetrygen-client'); - cy.get(':nth-child(7) > .MuiListItem-root > .MuiListItemText-root > .MuiTypography-h5').should('have.text', 'service.name'); - cy.get(':nth-child(7) > .MuiListItem-root > .MuiListItemText-root > .MuiTypography-body1').then(($el) => { - cy.log(`Actual text in service.name (TempoStack): ${$el.text()}`); - expect($el.text()).to.satisfy((text) => text === 'http' || text === 'grpc'); - }); - cy.get('.pf-v6-c-breadcrumb__list > :nth-child(1) > a').click(); - - cy.log('Assert traces in TempoMonolithic instance.'); - cy.get(':nth-child(1) > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle > .pf-v6-c-menu-toggle__button').click(); - cy.get(':nth-child(1) > .pf-v6-c-menu__item > .pf-v6-c-menu__item-main > .pf-v6-c-menu__item-text').click(); - cy.get(':nth-child(2) > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-menu-toggle').click(); - cy.get(':nth-child(3) > .pf-v6-c-menu__item > .pf-v6-c-menu__item-main > .pf-v6-c-menu__item-text').click(); - cy.get('.pf-v6-c-form__group-control > .pf-v6-c-button > .pf-v6-c-button__text').click(); - cy.get('.pf-m-toggle-group > .pf-m-action-group > .pf-v6-c-toolbar__item > .pf-v6-c-form > .pf-v6-c-form__group > .pf-v6-c-form__group-control > .pf-v6-c-button > .pf-v6-c-button__text').click(); - cy.get('.MuiDataGrid-row--firstVisible > [data-field="name"] > .MuiBox-root > .MuiTypography-root').click(); - cy.contains('div', 'okey-dokey').click({ force: true }); - cy.get('.css-1bmckj4').then(($el) => { - cy.log(`Actual text in .css-1bmckj4 (TempoMonolithic): ${$el.text()}`); - expect($el.text()).to.satisfy((text) => text === 'http' || text === 'grpc'); - }); - cy.get('.MuiTypography-h2').should('have.text', 'okey-dokey'); - cy.get('.MuiTabs-list > .MuiButtonBase-root').should('be.visible'); - cy.get(':nth-child(5) > :nth-child(1) > .MuiListItemText-root > .MuiTypography-h5').should('have.text', 'net.peer.ip'); - cy.get(':nth-child(5) > :nth-child(1) > .MuiListItemText-root > .MuiTypography-body1').should('have.text', '1.2.3.4'); - cy.get(':nth-child(2) > .MuiListItemText-root > .MuiTypography-h5').should('have.text', 'peer.service'); - cy.get(':nth-child(2) > .MuiListItemText-root > .MuiTypography-body1').should('have.text', 'telemetrygen-client'); - cy.get(':nth-child(7) > .MuiListItem-root > .MuiListItemText-root > .MuiTypography-h5').should('have.text', 'service.name'); - cy.get(':nth-child(7) > .MuiListItem-root > .MuiListItemText-root > .MuiTypography-body1').then(($el) => { - cy.log(`Actual text in service.name (TempoMonolithic): ${$el.text()}`); - expect($el.text()).to.satisfy((text) => text === 'http' || text === 'grpc'); - }); - cy.get('.pf-v6-c-breadcrumb__list > :nth-child(1) > a').click(); - }); - -}); From 7466bf14ebd62d5e857f54874013ce3b6277bf16 Mon Sep 17 00:00:00 2001 From: Ishwar Kanse Date: Fri, 18 Jul 2025 14:44:06 +0530 Subject: [PATCH 15/35] Improve Tracing UI plugin tests --- tests/PATTERNFLY_COMMANDS_EXAMPLES.md | 185 +++++++++++--- tests/README.md | 69 +++++ tests/SELECTOR_BEST_PRACTICES.md | 118 ++++++++- tests/cypress/support/commands.ts | 284 +++++++++++++++++++-- tests/e2e/dt-plugin-tests-debug.cy.ts.skip | 35 +++ tests/e2e/dt-plugin-tests.cy.ts | 237 ++++++----------- 6 files changed, 721 insertions(+), 207 deletions(-) create mode 100644 tests/e2e/dt-plugin-tests-debug.cy.ts.skip diff --git a/tests/PATTERNFLY_COMMANDS_EXAMPLES.md b/tests/PATTERNFLY_COMMANDS_EXAMPLES.md index 4cc0cdd..7dca022 100644 --- a/tests/PATTERNFLY_COMMANDS_EXAMPLES.md +++ b/tests/PATTERNFLY_COMMANDS_EXAMPLES.md @@ -1,6 +1,6 @@ # PatternFly Cypress Commands - Usage Examples -This file demonstrates how to properly use the PatternFly-aware Cypress commands to avoid React state management issues. +This file provides comprehensive examples of PatternFly-aware Cypress commands for distributed tracing UI testing, focusing on semantic selectors and efficient testing patterns. ## ✅ Recommended Usage Patterns @@ -61,50 +61,169 @@ cy.byLabelText('Namespace').select('default'); cy.pfButton('Submit').click(); ``` -## ⚠️ Patterns to Avoid +## 🆕 New Distributed Tracing UI Commands -### 1. Complex Chained Operations +### Menu & Dropdown Interactions ```typescript -// ❌ AVOID: Too many chained operations without waits -cy.pfToolbarItem(0).within(() => { - cy.pfMenuToggle().click(); -}).then(() => { - cy.pfMenuItem('Item').click(); -}).then(() => { - cy.pfToolbarItem(1).within(() => { - cy.pfMenuToggle().click(); +// Tempo instance selection +cy.pfTypeahead('Select a Tempo instance').click(); +cy.pfSelectMenuItem('chainsaw-rbac / simplst').click(); + +// Service filtering with checkboxes +cy.pfMenuToggleByLabel('Multi typeahead checkbox').click(); +cy.pfCheckMenuItem('http-rbac-1'); +cy.pfCheckMenuItem('http-rbac-2', true); // Explicitly check +cy.pfCheckMenuItem('grpc-rbac-1', false); // Uncheck + +// Time range selection +cy.pfMenuToggle('Last 30 minutes').click(); +cy.pfSelectMenuItem('Last 1 hour').click(); +``` + +### Navigation Commands + +```typescript +// Breadcrumb navigation +cy.pfBreadcrumb('Traces').click(); +cy.pfBreadcrumb('Observability').should('be.visible'); + +// Close buttons (chip groups, modals, etc.) +cy.pfCloseButton('Close chip group').click(); // PatternFly 5 +cy.pfCloseButton('Close label group').click(); // PatternFly 6 +cy.pfCloseButton().click(); // First close button found (any version) +``` + +### Trace & Span Interactions + +```typescript +// Click on traces +cy.muiFirstTraceLink().click(); // First trace in list +cy.muiTraceLink('http-rbac-2').click(); // Specific service trace + +// Interact with span bars +cy.muiFirstSpanBar().click(); // First span bar +cy.muiSpanBar('http-rbac-2').click(); // Specific service span +cy.findByTestId('span-duration-bar').first().click(); // By test ID (multiple spans) +``` + +### Trace Attribute Validation + +```typescript +// Single attribute validation +cy.muiTraceAttribute('net.peer.ip', '1.2.3.4'); +cy.muiTraceAttribute('peer.service', 'telemetrygen-client'); +cy.muiTraceAttribute('k8s.container.name', 'telemetrygen', true); // Optional attribute + +// Custom validation with function +cy.muiTraceAttribute('service.name', (text) => { + return ['http-rbac-1', 'http-rbac-2', 'grpc-rbac-1', 'grpc-rbac-2'].includes(text); +}, false, 'TempoStack'); // Required attribute with logging + +// Bulk attribute validation (recommended for multiple attributes) +cy.muiTraceAttributes({ + 'net.peer.ip': { value: '1.2.3.4' }, + 'peer.service': { value: 'telemetrygen-client' }, + 'k8s.container.name': { + value: 'telemetrygen', + optional: true + }, + 'k8s.namespace.name': { + value: (text) => text.startsWith('chainsaw-'), + optional: true + }, + 'service.name': { + value: ['http-rbac-1', 'http-rbac-2', 'grpc-rbac-1', 'grpc-rbac-2'] + } +}, 'Debug'); // Log prefix for debugging +``` + +### Complete Workflow Examples + +```typescript +// Complete trace inspection workflow +describe('Trace inspection', () => { + it('should navigate and validate trace details', () => { + // Navigate to traces page + cy.visit('/observe/traces'); + + // Select Tempo instance + cy.pfTypeahead('Select a Tempo instance').click(); + cy.pfSelectMenuItem('chainsaw-rbac / simplst').click(); + + // Set time range + cy.pfMenuToggle('Last 30 minutes').click(); + cy.pfSelectMenuItem('Last 1 hour').click(); + + // Filter by services + cy.pfMenuToggleByLabel('Multi typeahead checkbox').click(); + cy.pfCheckMenuItem('http-rbac-1'); + cy.pfCheckMenuItem('http-rbac-2'); + + // Click first trace + cy.muiFirstTraceLink().click(); + + // Click on span for details + cy.muiFirstSpanBar().click(); + + // Validate trace attributes efficiently + cy.muiTraceAttributes({ + 'net.peer.ip': { value: '1.2.3.4' }, + 'peer.service': { value: 'telemetrygen-client' }, + 'service.name': { + value: (text) => text.includes('rbac') + } + }); + + // Navigate back + cy.pfBreadcrumb('Traces').click(); }); }); +``` -// ✅ BETTER: Break into separate operations with waits -cy.pfToolbarItem(0).within(() => { - cy.get('.pf-v6-c-menu-toggle').first().click(); -}); -cy.get('.pf-v6-c-menu__item').contains('Item').click(); -cy.wait(1000); // Allow React state to stabilize -cy.pfToolbarItem(1).within(() => { - cy.get('.pf-v6-c-menu-toggle').first().click(); -}); +## 🔄 PatternFly 5 & 6 Compatibility + +Our commands seamlessly support both PatternFly 5 and PatternFly 6 components: + +### Close Buttons + +```typescript +// PatternFly 5 - Chip Group +// From 4c7c0af77b8adef3496d86a204f63af22d7b2d42 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 9 Oct 2025 15:03:01 +0200 Subject: [PATCH 25/35] COO-1017: Support pressing ENTER in filter bar Previously the was submitted, triggering a full-page refresh. Signed-off-by: Andreas Gerstmayr --- web/src/pages/TracesPage/Toolbar/AttributeFilters.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/src/pages/TracesPage/Toolbar/AttributeFilters.tsx b/web/src/pages/TracesPage/Toolbar/AttributeFilters.tsx index 150a347..48e4f0c 100644 --- a/web/src/pages/TracesPage/Toolbar/AttributeFilters.tsx +++ b/web/src/pages/TracesPage/Toolbar/AttributeFilters.tsx @@ -203,7 +203,7 @@ function TypeaheadStringAttributeFilter(props: TypeaheadStringAttributeFilterPro categoryName={filterName} showToolbarItem={show} > - + e.preventDefault()}>  } labelHelp={labelHelp}> - + e.preventDefault()}> -
+ e.preventDefault()}> - + e.preventDefault()}> Date: Thu, 9 Oct 2025 15:09:28 +0200 Subject: [PATCH 26/35] NO-JIRA: be explicit about unsupported instances without multi-tenancy Signed-off-by: Andreas Gerstmayr --- .../plugin__distributed-tracing-console-plugin.json | 3 +-- web/src/pages/TracesPage/TracesPage.tsx | 11 +++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/web/locales/en/plugin__distributed-tracing-console-plugin.json b/web/locales/en/plugin__distributed-tracing-console-plugin.json index 293f452..aceec2b 100644 --- a/web/locales/en/plugin__distributed-tracing-console-plugin.json +++ b/web/locales/en/plugin__distributed-tracing-console-plugin.json @@ -21,7 +21,6 @@ "Trace": "Trace", "Tracing": "Tracing", "Limit traces": "Limit traces", - "No datapoints found.": "No datapoints found.", "Hide graph": "Hide graph", "Show graph": "Show graph", "Filter": "Filter", @@ -47,7 +46,7 @@ "To get started, install the Tempo operator and create a TempoStack or TempoMonolithic instance with multi-tenancy enabled.": "To get started, install the Tempo operator and create a TempoStack or TempoMonolithic instance with multi-tenancy enabled.", "Install Tempo operator": "Install Tempo operator", "No Tempo instances yet": "No Tempo instances yet", - "To get started, create a TempoStack or TempoMonolithic instance with multi-tenancy enabled.": "To get started, create a TempoStack or TempoMonolithic instance with multi-tenancy enabled.", + "To get started, create a TempoStack or TempoMonolithic instance with multi-tenancy enabled.<1>Instances without multi-tenancy are not supported.": "To get started, create a TempoStack or TempoMonolithic instance with multi-tenancy enabled.<1>Instances without multi-tenancy are not supported.", "Create a Tempo instance": "Create a Tempo instance", "Create a TempoStack instance": "Create a TempoStack instance", "Create a TempoMonolithic instance": "Create a TempoMonolithic instance", diff --git a/web/src/pages/TracesPage/TracesPage.tsx b/web/src/pages/TracesPage/TracesPage.tsx index 0e480a2..78db2eb 100644 --- a/web/src/pages/TracesPage/TracesPage.tsx +++ b/web/src/pages/TracesPage/TracesPage.tsx @@ -15,7 +15,7 @@ import { Title, } from '@patternfly/react-core'; import { PlusCircleIcon, WrenchIcon, ExternalLinkAltIcon } from '@patternfly/react-icons'; -import { useTranslation } from 'react-i18next'; +import { useTranslation, Trans } from 'react-i18next'; import { ErrorAlert } from '../../components/ErrorAlert'; import { Helmet } from 'react-helmet'; import { Link } from 'react-router-dom-v5-compat'; @@ -125,9 +125,12 @@ function NoTempoInstance() { - {t( - 'To get started, create a TempoStack or TempoMonolithic instance with multi-tenancy enabled.', - )} + + To get started, create a TempoStack or TempoMonolithic instance with multi-tenancy + enabled. +
+ Instances without multi-tenancy are not supported. +
From 8179b35be871905c4b8f7917235d1e78a9856938 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 9 Oct 2025 14:14:40 +0200 Subject: [PATCH 27/35] COO-1118: Show an actionable error message if the namespace filter is empty Signed-off-by: Andreas Gerstmayr --- .../plugin__distributed-tracing-console-plugin.json | 2 +- web/src/components/TypeaheadCheckboxSelect.tsx | 5 +++-- .../pages/TracesPage/Toolbar/AttributeFilters.tsx | 13 ++++++++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/web/locales/en/plugin__distributed-tracing-console-plugin.json b/web/locales/en/plugin__distributed-tracing-console-plugin.json index 293f452..bad7121 100644 --- a/web/locales/en/plugin__distributed-tracing-console-plugin.json +++ b/web/locales/en/plugin__distributed-tracing-console-plugin.json @@ -21,12 +21,12 @@ "Trace": "Trace", "Tracing": "Tracing", "Limit traces": "Limit traces", - "No datapoints found.": "No datapoints found.", "Hide graph": "Hide graph", "Show graph": "Show graph", "Filter": "Filter", "Filter by namespace": "Filter by namespace", "This filter is based on the <1>k8s.namespace.name resource attribute. To set this attribute, it is recommended to enable the <4>Kubernetes Attributes Processor in your OpenTelemetry Collector pipeline.": "This filter is based on the <1>k8s.namespace.name resource attribute. To set this attribute, it is recommended to enable the <4>Kubernetes Attributes Processor in your OpenTelemetry Collector pipeline.", + "No results found. Please ensure that the <2>Kubernetes Attributes Processor<3>is enabled in your OpenTelemetry collector pipeline.": "No results found. Please ensure that the <2>Kubernetes Attributes Processor<3>is enabled in your OpenTelemetry collector pipeline.", "between {{min}} and {{max}}": "between {{min}} and {{max}}", "greater than {{min}}": "greater than {{min}}", "less than {{max}}": "less than {{max}}", diff --git a/web/src/components/TypeaheadCheckboxSelect.tsx b/web/src/components/TypeaheadCheckboxSelect.tsx index 408ca39..550dc98 100644 --- a/web/src/components/TypeaheadCheckboxSelect.tsx +++ b/web/src/components/TypeaheadCheckboxSelect.tsx @@ -16,6 +16,7 @@ import { TypeaheadSelectOption } from '@patternfly/react-templates'; export interface TypeaheadCheckboxSelectProps { placeholder: string; + noResultsFoundText?: React.ReactNode; toggleWidth?: string; isCreatable?: boolean; style?: CSSProperties; @@ -102,7 +103,7 @@ export function TypeaheadCheckboxSelect(props: TypeaheadCheckboxSelectProps) { newSelectOptions = [ { isAriaDisabled: true, - children: `No results found`, + children: props.noResultsFoundText ?? `No results found`, value: NO_RESULTS, hasCheckbox: false, }, @@ -110,7 +111,7 @@ export function TypeaheadCheckboxSelect(props: TypeaheadCheckboxSelectProps) { } setSelectOptions(newSelectOptions); - }, [inputValue, initialSelectOptions, isOpen, props.isCreatable]); + }, [inputValue, initialSelectOptions, isOpen, props.isCreatable, props.noResultsFoundText]); const createItemId = (value: string) => `select-multi-typeahead-${value.replace(' ', '-')}`; diff --git a/web/src/pages/TracesPage/Toolbar/AttributeFilters.tsx b/web/src/pages/TracesPage/Toolbar/AttributeFilters.tsx index 150a347..aac47d8 100644 --- a/web/src/pages/TracesPage/Toolbar/AttributeFilters.tsx +++ b/web/src/pages/TracesPage/Toolbar/AttributeFilters.tsx @@ -148,6 +148,14 @@ export function AttributeFilters(props: AttributeFiltersProps) { } + noResultsFoundText={ + + No results found. Please ensure that the{' '} + Kubernetes Attributes Processor +
+ is enabled in your OpenTelemetry collector pipeline. +
+ } show={activeFilter === namespaceFilter.value} options={namespaceOptions ?? []} value={filter.namespace} @@ -186,6 +194,7 @@ interface TypeaheadStringAttributeFilterProps { filterName: string; label?: React.ReactNode; labelHelp?: React.ReactElement; + noResultsFoundText?: React.ReactNode; show?: boolean; options: TypeaheadSelectOption[]; value: string[]; @@ -193,7 +202,8 @@ interface TypeaheadStringAttributeFilterProps { } function TypeaheadStringAttributeFilter(props: TypeaheadStringAttributeFilterProps) { - const { filterName, label, labelHelp, show, options, value, setValue } = props; + const { filterName, label, labelHelp, noResultsFoundText, show, options, value, setValue } = + props; return ( 0 ? ' (' + value.length + ')' : '' }`} + noResultsFoundText={noResultsFoundText} options={options} value={value} setValue={setValue} From a544bfcdaf08860f36c250692e2914faf019ba68 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 9 Oct 2025 15:33:49 +0200 Subject: [PATCH 28/35] TRACING-5455: Do not sort select box items based on their selection status Changing the order of the select box items if any item is selected may be more confusing than helping. Signed-off-by: Andreas Gerstmayr --- web/src/components/TypeaheadCheckboxSelect.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/web/src/components/TypeaheadCheckboxSelect.tsx b/web/src/components/TypeaheadCheckboxSelect.tsx index 408ca39..b87cc55 100644 --- a/web/src/components/TypeaheadCheckboxSelect.tsx +++ b/web/src/components/TypeaheadCheckboxSelect.tsx @@ -45,10 +45,7 @@ export function TypeaheadCheckboxSelect(props: TypeaheadCheckboxSelectProps) { } } - // selected items should be on top - return initialOptions.sort((a, b) => - `${a.selected ? 0 : 1}${a.value}`.localeCompare(`${b.selected ? 0 : 1}${b.value}`), - ); + return initialOptions; }, [props.options, props.value, props.isCreatable]); const [isOpen, setIsOpen] = useState(false); From f14c16975164d4bf11fcb034e5e21d14b45f58e1 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 9 Oct 2025 20:33:13 +0200 Subject: [PATCH 29/35] NO-JIRA: Enable MUI CSS variables, upgrade @patternfly/react-charts Signed-off-by: Andreas Gerstmayr --- web/package-lock.json | 598 +++++++++++++----- web/package.json | 8 +- web/src/components/PersesWrapper.css | 6 + web/src/components/PersesWrapper.tsx | 28 +- .../pages/TraceDetailPage/TraceDetailPage.tsx | 7 +- web/src/pages/TracesPage/QueryBrowser.tsx | 4 +- 6 files changed, 460 insertions(+), 191 deletions(-) create mode 100644 web/src/components/PersesWrapper.css diff --git a/web/package-lock.json b/web/package-lock.json index 9c57efa..804e8d4 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -13,7 +13,7 @@ "@emotion/styled": "^11.11.0", "@grafana/lezer-traceql": "^0.0.22", "@hookform/resolvers": "^3.3.4", - "@patternfly/react-charts": "6.92.0", + "@patternfly/react-charts": "^8.2.0", "@patternfly/react-core": "^6.2.0", "@patternfly/react-icons": "^5.4.2", "@patternfly/react-templates": "^6.2.2", @@ -31,7 +31,8 @@ "i18next-parser": "^8.13.0", "lodash": "^4.17.21", "pluralize": "^8.0.0", - "use-query-params": "^2.2.1" + "use-query-params": "^2.2.1", + "victory": "^37.3.6" }, "devDependencies": { "@cypress/webpack-preprocessor": "^5.15.5", @@ -5540,36 +5541,94 @@ } }, "node_modules/@patternfly/react-charts": { - "version": "6.92.0", - "resolved": "https://registry.npmjs.org/@patternfly/react-charts/-/react-charts-6.92.0.tgz", - "integrity": "sha512-aQl+L7zH/tWX00L2xI6B0414xweaIocR98wbyhXLi6zknkhcAUcE2zITFIW4+EsU+qPvUlptwRiRufoH8/BlEw==", + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/@patternfly/react-charts/-/react-charts-8.3.1.tgz", + "integrity": "sha512-5EPc4Bl7ZhZiDnFZzgyRhCkFcC0Km3fbC8PLjAxAf/prcWNCKXNHc6dtP38rYfrwP8R8Y831gF2bYg1uTbpKHQ==", "license": "MIT", "dependencies": { - "@patternfly/react-styles": "^4.89.0", - "@patternfly/react-tokens": "^4.91.0", - "hoist-non-react-statics": "^3.3.0", - "lodash": "^4.17.19", - "tslib": "^2.0.0", - "victory-area": "^36.2.1", - "victory-axis": "^36.2.1", - "victory-bar": "^36.2.1", - "victory-chart": "^36.2.1", - "victory-core": "^36.2.1", - "victory-create-container": "^36.2.1", - "victory-cursor-container": "^36.2.1", - "victory-group": "^36.2.1", - "victory-legend": "^36.2.1", - "victory-line": "^36.2.1", - "victory-pie": "^36.2.1", - "victory-scatter": "^36.2.1", - "victory-stack": "^36.2.1", - "victory-tooltip": "^36.2.1", - "victory-voronoi-container": "^36.2.1", - "victory-zoom-container": "^36.2.1" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0", - "react-dom": "^16.8.0 || ^17.0.0" + "@patternfly/react-styles": "^6.3.1", + "@patternfly/react-tokens": "^6.3.1", + "hoist-non-react-statics": "^3.3.2", + "lodash": "^4.17.21", + "tslib": "^2.8.1" + }, + "peerDependencies": { + "echarts": "^5.6.0", + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19", + "victory-area": "^37.3.6", + "victory-axis": "^37.3.6", + "victory-bar": "^37.3.6", + "victory-box-plot": "^37.3.6", + "victory-chart": "^37.3.6", + "victory-core": "^37.3.6", + "victory-create-container": "^37.3.6", + "victory-cursor-container": "^37.3.6", + "victory-group": "^37.3.6", + "victory-legend": "^37.3.6", + "victory-line": "^37.3.6", + "victory-pie": "^37.3.6", + "victory-scatter": "^37.3.6", + "victory-stack": "^37.3.6", + "victory-tooltip": "^37.3.6", + "victory-voronoi-container": "^37.3.6", + "victory-zoom-container": "^37.3.6" + }, + "peerDependenciesMeta": { + "echarts": { + "optional": true + }, + "victory-area": { + "optional": true + }, + "victory-axis": { + "optional": true + }, + "victory-bar": { + "optional": true + }, + "victory-box-plot": { + "optional": true + }, + "victory-chart": { + "optional": true + }, + "victory-core": { + "optional": true + }, + "victory-create-container": { + "optional": true + }, + "victory-cursor-container": { + "optional": true + }, + "victory-group": { + "optional": true + }, + "victory-legend": { + "optional": true + }, + "victory-line": { + "optional": true + }, + "victory-pie": { + "optional": true + }, + "victory-scatter": { + "optional": true + }, + "victory-stack": { + "optional": true + }, + "victory-tooltip": { + "optional": true + }, + "victory-voronoi-container": { + "optional": true + }, + "victory-zoom-container": { + "optional": true + } } }, "node_modules/@patternfly/react-core": { @@ -5600,18 +5659,6 @@ "react-dom": "^17 || ^18" } }, - "node_modules/@patternfly/react-core/node_modules/@patternfly/react-styles": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-6.2.2.tgz", - "integrity": "sha512-rncRDq66H8VnLyb9DrHHlZtPddlpNL9+W0XuQC0L7F6p78hOwSZmoGTW2Vq8/wJplDj8h/61qRpfRF9VEYPW0g==", - "license": "MIT" - }, - "node_modules/@patternfly/react-core/node_modules/@patternfly/react-tokens": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-6.2.2.tgz", - "integrity": "sha512-2GRWDPBTrcTlGNFc5NPJjrjEVU90RpgcGX/CIe2MplLgM32tpVIkeUtqIoJPLRk5GrbhyFuHJYRU+O93gU4o3Q==", - "license": "MIT" - }, "node_modules/@patternfly/react-icons": { "version": "5.4.2", "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-5.4.2.tgz", @@ -5623,9 +5670,9 @@ } }, "node_modules/@patternfly/react-styles": { - "version": "4.92.8", - "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-4.92.8.tgz", - "integrity": "sha512-K4lUU8O4HiCX9NeuNUIrPgN3wlGERCxJVio+PAjd8hpJD/PKnjFfOJ9u6/Cii3qLy/5ZviWPRNHbGiwA/+YUhg==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-6.3.1.tgz", + "integrity": "sha512-hyb+PlO8YITjKh2wBvjdeZhX6FyB3hlf4r6yG4rPOHk4SgneXHjNSdGwQ3szAxgGqtbENCYtOqwD/8ai72GrxQ==", "license": "MIT" }, "node_modules/@patternfly/react-templates": { @@ -5655,22 +5702,10 @@ "react-dom": "^17 || ^18" } }, - "node_modules/@patternfly/react-templates/node_modules/@patternfly/react-styles": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-6.2.2.tgz", - "integrity": "sha512-rncRDq66H8VnLyb9DrHHlZtPddlpNL9+W0XuQC0L7F6p78hOwSZmoGTW2Vq8/wJplDj8h/61qRpfRF9VEYPW0g==", - "license": "MIT" - }, - "node_modules/@patternfly/react-templates/node_modules/@patternfly/react-tokens": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-6.2.2.tgz", - "integrity": "sha512-2GRWDPBTrcTlGNFc5NPJjrjEVU90RpgcGX/CIe2MplLgM32tpVIkeUtqIoJPLRk5GrbhyFuHJYRU+O93gU4o3Q==", - "license": "MIT" - }, "node_modules/@patternfly/react-tokens": { - "version": "4.94.7", - "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-4.94.7.tgz", - "integrity": "sha512-h+ducOLDMSxcuec3+YY3x+stM5ZUSnrl/lC/eVmjypil2El08NuE2MNEPMQWdhrod6VRRZFMNqZw/m82iv6U1A==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-6.3.1.tgz", + "integrity": "sha512-wt/xKU1tGCDXUueFb+8/Cwxlm4vUD/Xl26O8MxbSLm6NZAHOUPwytJ7gugloGSPvc/zcsXxEgKANL8UZNO6DTw==", "license": "MIT" }, "node_modules/@patternfly/react-topology": { @@ -5712,13 +5747,6 @@ "react-dom": "^17 || ^18" } }, - "node_modules/@patternfly/react-topology/node_modules/@patternfly/react-styles": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-6.2.2.tgz", - "integrity": "sha512-rncRDq66H8VnLyb9DrHHlZtPddlpNL9+W0XuQC0L7F6p78hOwSZmoGTW2Vq8/wJplDj8h/61qRpfRF9VEYPW0g==", - "dev": true, - "license": "MIT" - }, "node_modules/@perses-dev/components": { "version": "0.52.0", "resolved": "https://registry.npmjs.org/@perses-dev/components/-/components-0.52.0.tgz", @@ -11025,6 +11053,12 @@ "d3-selection": "2 - 3" } }, + "node_modules/d3-voronoi": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz", + "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==", + "license": "BSD-3-Clause" + }, "node_modules/d3-zoom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", @@ -11614,13 +11648,13 @@ } }, "node_modules/echarts": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.0.tgz", - "integrity": "sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.6.0.tgz", + "integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==", "license": "Apache-2.0", "dependencies": { "tslib": "2.3.0", - "zrender": "5.5.0" + "zrender": "5.6.1" } }, "node_modules/echarts/node_modules/tslib": { @@ -27372,265 +27406,462 @@ "node": ">= 0.10" } }, + "node_modules/victory": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory/-/victory-37.3.6.tgz", + "integrity": "sha512-CZ1vjvra0R1U3T2dMI4EsjI8Ng+JmQ2ox/EweSzjkTnHfW/Vn5ylryadawDiYjDMcBvABjO3uODsIlSEm4d/Sw==", + "license": "MIT", + "dependencies": { + "victory-area": "37.3.6", + "victory-axis": "37.3.6", + "victory-bar": "37.3.6", + "victory-box-plot": "37.3.6", + "victory-brush-container": "37.3.6", + "victory-brush-line": "37.3.6", + "victory-candlestick": "37.3.6", + "victory-canvas": "37.3.6", + "victory-chart": "37.3.6", + "victory-core": "37.3.6", + "victory-create-container": "37.3.6", + "victory-cursor-container": "37.3.6", + "victory-errorbar": "37.3.6", + "victory-group": "37.3.6", + "victory-histogram": "37.3.6", + "victory-legend": "37.3.6", + "victory-line": "37.3.6", + "victory-pie": "37.3.6", + "victory-polar-axis": "37.3.6", + "victory-scatter": "37.3.6", + "victory-selection-container": "37.3.6", + "victory-shared-events": "37.3.6", + "victory-stack": "37.3.6", + "victory-tooltip": "37.3.6", + "victory-voronoi": "37.3.6", + "victory-voronoi-container": "37.3.6", + "victory-zoom-container": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, "node_modules/victory-area": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-area/-/victory-area-36.9.2.tgz", - "integrity": "sha512-32aharvPf2RgdQB+/u1j3/ajYFNH/7ugLX9ZRpdd65gP6QEbtXL+58gS6CxvFw6gr/y8a0xMlkMKkpDVacXLpw==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-area/-/victory-area-37.3.6.tgz", + "integrity": "sha512-wVC8LKrZJLiSySNuJLRCB449qZTsPiRyzLlNoJwe21y+XA/a2HJbmJSeywmo8P153aX8viKe1H8ygDsTFXQhHw==", "license": "MIT", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.2", - "victory-vendor": "^36.9.2" + "victory-core": "37.3.6", + "victory-vendor": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-axis": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-axis/-/victory-axis-36.9.2.tgz", - "integrity": "sha512-4Odws+IAjprJtBg2b2ZCxEPgrQ6LgIOa22cFkGghzOSfTyNayN4M3AauNB44RZyn2O/hDiM1gdBkEg1g9YDevQ==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-axis/-/victory-axis-37.3.6.tgz", + "integrity": "sha512-Vi0dZvgmXmnCdoqc49WckeG5cMXnl7FTtqVhXu9JweA9cgCnkZabBd5mRvAjblb3Lo4j0HZCSPKHYWUPW70qZg==", "license": "MIT", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.2" + "victory-core": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-bar": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-bar/-/victory-bar-36.9.2.tgz", - "integrity": "sha512-R3LFoR91FzwWcnyGK2P8DHNVv9gsaWhl5pSr2KdeNtvLbZVEIvUkTeVN9RMBMzterSFPw0mbWhS1Asb3sV6PPw==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-bar/-/victory-bar-37.3.6.tgz", + "integrity": "sha512-jdATFRWL1LUW/yEpKWx/aId2BiU2o1pPF9+Kh1TFISBduJoI4ZqvZD90H1QK4f/z50PikqiqiDECaKoKM1jfOQ==", "license": "MIT", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.2", - "victory-vendor": "^36.9.2" + "victory-core": "37.3.6", + "victory-vendor": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-box-plot": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-box-plot/-/victory-box-plot-37.3.6.tgz", + "integrity": "sha512-GOucnD63h14ScBuISC/nd1GBTEx6gIZfLE+0P0gyeH1poBKq0trTTvpQDvAMuGR8zICfEETG3ltmUMCwRrFyUg==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.19", + "victory-core": "37.3.6", + "victory-vendor": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-brush-container": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-brush-container/-/victory-brush-container-36.9.2.tgz", - "integrity": "sha512-KcQjzFeo40tn52cJf1A02l5MqeR9GKkk3loDqM3T2hfi1PCyUrZXEUjGN5HNlLizDRvtcemaAHNAWlb70HbG/g==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-brush-container/-/victory-brush-container-37.3.6.tgz", + "integrity": "sha512-LfZ2CgX1cYAqCtYxcSB68OfZS2v0T2VLXoEArd0lCXfRBY1Gya7GacCUcuo7GoK9XOXeslx7S/U95aVutt1VLg==", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", - "victory-core": "^36.9.2" + "victory-core": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-brush-line": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-brush-line/-/victory-brush-line-37.3.6.tgz", + "integrity": "sha512-zsZJfF1fUj4F7mUoIMV+h73qoTClPA4bKM1terlYrDBD8l/c/f0KBbEotu3E1X+n4QMmDRruswaB/YUdqK5QLA==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.19", + "react-fast-compare": "^3.2.0", + "victory-core": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-candlestick": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-candlestick/-/victory-candlestick-37.3.6.tgz", + "integrity": "sha512-h/mOmkCrsWrirn4dFnpLxJPXpxT+uHxuYxnXGrAyH+YUOrVj3iKaDJlEiVlz5vy30syE5j5hzTQCMsZ/hzHNdg==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.19", + "victory-core": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-canvas": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-canvas/-/victory-canvas-37.3.6.tgz", + "integrity": "sha512-1CD4S0uZ92sUGGSIEQferEfSqd/z9EXw9G6zkzPIoJeTKFshpfqCjUkNRx9Iu9Upxt3fUpId8Qwl1YfchmbrFg==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.19", + "victory-bar": "37.3.6", + "victory-core": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-chart": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-chart/-/victory-chart-36.9.2.tgz", - "integrity": "sha512-dMNcS0BpqL3YiGvI4BSEmPR76FCksCgf3K4CSZ7C/MGyrElqB6wWwzk7afnlB1Qr71YIHXDmdwsPNAl/iEwTtA==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-chart/-/victory-chart-37.3.6.tgz", + "integrity": "sha512-IkPo/W4AJ7bPu902TGER09OseR9ODm+FQAKfOBw4JsdEhZZ7BiG9zgd/25+x0r5EsTLu81CYGQVkBa+ZazcOlA==", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", - "victory-axis": "^36.9.2", - "victory-core": "^36.9.2", - "victory-polar-axis": "^36.9.2", - "victory-shared-events": "^36.9.2" + "victory-axis": "37.3.6", + "victory-core": "37.3.6", + "victory-polar-axis": "37.3.6", + "victory-shared-events": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-core": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.2.tgz", - "integrity": "sha512-AzmMy+9MYMaaRmmZZovc/Po9urHne3R3oX7bbXeQdVuK/uMBrlPiv11gVJnuEH2SXLVyep43jlKgaBp8ef9stQ==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-37.3.6.tgz", + "integrity": "sha512-aFgO6KokxPbUCPznZP5UPhOdI22pMuwDXKDt6eoQOnkVim66Ia+K95TQar2nwVKGYV5j26aKVf/n9blwphGJRw==", "license": "MIT", "dependencies": { "lodash": "^4.17.21", "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.2" + "victory-vendor": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-create-container": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-create-container/-/victory-create-container-36.9.2.tgz", - "integrity": "sha512-uA0dh1R0YDzuXyE/7StZvq4qshet+WYceY7R1UR5mR/F9079xy+iQsa2Ca4h97/GtVZoLO6r1eKLWBt9TN+U7A==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-create-container/-/victory-create-container-37.3.6.tgz", + "integrity": "sha512-Uf5bFQvqUsXCjqpvBW4LhrdrHkM6dBqxYgub6FCsBb86f84xZQ3vY7jFkg/JfvF0oGKMoWXYYrYLC1sk+fcWVA==", "license": "MIT", "dependencies": { "lodash": "^4.17.19", - "victory-brush-container": "^36.9.2", - "victory-core": "^36.9.2", - "victory-cursor-container": "^36.9.2", - "victory-selection-container": "^36.9.2", - "victory-voronoi-container": "^36.9.2", - "victory-zoom-container": "^36.9.2" + "victory-brush-container": "37.3.6", + "victory-core": "37.3.6", + "victory-cursor-container": "37.3.6", + "victory-selection-container": "37.3.6", + "victory-voronoi-container": "37.3.6", + "victory-zoom-container": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-cursor-container": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-cursor-container/-/victory-cursor-container-36.9.2.tgz", - "integrity": "sha512-jidab4j3MaciF3fGX70jTj4H9rrLcY8o2LUrhJ67ZLvEFGGmnPtph+p8Fe97Umrag7E/DszjNxQZolpwlgUh3g==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-cursor-container/-/victory-cursor-container-37.3.6.tgz", + "integrity": "sha512-+Oiw57d5nE+iq8As8RvepknzmNtKq1Gsc50u1X3IRd4jXtX8zqZrgXGlVZ+BP/tkLsWnGYVjKulwKBf2oaEUuw==", "license": "MIT", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.2" + "victory-core": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-errorbar": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-errorbar/-/victory-errorbar-37.3.6.tgz", + "integrity": "sha512-WGAv/qizOlfmwKv+Yfxr4q6pDgTfloNQwi3Z3M0h8povjMZt74tHYkvi/TASSRYr3zv5kjUqUJ28qAyGMWwryQ==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.19", + "victory-core": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-group": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-group/-/victory-group-36.9.2.tgz", - "integrity": "sha512-wBmpsjBTKva8mxHvHNY3b8RE58KtnpLLItEyyAHaYkmExwt3Uj8Cld3sF3vmeuijn2iR64NPKeMbgMbfZJzycw==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-group/-/victory-group-37.3.6.tgz", + "integrity": "sha512-kgy/Azl5BxwlJAV0KDPGypv35TMrOD1J2ZxnJW2Wyyq+e8i0GGBIv5MoBzou64BRsDlS9V0CYRIjnkHgrBpB5w==", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", - "victory-core": "^36.9.2", - "victory-shared-events": "^36.9.2" + "victory-core": "37.3.6", + "victory-shared-events": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-histogram": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-histogram/-/victory-histogram-37.3.6.tgz", + "integrity": "sha512-K4d43MpXHYnGCLEMzfRpJ+lCRRDKALPi/juxfMGVzBPzSMgjC8h9x6hKdxaejiTd/E04UdzNO7J24plL3Uz8rA==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.19", + "react-fast-compare": "^3.2.0", + "victory-bar": "37.3.6", + "victory-core": "37.3.6", + "victory-vendor": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-legend": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-legend/-/victory-legend-36.9.2.tgz", - "integrity": "sha512-cucFJpv6fty+yXp5pElQFQnHBk1TqA4guGUMI+XF/wLlnuM4bhdAtASobRIIBkz0mHGBaCAAV4PzL9azPU/9dg==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-legend/-/victory-legend-37.3.6.tgz", + "integrity": "sha512-vRRrhj3/ENqKVLdaBMzEmR83N6BOjox1bthYT1eJjN2H5SIK35bxn30IkiV/Pz3y627EqZe4TAWaxc0jiJlCiA==", "license": "MIT", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.2" + "victory-core": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-line": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-line/-/victory-line-36.9.2.tgz", - "integrity": "sha512-kmYFZUo0o2xC8cXRsmt/oUBRQSZJVT2IJnAkboUepypoj09e6CY5tRH4TSdfEDGkBk23xQkn7d4IFgl4kAGnSA==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-line/-/victory-line-37.3.6.tgz", + "integrity": "sha512-Ke817uf/qFbN9jU7Dba7CrcHXYO5wAZuKKnyeHJmLDeQeFST0773xejnIuC+dBgZipjFr4KIbSd+VcUafFNE1g==", "license": "MIT", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.2", - "victory-vendor": "^36.9.2" + "victory-core": "37.3.6", + "victory-vendor": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-pie": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-pie/-/victory-pie-36.9.2.tgz", - "integrity": "sha512-i3zWezvy5wQEkhXKt4rS9ILGH7Vr9Q5eF9fKO4GMwDPBdYOTE3Dh2tVaSrfDC8g9zFIc0DKzOtVoJRTb+0AkPg==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-pie/-/victory-pie-37.3.6.tgz", + "integrity": "sha512-tvdgAZ/HQWlo3KDDe0XAVbizHuaNMbgkkiF7zfA7Ww+3bHSs+0P9dsDtK2xP365D8gBCOv8pWmuzvKRhzNbqeA==", "license": "MIT", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.2", - "victory-vendor": "^36.9.2" + "victory-core": "37.3.6", + "victory-vendor": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-polar-axis": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-polar-axis/-/victory-polar-axis-36.9.2.tgz", - "integrity": "sha512-HBR90FF4M56yf/atXjSmy3DMps1vSAaLXmdVXLM/A5g+0pUS7HO719r5x6dsR3I6Rm+8x6Kk8xJs0qgpnGQIEw==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-polar-axis/-/victory-polar-axis-37.3.6.tgz", + "integrity": "sha512-RpFsCkzHezJq5P+C/wtVdjEHX25JIFsSgs6qYSnfr/hayaFbWgK5HhRFpriQm5hg61cx47WxAOLyHvzf0nasvw==", "license": "MIT", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.2" + "victory-core": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-scatter": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-scatter/-/victory-scatter-36.9.2.tgz", - "integrity": "sha512-hK9AtbJQfaW05i8BH7Lf1HK7vWMAfQofj23039HEQJqTKbCL77YT+Q0LhZw1a1BRCpC/5aSg9EuqblhfIYw2wg==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-scatter/-/victory-scatter-37.3.6.tgz", + "integrity": "sha512-fp95zMTPXgW1cmTowzDXhn+KxePMVDrzU0lotsHQMdBV7eB+ioXdu9hORlx4VHmMYg2ihsGwRTF+VAZ7rGxphA==", "license": "MIT", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.2" + "victory-core": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-selection-container": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-selection-container/-/victory-selection-container-36.9.2.tgz", - "integrity": "sha512-chboroEwqqVlMB60kveXM2WznJ33ZM00PWkFVCoJDzHHlYs7TCADxzhqet2S67SbZGSyvSprY2YztSxX8kZ+XQ==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-selection-container/-/victory-selection-container-37.3.6.tgz", + "integrity": "sha512-gd3qODDlBtLEJM7+2jCXk2YcLBUmIpYEEHswytMhwc6zihxXipGBUHRulhLj/I05mKay2gaOAg5ewiJHd4Awgw==", "license": "MIT", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.2" + "victory-core": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-shared-events": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-shared-events/-/victory-shared-events-36.9.2.tgz", - "integrity": "sha512-W/atiw3Or6MnpBuhluFv6007YrixIRh5NtiRvtFLGxNuQJLYjaSh6koRAih5xJer5Pj7YUx0tL9x67jTRcJ6Dg==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-shared-events/-/victory-shared-events-37.3.6.tgz", + "integrity": "sha512-ygrbOtzLUTbtKebacZKyQRekhSAROnAvMkVI/PKsAGsz0ClY9P7qDEJG7eTUUygjO6ax0tI6WNE6JogQzeD1gw==", "license": "MIT", "dependencies": { "json-stringify-safe": "^5.0.1", "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", - "victory-core": "^36.9.2" + "victory-core": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-stack": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-stack/-/victory-stack-36.9.2.tgz", - "integrity": "sha512-imR6FniVlDFlBa/B3Est8kTryNhWj2ZNpivmVOebVDxkKcVlLaDg3LotCUOI7NzOhBQaro0UzeE9KmZV93JcYA==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-stack/-/victory-stack-37.3.6.tgz", + "integrity": "sha512-ldod04RdqGJGH5p5eWXCofdTkbhZqIp3iwW7NpxSbMDLs8zPQIVvDFVtuJgMwQiC5vnIpbhMmxVeFbr8m64ZKA==", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", - "victory-core": "^36.9.2", - "victory-shared-events": "^36.9.2" + "victory-core": "37.3.6", + "victory-shared-events": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-tooltip": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-tooltip/-/victory-tooltip-36.9.2.tgz", - "integrity": "sha512-76seo4TWD1WfZHJQH87IP3tlawv38DuwrUxpnTn8+uW6/CUex82poQiVevYdmJzhataS9jjyCWv3w7pOmLBCLg==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-tooltip/-/victory-tooltip-37.3.6.tgz", + "integrity": "sha512-vqaJS9noauOqDDBBAV9Ln9duOY/i17h1DCfCPAqhwPFyvFbwKvAub9zPTeYWAm/14VvWX5O/0yekFCVbcC7hjg==", "license": "MIT", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.2" + "victory-core": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-vendor": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", - "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", + "integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==", "license": "MIT AND ISC", "dependencies": { "@types/d3-array": "^3.0.3", @@ -27649,30 +27880,53 @@ "d3-timer": "^3.0.1" } }, + "node_modules/victory-voronoi": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-voronoi/-/victory-voronoi-37.3.6.tgz", + "integrity": "sha512-Q+1FWHp8IAbmDL9pGWS0y0N4Cb5qmD9OOgxoxCfIDsLlhGvd6LddhRoknWsN7WnreaK+XiwjSfQkdMTCZ4hdhQ==", + "license": "MIT", + "dependencies": { + "d3-voronoi": "^1.1.4", + "lodash": "^4.17.19", + "victory-core": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, "node_modules/victory-voronoi-container": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-voronoi-container/-/victory-voronoi-container-36.9.2.tgz", - "integrity": "sha512-NIVYqck9N4OQnEz9mgQ4wILsci3OBWWK7RLuITGHyoD7Ne/+WH1i0Pv2y9eIx+f55rc928FUTugPPhkHvXyH3A==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-voronoi-container/-/victory-voronoi-container-37.3.6.tgz", + "integrity": "sha512-qAAG0rMuK7A4EoJ4cyUk5wNdOW+HuCXNKPOko+hYK6wWOYXJvFhiglYyA85a695YyAXECc6JyJS/crm4IOEFag==", "license": "MIT", "dependencies": { "delaunay-find": "0.0.6", "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", - "victory-core": "^36.9.2", - "victory-tooltip": "^36.9.2" + "victory-core": "37.3.6", + "victory-tooltip": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-zoom-container": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-zoom-container/-/victory-zoom-container-36.9.2.tgz", - "integrity": "sha512-pXa2Ji6EX/pIarKT6Hcmmu2n7IG/x8Vs0D2eACQ/nbpvZa+DXWIxCRW4hcg2Va35fmXcDIEpGaX3/soXzZ+pbw==", + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-zoom-container/-/victory-zoom-container-37.3.6.tgz", + "integrity": "sha512-AGL+k20mI44OL5b0VgIxlmnNSefIoFmbbim5NraPmIxbtns9qQW/56ivIncJcYomBungIx99gUpsEpcQaMNHgQ==", "license": "MIT", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.2" + "victory-core": "37.3.6" + }, + "engines": { + "node": ">=18.0.0" }, "peerDependencies": { "react": ">=16.6.0" @@ -28785,9 +29039,9 @@ } }, "node_modules/zrender": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.5.0.tgz", - "integrity": "sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.1.tgz", + "integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==", "license": "BSD-3-Clause", "dependencies": { "tslib": "2.3.0" diff --git a/web/package.json b/web/package.json index 8db223d..9825068 100644 --- a/web/package.json +++ b/web/package.json @@ -79,7 +79,7 @@ "@emotion/styled": "^11.11.0", "@grafana/lezer-traceql": "^0.0.22", "@hookform/resolvers": "^3.3.4", - "@patternfly/react-charts": "6.92.0", + "@patternfly/react-charts": "^8.2.0", "@patternfly/react-core": "^6.2.0", "@patternfly/react-icons": "^5.4.2", "@patternfly/react-templates": "^6.2.2", @@ -97,12 +97,14 @@ "i18next-parser": "^8.13.0", "lodash": "^4.17.21", "pluralize": "^8.0.0", - "use-query-params": "^2.2.1" + "use-query-params": "^2.2.1", + "victory": "^37.3.6" }, "peerDependencies": { "react-router-dom": "<7" }, "overrides": { - "cheerio": "1.0.0-rc.12" + "cheerio": "1.0.0-rc.12", + "echarts": "^5.6.0" } } diff --git a/web/src/components/PersesWrapper.css b/web/src/components/PersesWrapper.css new file mode 100644 index 0000000..56a9784 --- /dev/null +++ b/web/src/components/PersesWrapper.css @@ -0,0 +1,6 @@ +/* This theme styles Material UI components to look like PatternFly components */ + +.mui-pf-theme { + --mui-palette-text-primary: var(--pf-t--global--text--color--regular); + --mui-shape-borderRadius: var(--pf-t--global--border--radius--small); +} diff --git a/web/src/components/PersesWrapper.tsx b/web/src/components/PersesWrapper.tsx index 5a23cee..ffc9ca0 100644 --- a/web/src/components/PersesWrapper.tsx +++ b/web/src/components/PersesWrapper.tsx @@ -38,7 +38,7 @@ import * as tempoPlugin from '@perses-dev/tempo-plugin'; import * as scatterChartPlugin from '@perses-dev/scatter-chart-plugin'; import * as traceTablePlugin from '@perses-dev/trace-table-plugin'; import * as tracingGanttChartPlugin from '@perses-dev/tracing-gantt-chart-plugin'; -import { ChartThemeColor, getThemeColors } from '@patternfly/react-charts'; +import { ChartThemeColor, getThemeColors } from '@patternfly/react-charts/victory'; import { TempoInstance } from '../hooks/useTempoInstance'; import { getProxyURLFor } from '../hooks/api'; import { ErrorBoundary } from 'react-error-boundary'; @@ -47,6 +47,7 @@ import { NoTempoInstanceSelectedState } from './NoTempoInstanceSelectedState'; import { LoadingState } from './LoadingState'; import { usePatternFlyTheme } from './console/utils/usePatternFlyTheme'; import { Link as RouterLink, useNavigate } from 'react-router-dom-v5-compat'; +import './PersesWrapper.css'; class DatasourceApiImpl implements DatasourceApi { constructor(public proxyDatasource: GlobalDatasourceResource) {} @@ -75,14 +76,15 @@ const patternflyBlue500 = '#004080'; const patternflyBlue600 = '#002952'; const defaultPaletteColors = [patternflyBlue400, patternflyBlue500, patternflyBlue600]; -const patternflyChartsMultiUnorderedPalette = getThemeColors( - ChartThemeColor.multiUnordered, -).chart.colorScale.flatMap((cssColor) => { - // colors are stored as 'var(--pf-chart-theme--multi-color-unordered--ColorScale--3400, #73c5c5)' - // need to extract the hex value, because fillStyle() of does not support CSS vars - const match = cssColor.match(/#[a-fA-F0-9]+/); - return match ? [match[0]] : []; -}); +const chartColorScale = getThemeColors(ChartThemeColor.multiUnordered).chart?.colorScale; +const patternflyChartsMultiUnorderedPalette = Array.isArray(chartColorScale) + ? chartColorScale.flatMap((cssColor: string) => { + // colors are stored as 'var(--pf-chart-theme--multi-color-unordered--ColorScale--3400, #73c5c5)' + // need to extract the hex value, because fillStyle() of does not support CSS vars + const match = cssColor.match(/#[a-fA-F0-9]+/); + return match ? [match[0]] : []; + }) + : []; // PluginRegistry configuration to allow access to // visualization panels/charts (@perses-dev/panels-plugin) @@ -110,10 +112,10 @@ export function PersesWrapper({ children }: PersesWrapperProps) { ...typography, fontFamily: 'var(--pf-t--global--font--family--body)', }, - shape: { - borderRadius: 4, // should be var(--pf-t--global--border--radius--tiny), but type must be a number. - }, - }); + cssVariables: true, + // Remove casting once https://github.com/perses/perses/pull/3443 is merged + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any); const chartsTheme: PersesChartsTheme = generateChartsTheme(muiTheme, { echartsTheme: { diff --git a/web/src/pages/TraceDetailPage/TraceDetailPage.tsx b/web/src/pages/TraceDetailPage/TraceDetailPage.tsx index 4738dbd..73fa927 100644 --- a/web/src/pages/TraceDetailPage/TraceDetailPage.tsx +++ b/web/src/pages/TraceDetailPage/TraceDetailPage.tsx @@ -53,7 +53,12 @@ function TraceDetailPageBody() {
- +
- + {t('Traces')} @@ -91,7 +91,7 @@ export function QueryBrowserBody() { - + Date: Thu, 9 Oct 2025 20:41:37 +0200 Subject: [PATCH 30/35] TRACING-5579: Support custom time range Signed-off-by: Andreas Gerstmayr --- web/src/components/DurationDropdown.tsx | 72 ----------------------- web/src/components/PersesWrapper.tsx | 19 +++--- web/src/components/TimeRangeSelect.css | 4 ++ web/src/components/TimeRangeSelect.tsx | 21 +++++++ web/src/pages/TracesPage/QueryBrowser.tsx | 44 ++------------ 5 files changed, 39 insertions(+), 121 deletions(-) delete mode 100644 web/src/components/DurationDropdown.tsx create mode 100644 web/src/components/TimeRangeSelect.css create mode 100644 web/src/components/TimeRangeSelect.tsx diff --git a/web/src/components/DurationDropdown.tsx b/web/src/components/DurationDropdown.tsx deleted file mode 100644 index 279deea..0000000 --- a/web/src/components/DurationDropdown.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import * as React from 'react'; -import { Form, FormGroup } from '@patternfly/react-core'; -import { useTranslation } from 'react-i18next'; -import { DurationString } from '@perses-dev/core'; -import { ControlledSimpleSelect } from './ControlledSelects'; - -type DurationDropDownProps = { - duration: DurationString; - setDuration: (timeRange: DurationString) => void; -}; - -export const DEFAULT_DURATION = '30m'; - -// Keep this list in sync with timeRangeSelectOptions below -// (due to the translation we can't move TimeRangeSelectOption[] outside of the component) -export const DurationValues = ['5m', '15m', '30m', '1h', '6h', '12h', '1d', '7d']; - -export const DurationDropdown = ({ duration, setDuration }: DurationDropDownProps) => { - const { t } = useTranslation('plugin__distributed-tracing-console-plugin'); - - // The time range selection mirrors the options on the Metrics Page - // Keep this list in sync with DurationValues above - const timeRangeSelectOptions = [ - { - content: t('Last 5 minutes'), - value: '5m', - }, - { - content: t('Last 15 minutes'), - value: '15m', - }, - { - content: t('Last 30 minutes'), - value: '30m', - }, - { - content: t('Last 1 hour'), - value: '1h', - }, - { - content: t('Last 6 hours'), - value: '6h', - }, - { - content: t('Last 12 hours'), - value: '12h', - }, - { - content: t('Last 1 day'), - value: '1d', - }, - { - content: t('Last 7 days'), - value: '7d', - }, - ]; - - return ( - - - setDuration(value as DurationString)} - /> - - - ); -}; diff --git a/web/src/components/PersesWrapper.tsx b/web/src/components/PersesWrapper.tsx index 5a23cee..7c59b0e 100644 --- a/web/src/components/PersesWrapper.tsx +++ b/web/src/components/PersesWrapper.tsx @@ -15,15 +15,15 @@ import { PanelProps, PluginRegistry, RouterProvider, - TimeRangeProvider, + TimeRangeProviderWithQueryParams, useDataQueriesContext, + useInitialTimeRange, } from '@perses-dev/plugin-system'; import { DatasourceResource, Definition, DurationString, GlobalDatasourceResource, - TimeRangeValue, TraceData, UnknownSpec, } from '@perses-dev/core'; @@ -137,23 +137,20 @@ export function PersesWrapper({ children }: PersesWrapperProps) { } interface PersesDashboardWrapperProps { - timeRange?: TimeRangeValue; - setTimeRange?: (value: TimeRangeValue) => void; children?: React.ReactNode; } /** * PersesDashboardWrapper initializes the dashboard time range and variable providers. */ -export function PersesDashboardWrapper({ - timeRange = { pastDuration: '0s' }, - setTimeRange, - children, -}: PersesDashboardWrapperProps) { +export function PersesDashboardWrapper({ children }: PersesDashboardWrapperProps) { + const DEFAULT_DASHBOARD_DURATION = '30m'; + const initialTimeRange = useInitialTimeRange(DEFAULT_DASHBOARD_DURATION); + return ( - + {children} - + ); } diff --git a/web/src/components/TimeRangeSelect.css b/web/src/components/TimeRangeSelect.css new file mode 100644 index 0000000..f1b09c4 --- /dev/null +++ b/web/src/components/TimeRangeSelect.css @@ -0,0 +1,4 @@ +.dt-plugin-time-range-select .MuiSelect-select { + /** override hardcoded padding https://github.com/perses/perses/blob/601768a329985efeaaf8c43c3ba1b8f7b26cb08c/ui/components/src/TimeRangeSelector/TimeRangeSelector.tsx#L130 */ + padding: 2px 38px 2px 16px !important; +} diff --git a/web/src/components/TimeRangeSelect.tsx b/web/src/components/TimeRangeSelect.tsx new file mode 100644 index 0000000..b024906 --- /dev/null +++ b/web/src/components/TimeRangeSelect.tsx @@ -0,0 +1,21 @@ +import * as React from 'react'; +import { Form, FormGroup } from '@patternfly/react-core'; +import { useTranslation } from 'react-i18next'; +import { TimeRangeControls } from '@perses-dev/plugin-system'; +import './TimeRangeSelect.css'; + +export const TimeRangeSelect = () => { + const { t } = useTranslation('plugin__distributed-tracing-console-plugin'); + + return ( +
+ + + +
+ ); +}; diff --git a/web/src/pages/TracesPage/QueryBrowser.tsx b/web/src/pages/TracesPage/QueryBrowser.tsx index ff87d8e..3b63c72 100644 --- a/web/src/pages/TracesPage/QueryBrowser.tsx +++ b/web/src/pages/TracesPage/QueryBrowser.tsx @@ -1,11 +1,7 @@ -import React, { useCallback, useEffect, useMemo } from 'react'; +import React, { useCallback, useEffect } from 'react'; import { Divider, PageSection, Split, SplitItem, Stack, Title } from '@patternfly/react-core'; import { useTranslation } from 'react-i18next'; -import { - DEFAULT_DURATION, - DurationDropdown, - DurationValues, -} from '../../components/DurationDropdown'; +import { TimeRangeSelect } from '../../components/TimeRangeSelect'; import { ScatterPlot } from './ScatterPlot'; import { TraceTable } from './TraceTable'; import { @@ -13,41 +9,16 @@ import { PersesTempoDatasourceWrapper, PersesWrapper, } from '../../components/PersesWrapper'; -import { DurationString, RelativeTimeRange, TimeRangeValue } from '@perses-dev/core'; -import { - createEnumParam, - NumberParam, - StringParam, - useQueryParam, - withDefault, -} from 'use-query-params'; +import { NumberParam, StringParam, useQueryParam, withDefault } from 'use-query-params'; import { useTempoInstance } from '../../hooks/useTempoInstance'; import { useTimeRange } from '@perses-dev/plugin-system'; import { FilterToolbar } from './Toolbar/FilterToolbar'; import { DEFAULT_LIMIT, LimitSelect } from './LimitSelect'; -const durationQueryParam = withDefault(createEnumParam(DurationValues), DEFAULT_DURATION); - export function QueryBrowser() { - const [duration, setDuration] = useQueryParam('duration', durationQueryParam, { - updateType: 'replaceIn', - }); - const timeRange = useMemo(() => { - return { pastDuration: duration as DurationString }; - }, [duration]); - const setTimeRange = useCallback( - (value: TimeRangeValue) => { - const pastDuration = (value as RelativeTimeRange).pastDuration; - if (pastDuration) { - setDuration(pastDuration); - } - }, - [setDuration], - ); - return ( - + @@ -59,7 +30,7 @@ export function QueryBrowserBody() { const [tempo, setTempo] = useTempoInstance(); const [query, setQuery] = useQueryParam('q', withDefault(StringParam, '{}')); const [limit, setLimit] = useQueryParam('limit', withDefault(NumberParam, DEFAULT_LIMIT)); - const { timeRange, setTimeRange, refresh } = useTimeRange(); + const { refresh } = useTimeRange(); // Refresh query if Tempo instance or tenant changes. // The Perses data source is selected via a mock DatasourceApiImpl implementation in , @@ -83,10 +54,7 @@ export function QueryBrowserBody() { {t('Traces')} - setTimeRange({ pastDuration: value })} - /> + From 9c5d3c2c76de8ba83bcbd6fd1e9dba8ab4b97f8d Mon Sep 17 00:00:00 2001 From: Ishwar Kanse Date: Fri, 10 Oct 2025 11:22:08 +0530 Subject: [PATCH 31/35] TRACING-5579: Update Cypress tests for Material-UI time range component Assisted by Cursor IDE --- tests/cypress/support/commands.ts | 27 +++++++++++++++++++++++++-- tests/e2e/dt-plugin-tests.cy.ts | 8 ++++---- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/tests/cypress/support/commands.ts b/tests/cypress/support/commands.ts index 682a861..49d00b4 100644 --- a/tests/cypress/support/commands.ts +++ b/tests/cypress/support/commands.ts @@ -59,6 +59,9 @@ declare global { pfCloseButtonIfExists(ariaLabel?: string, options?: Partial): Chainable; menuToggleContains(text: string | RegExp, options?: Partial): Chainable>; verifyTraceCount(expectedCount: number | string, options?: Partial): Chainable; + // Material-UI specific commands + muiSelect(ariaLabel: string, options?: Partial): Chainable>; + muiSelectOption(optionText: string, options?: Partial): Chainable>; // Tracing-specific commands setupTracePage(tempoInstance: string, tenant: string, timeframe?: string, serviceFilter?: string): Chainable; navigateToTraceDetails(): Chainable; @@ -595,6 +598,26 @@ Cypress.Commands.add( }, ); +// Material-UI Select commands + +Cypress.Commands.add( + 'muiSelect', + (ariaLabel: string, options?: Partial) => { + const defaultOptions = { timeout: 10000, ...options }; + // Find Material-UI Select component by aria-label (supports partial match) + cy.get(`[role="combobox"][aria-label*="${ariaLabel}"]`, defaultOptions); + }, +); + +Cypress.Commands.add( + 'muiSelectOption', + (optionText: string, options?: Partial) => { + const defaultOptions = { timeout: 10000, ...options }; + // Find Material-UI Select option by text in the listbox + cy.get('[role="listbox"] [role="option"]', defaultOptions).contains(optionText); + }, +); + Cypress.Commands.add( 'pfCloseButton', (ariaLabel?: string, options?: Partial) => { @@ -685,8 +708,8 @@ Cypress.Commands.add( // Set timeframe if provided if (timeframe) { - cy.pfMenuToggle('Last 30 minutes').click(); - cy.pfSelectMenuItem(timeframe).click(); + cy.muiSelect('Select time range').click(); + cy.muiSelectOption(timeframe).click(); } // Set service filter if provided diff --git a/tests/e2e/dt-plugin-tests.cy.ts b/tests/e2e/dt-plugin-tests.cy.ts index 204771c..dd33558 100644 --- a/tests/e2e/dt-plugin-tests.cy.ts +++ b/tests/e2e/dt-plugin-tests.cy.ts @@ -373,8 +373,8 @@ describe('OpenShift Distributed Tracing UI Plugin tests', () => { cy.pfSelectMenuItem('chainsaw-rbac / simplst').click(); cy.pfTypeahead('Select a tenant').click(); cy.pfSelectMenuItem('dev').click(); - cy.pfMenuToggle('Last 30 minutes').click(); - cy.pfSelectMenuItem('Last 15 minutes').click(); + cy.muiSelect('Select time range').click(); + cy.muiSelectOption('Last 15 minutes').click(); cy.pfMenuToggle('Service Name').click(); cy.pfMenuToggleByLabel('Multi typeahead checkbox').click(); cy.pfCheckMenuItem('http-rbac-1'); @@ -403,8 +403,8 @@ describe('OpenShift Distributed Tracing UI Plugin tests', () => { cy.pfSelectMenuItem('chainsaw-mmo-rbac / mmo-rbac').click(); cy.pfTypeahead('Select a tenant').click(); cy.pfSelectMenuItem('dev').click(); - cy.pfMenuToggle('Last').click(); - cy.pfSelectMenuItem('Last 1 hour').click(); + cy.muiSelect('Select time range').click(); + cy.muiSelectOption('Last 1 hour').click(); cy.pfMenuToggle('Service Name').click(); cy.pfMenuToggleByLabel('Multi typeahead checkbox').click(); cy.pfCheckMenuItem('http-rbac-1'); From 0a5d5bb30632116d327d66808cd1358452aa4b50 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Mon, 13 Oct 2025 12:31:12 +0200 Subject: [PATCH 32/35] Update Cypress to resolve CVE-2025-7783 Cypress had a transitive dependency on `form-data@2.3.3`, which is affected by CVE-2025-7783. Signed-off-by: Andreas Gerstmayr --- web/package-lock.json | 312 ++++++++++++++++++++---------------------- web/package.json | 4 +- 2 files changed, 154 insertions(+), 162 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index 9c57efa..a9bd2c3 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -34,7 +34,7 @@ "use-query-params": "^2.2.1" }, "devDependencies": { - "@cypress/webpack-preprocessor": "^5.15.5", + "@cypress/webpack-preprocessor": "^6.0.2", "@openshift-console/dynamic-plugin-sdk": "^4.19.0", "@openshift-console/dynamic-plugin-sdk-webpack": "^4.19.0", "@types/jest": "^28.1.4", @@ -45,7 +45,7 @@ "@typescript-eslint/eslint-plugin": "^8.39.1", "@typescript-eslint/parser": "^8.39.1", "css-loader": "^6.7.1", - "cypress": "^11.0.1", + "cypress": "^14.2.1", "cypress-multi-reporters": "^1.6.2", "eslint": "^8.44.0", "eslint-config-prettier": "^8.5.0", @@ -2009,17 +2009,6 @@ "w3c-keyname": "^2.2.4" } }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -2112,9 +2101,9 @@ } }, "node_modules/@cypress/request": { - "version": "2.88.12", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.12.tgz", - "integrity": "sha512-tOn+0mDZxASFM+cuAP9szGUGPI1HwWVSvdzm7V4cCsPdFTx6qMj29CwaQmRAMIEhORIUBFBsYROYJcveK4uOjA==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.9.tgz", + "integrity": "sha512-I3l7FdGRXluAS44/0NguwWlO83J18p0vlr2FYHrJkWdNYhgVoiYo61IXPqaOsL+vNxU1ZqMACzItGK3/KKDsdw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2124,16 +2113,16 @@ "combined-stream": "~1.0.6", "extend": "~3.0.2", "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "http-signature": "~1.3.6", + "form-data": "~4.0.4", + "http-signature": "~1.4.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "performance-now": "^2.1.0", - "qs": "~6.10.3", + "qs": "6.14.0", "safe-buffer": "^5.1.2", - "tough-cookie": "^4.1.3", + "tough-cookie": "^5.0.0", "tunnel-agent": "^0.6.0", "uuid": "^8.3.2" }, @@ -2141,40 +2130,38 @@ "node": ">= 6" } }, - "node_modules/@cypress/request/node_modules/qs": { - "version": "6.10.4", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.4.tgz", - "integrity": "sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/@cypress/webpack-preprocessor": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/@cypress/webpack-preprocessor/-/webpack-preprocessor-5.17.1.tgz", - "integrity": "sha512-FE/e8ikPc8z4EVopJCaior3RGy0jd2q9Xcp5NtiwNG4XnLfEnUFTZlAGwXe75sEh4fNMPrBJW1KIz77PX5vGAw==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@cypress/webpack-preprocessor/-/webpack-preprocessor-6.0.4.tgz", + "integrity": "sha512-ly+EcabWWbhrSPr2J/njQX7Y3da+QqOmFg8Og/MVmLxhDLKIzr2WhTdgzDYviPTLx/IKsdb41cc2RLYp6mSBRA==", "dev": true, "license": "MIT", "dependencies": { "bluebird": "3.7.1", "debug": "^4.3.4", - "lodash": "^4.17.20" + "lodash": "^4.17.20", + "semver": "^7.3.2" }, "peerDependencies": { - "@babel/core": "^7.0.1", - "@babel/preset-env": "^7.0.0", - "babel-loader": "^8.0.2 || ^9", + "@babel/core": "^7.25.2", + "@babel/preset-env": "^7.25.3", + "babel-loader": "^8.3 || ^9 || ^10", "webpack": "^4 || ^5" } }, + "node_modules/@cypress/webpack-preprocessor/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@cypress/xvfb": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", @@ -8409,22 +8396,6 @@ "proxy-from-env": "^1.1.0" } }, - "node_modules/axios/node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/axios/node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -9635,9 +9606,9 @@ } }, "node_modules/cli-table3": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", - "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.1.tgz", + "integrity": "sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==", "dev": true, "license": "MIT", "dependencies": { @@ -9647,7 +9618,7 @@ "node": "10.* || >= 12.*" }, "optionalDependencies": { - "@colors/colors": "1.5.0" + "colors": "1.4.0" } }, "node_modules/cli-truncate": { @@ -9847,9 +9818,9 @@ } }, "node_modules/commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", "dev": true, "license": "MIT", "engines": { @@ -10418,31 +10389,31 @@ } }, "node_modules/cypress": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-11.2.0.tgz", - "integrity": "sha512-u61UGwtu7lpsNWLUma/FKNOsrjcI6wleNmda/TyKHe0dOBcVjbCPlp1N6uwFZ0doXev7f/91YDpU9bqDCFeBLA==", + "version": "14.5.4", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-14.5.4.tgz", + "integrity": "sha512-0Dhm4qc9VatOcI1GiFGVt8osgpPdqJLHzRwcAB5MSD/CAAts3oybvPUPawHyvJZUd8osADqZe/xzMsZ8sDTjXw==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "@cypress/request": "^2.88.10", + "@cypress/request": "^3.0.9", "@cypress/xvfb": "^1.2.4", - "@types/node": "^14.14.31", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", "arch": "^2.2.0", "blob-util": "^2.0.2", "bluebird": "^3.7.2", - "buffer": "^5.6.0", + "buffer": "^5.7.1", "cachedir": "^2.3.0", "chalk": "^4.1.0", "check-more-types": "^2.24.0", + "ci-info": "^4.1.0", "cli-cursor": "^3.1.0", - "cli-table3": "~0.6.1", - "commander": "^5.1.0", + "cli-table3": "0.6.1", + "commander": "^6.2.1", "common-tags": "^1.8.0", "dayjs": "^1.10.4", - "debug": "^4.3.2", + "debug": "^4.3.4", "enquirer": "^2.3.6", "eventemitter2": "6.4.7", "execa": "4.1.0", @@ -10451,20 +10422,22 @@ "figures": "^3.2.0", "fs-extra": "^9.1.0", "getos": "^3.2.1", - "is-ci": "^3.0.0", + "hasha": "5.2.2", "is-installed-globally": "~0.4.0", "lazy-ass": "^1.6.0", "listr2": "^3.8.3", "lodash": "^4.17.21", "log-symbols": "^4.0.0", - "minimist": "^1.2.6", + "minimist": "^1.2.8", "ospath": "^1.2.2", "pretty-bytes": "^5.6.0", + "process": "^0.11.10", "proxy-from-env": "1.0.0", "request-progress": "^3.0.0", - "semver": "^7.3.2", + "semver": "^7.7.1", "supports-color": "^8.1.1", - "tmp": "~0.2.1", + "tmp": "~0.2.3", + "tree-kill": "1.2.2", "untildify": "^4.0.0", "yauzl": "^2.10.0" }, @@ -10472,7 +10445,7 @@ "cypress": "bin/cypress" }, "engines": { - "node": ">=12.0.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" } }, "node_modules/cypress-multi-reporters": { @@ -10492,13 +10465,6 @@ "mocha": ">=3.1.2" } }, - "node_modules/cypress/node_modules/@types/node": { - "version": "14.18.63", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", - "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", - "dev": true, - "license": "MIT" - }, "node_modules/cypress/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -10552,6 +10518,22 @@ "node": ">=8" } }, + "node_modules/cypress/node_modules/ci-info": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", + "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/cypress/node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -13305,18 +13287,19 @@ } }, "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { - "node": ">= 0.12" + "node": ">= 6" } }, "node_modules/forwarded": { @@ -14109,6 +14092,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hasha/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -14429,15 +14439,15 @@ } }, "node_modules/http-signature": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", - "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz", + "integrity": "sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==", "dev": true, "license": "MIT", "dependencies": { "assert-plus": "^1.0.0", "jsprim": "^2.0.2", - "sshpk": "^1.14.1" + "sshpk": "^1.18.0" }, "engines": { "node": ">=0.10" @@ -14920,19 +14930,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", @@ -22122,6 +22119,16 @@ "dev": true, "license": "MIT" }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -22214,19 +22221,6 @@ "dev": true, "license": "ISC" }, - "node_modules/psl": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", - "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "funding": { - "url": "https://github.com/sponsors/lupomontero" - } - }, "node_modules/pump": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", @@ -22253,7 +22247,6 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "license": "BSD-3-Clause", - "peer": true, "dependencies": { "side-channel": "^1.1.0" }, @@ -22264,13 +22257,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true, - "license": "MIT" - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -26157,6 +26143,26 @@ "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", "license": "MIT" }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "dev": true, + "license": "MIT" + }, "node_modules/tmp": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", @@ -26216,29 +26222,16 @@ "license": "MIT" }, "node_modules/tough-cookie": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" + "tldts": "^6.1.32" }, "engines": { - "node": ">=6" - } - }, - "node_modules/tough-cookie/node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.0.0" + "node": ">=16" } }, "node_modules/tr46": { @@ -26247,6 +26240,16 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "license": "MIT" }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, "node_modules/trim": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", @@ -27133,17 +27136,6 @@ "punycode": "^2.1.0" } }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "node_modules/url-parse-lax": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", diff --git a/web/package.json b/web/package.json index 8db223d..a1cd8e0 100644 --- a/web/package.json +++ b/web/package.json @@ -21,7 +21,7 @@ "cypress-postreport": "npm run cypress-merge && npm run cypress-generate" }, "devDependencies": { - "@cypress/webpack-preprocessor": "^5.15.5", + "@cypress/webpack-preprocessor": "^6.0.2", "@openshift-console/dynamic-plugin-sdk": "^4.19.0", "@openshift-console/dynamic-plugin-sdk-webpack": "^4.19.0", "@types/jest": "^28.1.4", @@ -32,7 +32,7 @@ "@typescript-eslint/eslint-plugin": "^8.39.1", "@typescript-eslint/parser": "^8.39.1", "css-loader": "^6.7.1", - "cypress": "^11.0.1", + "cypress": "^14.2.1", "cypress-multi-reporters": "^1.6.2", "eslint": "^8.44.0", "eslint-config-prettier": "^8.5.0", From b7887605e733b4342d8b4c8b4becc440a2947a75 Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Wed, 15 Oct 2025 13:08:45 +0200 Subject: [PATCH 33/35] NO-JIRA: Do not fetch filter bar values on every re-render, and prevent flicker Signed-off-by: Andreas Gerstmayr --- web/src/hooks/useTagValues.ts | 5 ++++ web/src/pages/TracesPage/QueryBrowser.tsx | 17 ++++++------ .../TracesPage/Toolbar/AttributeFilters.tsx | 26 +++++++++---------- .../TracesPage/Toolbar/FilterToolbar.tsx | 6 ++--- 4 files changed, 28 insertions(+), 26 deletions(-) diff --git a/web/src/hooks/useTagValues.ts b/web/src/hooks/useTagValues.ts index a7b264f..8b35586 100644 --- a/web/src/hooks/useTagValues.ts +++ b/web/src/hooks/useTagValues.ts @@ -26,5 +26,10 @@ export function useTagValues( .sort((a, b) => a.value.localeCompare(b.value)); }, staleTime: 60 * 1000, // cache tag value response for 1m + // Keep the previous query result during loading. + // Without this setting, the select boxes in the filter bar flicker on every selection, + // because the select box data goes from list of values -> undefined (during loading state) -> list of values. + // The select box values are refreshed because the absolute time range changes. + keepPreviousData: true, }); } diff --git a/web/src/pages/TracesPage/QueryBrowser.tsx b/web/src/pages/TracesPage/QueryBrowser.tsx index f668c15..9ac16c1 100644 --- a/web/src/pages/TracesPage/QueryBrowser.tsx +++ b/web/src/pages/TracesPage/QueryBrowser.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect } from 'react'; +import React, { useCallback, useEffect, useRef } from 'react'; import { Divider, PageSection, Split, SplitItem, Stack, Title } from '@patternfly/react-core'; import { useTranslation } from 'react-i18next'; import { TimeRangeSelect } from '../../components/TimeRangeSelect'; @@ -31,11 +31,18 @@ export function QueryBrowserBody() { const [query, setQuery] = useQueryParam('q', withDefault(StringParam, '{}')); const [limit, setLimit] = useQueryParam('limit', withDefault(NumberParam, DEFAULT_LIMIT)); const { refresh } = useTimeRange(); + const isInitialRender = useRef(true); // Refresh query if Tempo instance or tenant changes. // The Perses data source is selected via a mock DatasourceApiImpl implementation in , // therefore Perses doesn't refresh automatically if the Tempo instance changes. useEffect(() => { + // Only call refresh() if the Tempo instance changed, not on the initial render of this component. + if (isInitialRender.current) { + isInitialRender.current = false; + return; + } + refresh(); }, [tempo, refresh]); @@ -61,13 +68,7 @@ export function QueryBrowserBody() {
- + void; + runQuery: (query: string) => void; } const statusOptions = [ @@ -59,44 +59,42 @@ const statusOptions = [ export function AttributeFilters(props: AttributeFiltersProps) { const { t } = useTranslation('plugin__distributed-tracing-console-plugin'); - const { tempo, query, setQuery } = props; + const { tempo, query, runQuery } = props; const [activeFilter, setActiveFilter] = useState( attributeFilterOptions[0].value, ); const filter = traceQLToFilter(query); const setFilter = (filter: Filter) => { - setQuery(filterToTraceQL(filter)); + runQuery(filterToTraceQL(filter)); }; - const { timeRange } = useTimeRange(); - const absTimeRange = !isAbsoluteTimeRange(timeRange) ? toAbsoluteTimeRange(timeRange) : timeRange; - const startTime = Math.round(absTimeRange.start.getTime() / 1000); - const endTime = Math.round(absTimeRange.end.getTime() / 1000); + const { absoluteTimeRange } = useTimeRange(); + const { start, end } = getUnixTimeRange(absoluteTimeRange); const { data: serviceNameOptions } = useTagValues( tempo, 'resource.service.name', filterToTraceQL({ ...filter, serviceName: [] }), activeFilter === serviceNameFilter.value, - startTime, - endTime, + start, + end, ); const { data: spanNameOptions } = useTagValues( tempo, 'name', filterToTraceQL({ ...filter, spanName: [] }), activeFilter === spanNameFilter.value, - startTime, - endTime, + start, + end, ); const { data: namespaceOptions } = useTagValues( tempo, 'resource.k8s.namespace.name', filterToTraceQL({ ...filter, namespace: [] }), activeFilter === namespaceFilter.value, - startTime, - endTime, + start, + end, ); return ( diff --git a/web/src/pages/TracesPage/Toolbar/FilterToolbar.tsx b/web/src/pages/TracesPage/Toolbar/FilterToolbar.tsx index 4d7a63c..5d677e3 100644 --- a/web/src/pages/TracesPage/Toolbar/FilterToolbar.tsx +++ b/web/src/pages/TracesPage/Toolbar/FilterToolbar.tsx @@ -22,15 +22,13 @@ interface FilterToolbarProps { tempo: TempoInstance | undefined; setTempo: (tempo?: TempoInstance) => void; query: string; - /** Update the query. Fetches the search results only if the query changed. */ - setQuery: (query: string) => void; /** Update and execute the query. Always fetches the search results. */ runQuery: (query: string) => void; } export function FilterToolbar(props: FilterToolbarProps) { const { t } = useTranslation('plugin__distributed-tracing-console-plugin'); - const { tempo, setTempo, query, setQuery, runQuery } = props; + const { tempo, setTempo, query, runQuery } = props; const [showAttributeFilters, setShowAttributeFilters] = useState(isSimpleTraceQLQuery(query)); return ( @@ -39,7 +37,7 @@ export function FilterToolbar(props: FilterToolbarProps) { } breakpoint="xl"> {showAttributeFilters ? ( - + ) : ( )} From 3def8e56d6bf955becb9db1cad26661344003520 Mon Sep 17 00:00:00 2001 From: Ishwar Kanse Date: Tue, 16 Sep 2025 08:26:58 +0530 Subject: [PATCH 34/35] Add test for span links and other fixes. Assisted by Claude and Cursor IDE --- tests/Dockerfile-cypress | 4 +- tests/PATTERNFLY_COMMANDS_EXAMPLES.md | 6 +- tests/README.md | 2 +- tests/SELECTOR_BEST_PRACTICES.md | 10 +- tests/e2e/dt-plugin-tests.cy.ts | 133 ++++++++++++++++-- tests/fixtures/.chainsaw.yaml | 14 +- .../02-assert.yaml | 4 + .../02-install-otelcol.yaml | 30 +++- .../assert-user-workload-monitoring.yaml | 28 ++++ .../chainsaw-test.yaml | 11 ++ .../check_metrics.sh | 28 ++++ .../install-user-workload-monitoring.yaml | 8 ++ .../kubeadmin-traces-verify.yaml | 4 +- .../tempo-rbac-sa-1-traces-gen.yaml | 6 +- .../tempo-rbac-sa-1-traces-verify.yaml | 8 +- .../tempo-rbac-sa-2-traces-gen.yaml | 6 +- .../multitenancy-rbac/02-install-otelcol.yaml | 30 +++- .../assert-user-workload-monitoring.yaml | 28 ++++ .../multitenancy-rbac/chainsaw-test.yaml | 13 +- .../multitenancy-rbac/check_metrics.sh | 28 ++++ .../install-user-workload-monitoring.yaml | 8 ++ .../kubeadmin-traces-verify.yaml | 4 +- .../tempo-rbac-sa-1-traces-gen.yaml | 6 +- .../tempo-rbac-sa-1-traces-verify.yaml | 8 +- .../tempo-rbac-sa-2-traces-gen.yaml | 6 +- tests/fixtures/monitoring-with-perses.yaml | 9 ++ tests/views/operator-hub-page.ts | 11 +- 27 files changed, 401 insertions(+), 52 deletions(-) create mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-user-workload-monitoring.yaml create mode 100755 tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/check_metrics.sh create mode 100644 tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/install-user-workload-monitoring.yaml create mode 100644 tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-user-workload-monitoring.yaml create mode 100755 tests/fixtures/chainsaw-tests/multitenancy-rbac/check_metrics.sh create mode 100644 tests/fixtures/chainsaw-tests/multitenancy-rbac/install-user-workload-monitoring.yaml create mode 100644 tests/fixtures/monitoring-with-perses.yaml diff --git a/tests/Dockerfile-cypress b/tests/Dockerfile-cypress index b181c20..374bf54 100755 --- a/tests/Dockerfile-cypress +++ b/tests/Dockerfile-cypress @@ -15,6 +15,6 @@ RUN wget -O chainsaw.tar.gz https://github.com/kyverno/chainsaw/releases/downloa && mv chainsaw /usr/local/bin/ \ && rm -rf chainsaw.tar.gz -# Install apache2-utils for htpasswd required by OCP CI -RUN apt update && apt install -y apache2-utils \ +# Install apache2-utils for htpasswd required by OCP CI, jq and curl for metrics validation +RUN apt update && apt install -y apache2-utils jq curl \ && rm -rf /var/lib/apt/lists/* diff --git a/tests/PATTERNFLY_COMMANDS_EXAMPLES.md b/tests/PATTERNFLY_COMMANDS_EXAMPLES.md index 7dca022..415c71e 100644 --- a/tests/PATTERNFLY_COMMANDS_EXAMPLES.md +++ b/tests/PATTERNFLY_COMMANDS_EXAMPLES.md @@ -111,7 +111,7 @@ cy.findByTestId('span-duration-bar').first().click(); // By test ID (multiple sp ```typescript // Single attribute validation -cy.muiTraceAttribute('net.peer.ip', '1.2.3.4'); +cy.muiTraceAttribute('network.peer.address', '1.2.3.4'); cy.muiTraceAttribute('peer.service', 'telemetrygen-client'); cy.muiTraceAttribute('k8s.container.name', 'telemetrygen', true); // Optional attribute @@ -122,7 +122,7 @@ cy.muiTraceAttribute('service.name', (text) => { // Bulk attribute validation (recommended for multiple attributes) cy.muiTraceAttributes({ - 'net.peer.ip': { value: '1.2.3.4' }, + 'network.peer.address': { value: '1.2.3.4' }, 'peer.service': { value: 'telemetrygen-client' }, 'k8s.container.name': { value: 'telemetrygen', @@ -168,7 +168,7 @@ describe('Trace inspection', () => { // Validate trace attributes efficiently cy.muiTraceAttributes({ - 'net.peer.ip': { value: '1.2.3.4' }, + 'network.peer.address': { value: '1.2.3.4' }, 'peer.service': { value: 'telemetrygen-client' }, 'service.name': { value: (text) => text.includes('rbac') diff --git a/tests/README.md b/tests/README.md index 6c959d1..2b81e6b 100644 --- a/tests/README.md +++ b/tests/README.md @@ -138,7 +138,7 @@ cy.muiSpanBar('grpc-service').click() // Attribute validation (bulk) cy.muiTraceAttributes({ 'service.name': { value: 'my-service' }, - 'net.peer.ip': { value: '1.2.3.4' }, + 'network.peer.address': { value: '1.2.3.4' }, 'k8s.namespace': { value: 'test-ns', optional: true } }) ``` diff --git a/tests/SELECTOR_BEST_PRACTICES.md b/tests/SELECTOR_BEST_PRACTICES.md index 895e7bd..3139860 100644 --- a/tests/SELECTOR_BEST_PRACTICES.md +++ b/tests/SELECTOR_BEST_PRACTICES.md @@ -63,9 +63,9 @@ cy.muiSpanBar('http-rbac-2') // Click specific span bar cy.muiFirstSpanBar() // Click first span bar // Trace Attribute Validation -cy.muiTraceAttribute('net.peer.ip', '1.2.3.4') // Single attribute check +cy.muiTraceAttribute('network.peer.address', '1.2.3.4') // Single attribute check cy.muiTraceAttributes({ // Bulk attribute validation - 'net.peer.ip': { value: '1.2.3.4' }, + 'network.peer.address': { value: '1.2.3.4' }, 'service.name': { value: (text) => text.includes('rbac') } }) ``` @@ -219,7 +219,7 @@ cy.findByTestId('span-duration-bar').first().click() // By test ID ### Trace Attribute Validation ```typescript // Old approach (48+ lines of repetitive code): -cy.contains('.MuiTypography-h5', 'net.peer.ip').next('.MuiTypography-body1').should('have.text', '1.2.3.4') +cy.contains('.MuiTypography-h5', 'network.peer.address').next('.MuiTypography-body1').should('have.text', '1.2.3.4') cy.contains('.MuiTypography-h5', 'peer.service').next('.MuiTypography-body1').should('have.text', 'telemetrygen-client') cy.get('body').then(($body) => { if ($body.find('.MuiTypography-h5:contains("k8s.container.name")').length > 0) { @@ -230,7 +230,7 @@ cy.get('body').then(($body) => { // New approach (12 lines, 75% reduction): cy.muiTraceAttributes({ - 'net.peer.ip': { value: '1.2.3.4' }, + 'network.peer.address': { value: '1.2.3.4' }, 'peer.service': { value: 'telemetrygen-client' }, 'k8s.container.name': { value: 'telemetrygen', optional: true }, 'k8s.namespace.name': { @@ -243,7 +243,7 @@ cy.muiTraceAttributes({ }, 'TempoStack') // Or individual attribute checks: -cy.muiTraceAttribute('net.peer.ip', '1.2.3.4') +cy.muiTraceAttribute('network.peer.address', '1.2.3.4') cy.muiTraceAttribute('service.name', (text) => text.includes('rbac'), false, 'TempoStack') ``` diff --git a/tests/e2e/dt-plugin-tests.cy.ts b/tests/e2e/dt-plugin-tests.cy.ts index dd33558..9c1eed6 100644 --- a/tests/e2e/dt-plugin-tests.cy.ts +++ b/tests/e2e/dt-plugin-tests.cy.ts @@ -350,7 +350,7 @@ describe('OpenShift Distributed Tracing UI Plugin tests', () => { .and('have.text', 'Create a TempoMonolithic instance'); }); - it('Test Distributed Tracing UI plugin with Tempo instances and verify traces using user having cluster-admin role', function () { + it('Test Distributed Tracing UI plugin with Tempo instances and verify traces, span links using user having cluster-admin role', function () { cy.log('Create TempoStack and TempoMonolithic instances'); cy.exec( 'chainsaw test --config ./fixtures/.chainsaw.yaml --skip-delete ./fixtures/chainsaw-tests', @@ -358,16 +358,17 @@ describe('OpenShift Distributed Tracing UI Plugin tests', () => { env: { KUBECONFIG: Cypress.env('KUBECONFIG_PATH'), }, - timeout: 1800000, + timeout: 1200000, failOnNonZeroExit: true } ) .then((result) => { expect(result.code).to.eq(0); cy.log(`Chainsaw test ran successfully: ${result.stdout}`); }); - cy.log('Navigate to the observe/traces page'); - cy.visit('/observe/traces'); + cy.log('Navigate to the /observe/traces page'); + cy.visit('/observe/traces'); + cy.url().should('include', '/observe/traces'); cy.log('Assert traces in TempoStack instance.'); cy.pfTypeahead('Select a Tempo instance').click(); cy.pfSelectMenuItem('chainsaw-rbac / simplst').click(); @@ -384,8 +385,93 @@ describe('OpenShift Distributed Tracing UI Plugin tests', () => { cy.muiFirstTraceLink().click(); cy.findByTestId('span-duration-bar').eq(1).click(); cy.muiTraceAttributes({ - 'net.peer.ip': { value: '1.2.3.4' }, - 'peer.service': { value: 'telemetrygen-client' }, + 'network.peer.address': { value: '1.2.3.4' }, + 'peer.service': { value: (text) => ['telemetrygen-server', 'telemetrygen-client'].includes(text) }, + 'k8s.container.name': { value: 'telemetrygen', optional: true }, + 'k8s.namespace.name': { + value: (text) => ['chainsaw-test-rbac-1', 'chainsaw-test-rbac-2', 'chainsaw-mono-rbac-1', 'chainsaw-mono-rbac-2'].includes(text), + optional: true + }, + 'service.name': { + value: (text) => ['http-rbac-1', 'http-rbac-2', 'grpc-rbac-1', 'grpc-rbac-2'].includes(text) + } + }, 'TempoStack'); + cy.log('Click on the Links tab'); + cy.get('button.MuiTab-root').contains('Links').click(); + cy.log('Verify link details are present'); + // Verify first link (index 0) + cy.muiTraceAttribute('link.index', '0', false, 'Links'); + cy.muiTraceAttribute('link.type', 'random', false, 'Links'); + // Verify trace ID and span ID have valid format for first link (they will be different each time) + cy.contains('.MuiTypography-h5', 'trace ID').first().next('.MuiTypography-body1').invoke('text').then((traceId) => { + cy.log(`First link trace ID: ${traceId.trim()}`); + expect(traceId.trim()).to.match(/^[A-F0-9]{32}$/); + }); + cy.contains('.MuiTypography-h5', 'span ID').first().next('.MuiTypography-body1').invoke('text').then((spanId) => { + cy.log(`First link span ID: ${spanId.trim()}`); + expect(spanId.trim()).to.match(/^[A-F0-9]{16}$/); + }); + cy.log('Click on the first trace ID link to navigate to that trace'); + cy.contains('.MuiTypography-h5', 'trace ID').first().next('.MuiTypography-body1').invoke('text').then((traceId) => { + const cleanTraceId = traceId.trim(); + cy.get('a.MuiLink-root[href*="/observe/traces/"]').first().click(); + + cy.log('Verify URL contains the correct trace ID'); + cy.url().should('include', `/observe/traces/${cleanTraceId}`); + cy.log(`✓ Successfully navigated to trace: ${cleanTraceId}`); + }); + cy.log('Verify navigation by checking trace attributes'); + cy.findByTestId('span-duration-bar').eq(1).click(); + cy.muiTraceAttributes({ + 'network.peer.address': { value: '1.2.3.4' }, + 'peer.service': { value: (text) => ['telemetrygen-server', 'telemetrygen-client'].includes(text) }, + 'k8s.container.name': { value: 'telemetrygen', optional: true }, + 'k8s.namespace.name': { + value: (text) => ['chainsaw-test-rbac-1', 'chainsaw-test-rbac-2', 'chainsaw-mono-rbac-1', 'chainsaw-mono-rbac-2'].includes(text), + optional: true + }, + 'service.name': { + value: (text) => ['http-rbac-1', 'http-rbac-2', 'grpc-rbac-1', 'grpc-rbac-2'].includes(text) + } + }, 'TempoStack'); + + cy.log('Rerun the steps and select span ID from links'); + cy.pfBreadcrumb('Traces').click(); + cy.pfCloseButtonIfExists('Close chip group'); + cy.pfTypeahead('Select a Tempo instance').click(); + cy.pfSelectMenuItem('chainsaw-rbac / simplst').click(); + cy.pfTypeahead('Select a tenant').click(); + cy.pfSelectMenuItem('dev').click(); + cy.muiSelect('Select time range').click(); + cy.muiSelectOption('Last 1 hour').click(); + cy.pfMenuToggle('Service Name').click(); + cy.pfMenuToggleByLabel('Multi typeahead checkbox').click(); + cy.pfCheckMenuItem('http-rbac-1'); + cy.pfCheckMenuItem('http-rbac-2'); + cy.pfCheckMenuItem('grpc-rbac-1'); + cy.pfCheckMenuItem('grpc-rbac-2'); + cy.muiFirstTraceLink().click(); + cy.findByTestId('span-duration-bar').eq(1).click(); + cy.log('Click on the Links tab again'); + cy.get('button.MuiTab-root').contains('Links').click(); + cy.log('Click on the first span ID link to navigate to that span'); + cy.contains('.MuiTypography-h5', 'trace ID').first().next('.MuiTypography-body1').invoke('text').then((traceId) => { + const cleanTraceId = traceId.trim(); + cy.contains('.MuiTypography-h5', 'span ID').first().next('.MuiTypography-body1').invoke('text').then((spanId) => { + const cleanSpanId = spanId.trim(); + cy.contains('.MuiTypography-h5', 'span ID').first().next('.MuiTypography-body1').find('a').first().click(); + + cy.log('Verify URL contains the correct trace ID and span ID'); + cy.url().should('include', `/observe/traces/${cleanTraceId}`); + cy.url().should('include', `selectSpan=${cleanSpanId}`); + cy.log(`✓ Successfully navigated to trace: ${cleanTraceId} with selected span: ${cleanSpanId}`); + }); + }); + cy.log('Verify navigation by checking trace attributes'); + cy.findByTestId('span-duration-bar').eq(1).click(); + cy.muiTraceAttributes({ + 'network.peer.address': { value: '1.2.3.4' }, + 'peer.service': { value: (text) => ['telemetrygen-server', 'telemetrygen-client'].includes(text) }, 'k8s.container.name': { value: 'telemetrygen', optional: true }, 'k8s.namespace.name': { value: (text) => ['chainsaw-test-rbac-1', 'chainsaw-test-rbac-2', 'chainsaw-mono-rbac-1', 'chainsaw-mono-rbac-2'].includes(text), @@ -396,6 +482,37 @@ describe('OpenShift Distributed Tracing UI Plugin tests', () => { } }, 'TempoStack'); + cy.log('Rerun the steps and select span links from the Traces page'); + cy.pfBreadcrumb('Traces').click(); + cy.pfCloseButtonIfExists('Close chip group'); + cy.pfTypeahead('Select a Tempo instance').click(); + cy.pfSelectMenuItem('chainsaw-rbac / simplst').click(); + cy.pfTypeahead('Select a tenant').click(); + cy.pfSelectMenuItem('dev').click(); + cy.muiSelect('Select time range').click(); + cy.muiSelectOption('Last 1 hour').click(); + cy.pfMenuToggle('Service Name').click(); + cy.pfMenuToggleByLabel('Multi typeahead checkbox').click(); + cy.pfCheckMenuItem('http-rbac-1'); + cy.pfCheckMenuItem('http-rbac-2'); + cy.pfCheckMenuItem('grpc-rbac-1'); + cy.pfCheckMenuItem('grpc-rbac-2'); + cy.muiFirstTraceLink().click(); + cy.get('[data-testid="LaunchIcon"]').first().click(); + cy.get('a[role="menuitem"]').contains('Open linked span').first().click(); + cy.muiTraceAttributes({ + 'network.peer.address': { value: '1.2.3.4' }, + 'peer.service': { value: (text) => ['telemetrygen-server', 'telemetrygen-client'].includes(text) }, + 'k8s.container.name': { value: 'telemetrygen', optional: true }, + 'k8s.namespace.name': { + value: (text) => ['chainsaw-test-rbac-1', 'chainsaw-test-rbac-2', 'chainsaw-mono-rbac-1', 'chainsaw-mono-rbac-2'].includes(text), + optional: true + }, + 'service.name': { + value: (text) => ['http-rbac-1', 'http-rbac-2', 'grpc-rbac-1', 'grpc-rbac-2'].includes(text) + } + }, 'TempoMonolithic'); + cy.log('Assert traces in TempoMonolithic instance.'); cy.pfBreadcrumb('Traces').click(); cy.pfCloseButtonIfExists('Close chip group'); @@ -414,8 +531,8 @@ describe('OpenShift Distributed Tracing UI Plugin tests', () => { cy.muiFirstTraceLink().click(); cy.findByTestId('span-duration-bar').eq(1).click(); cy.muiTraceAttributes({ - 'net.peer.ip': { value: '1.2.3.4' }, - 'peer.service': { value: 'telemetrygen-client' }, + 'network.peer.address': { value: '1.2.3.4' }, + 'peer.service': { value: (text) => ['telemetrygen-server', 'telemetrygen-client'].includes(text) }, 'k8s.container.name': { value: 'telemetrygen', optional: true }, 'k8s.namespace.name': { value: (text) => ['chainsaw-test-rbac-1', 'chainsaw-test-rbac-2', 'chainsaw-mono-rbac-1', 'chainsaw-mono-rbac-2'].includes(text), diff --git a/tests/fixtures/.chainsaw.yaml b/tests/fixtures/.chainsaw.yaml index c2507c4..e8a8b4c 100644 --- a/tests/fixtures/.chainsaw.yaml +++ b/tests/fixtures/.chainsaw.yaml @@ -4,11 +4,11 @@ kind: Configuration metadata: name: configuration spec: - parallel: 4 + parallel: 2 timeouts: - assert: 6m0s - cleanup: 5m0s - delete: 5m0s - error: 5m0s - apply: 10s - exec: 15s + assert: 15m0s + cleanup: 10m0s + delete: 10m0s + error: 10m0s + apply: 2m0s + exec: 5m0s diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/02-assert.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/02-assert.yaml index 4062bd9..252e077 100644 --- a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/02-assert.yaml +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/02-assert.yaml @@ -24,6 +24,10 @@ spec: port: 4318 protocol: TCP targetPort: 4318 + - name: prometheus + port: 8889 + protocol: TCP + targetPort: 8889 selector: app.kubernetes.io/component: opentelemetry-collector app.kubernetes.io/instance: chainsaw-mmo-rbac.dev diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/02-install-otelcol.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/02-install-otelcol.yaml index 0ce5aed..faf644b 100644 --- a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/02-install-otelcol.yaml +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/02-install-otelcol.yaml @@ -39,8 +39,13 @@ apiVersion: opentelemetry.io/v1alpha1 kind: OpenTelemetryCollector metadata: name: dev + namespace: chainsaw-mmo-rbac spec: serviceAccount: otel-collector-deployment + mode: deployment + observability: + metrics: + enableMetrics: true config: | extensions: bearertokenauth: @@ -54,6 +59,19 @@ spec: protocols: http: + connectors: + spanmetrics: + histogram: + explicit: + buckets: [2ms, 4ms, 6ms, 8ms, 10ms, 50ms, 100ms, 200ms, 400ms, 800ms, 1s, 1400ms, 2s, 5s, 10s, 15s] + dimensions: + - name: http.method + default: GET + - name: http.status_code + dimensions_cache_size: 1000 + aggregation_temporality: "AGGREGATION_TEMPORALITY_CUMULATIVE" + metrics_flush_interval: 5s + processors: k8sattributes: extract: @@ -89,6 +107,11 @@ spec: authenticator: bearertokenauth headers: X-Scope-OrgID: dev # tenantName + prometheus: + add_metric_suffixes: false + endpoint: 0.0.0.0:8889 + resource_to_telemetry_conversion: + enabled: true service: telemetry: @@ -100,11 +123,14 @@ spec: traces/grpc: receivers: [otlp/grpc] processors: [k8sattributes] - exporters: [otlp,debug] + exporters: [otlp,debug,spanmetrics] traces/http: receivers: [otlp/http] processors: [k8sattributes] - exporters: [otlphttp,debug] + exporters: [otlphttp,debug,spanmetrics] + metrics: + receivers: [spanmetrics] + exporters: [prometheus,debug] --- apiVersion: rbac.authorization.k8s.io/v1 diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-user-workload-monitoring.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-user-workload-monitoring.yaml new file mode 100644 index 0000000..a94fda1 --- /dev/null +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/assert-user-workload-monitoring.yaml @@ -0,0 +1,28 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: prometheus-operator + namespace: openshift-user-workload-monitoring +(status.replicas == spec.replicas): true +spec: + (replicas >= `1`): true + +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: prometheus-user-workload + namespace: openshift-user-workload-monitoring +(status.replicas == spec.replicas): true +spec: + (replicas >= `1`): true + +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: thanos-ruler-user-workload + namespace: openshift-user-workload-monitoring +(status.replicas == spec.replicas): true +spec: + (replicas >= `1`): true diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/chainsaw-test.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/chainsaw-test.yaml index 6606509..5d2d429 100644 --- a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/chainsaw-test.yaml +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/chainsaw-test.yaml @@ -5,6 +5,12 @@ metadata: spec: namespace: chainsaw-mmo-rbac steps: + - name: Enable user workload monitoring + try: + - apply: + file: install-user-workload-monitoring.yaml + - assert: + file: assert-user-workload-monitoring.yaml - name: step-01 try: - apply: @@ -59,3 +65,8 @@ spec: file: kubeadmin-traces-verify.yaml - assert: file: assert-kubeadmin-traces-verify.yaml + - name: Verify metrics + try: + - script: + timeout: 5m + content: ./check_metrics.sh diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/check_metrics.sh b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/check_metrics.sh new file mode 100755 index 0000000..e9140f8 --- /dev/null +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/check_metrics.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +oc create serviceaccount e2e-test-metrics-reader -n $NAMESPACE +oc adm policy add-cluster-role-to-user cluster-monitoring-view system:serviceaccount:$NAMESPACE:e2e-test-metrics-reader + +TOKEN=$(oc create token e2e-test-metrics-reader -n $NAMESPACE) +THANOS_QUERIER_HOST=$(oc get route thanos-querier -n openshift-monitoring -o json | jq -r '.spec.host') + +#Check metrics used in the prometheus rules created for TempoStack. Refer issue https://issues.redhat.com/browse/TRACING-3399 for skipped metrics. +metrics="traces_span_metrics_duration_bucket traces_span_metrics_duration_count traces_span_metrics_duration_sum traces_span_metrics_calls" + +for metric in $metrics; do +query="$metric" +count=0 + +# Keep fetching and checking the metrics until metrics with value is present. +while [[ $count -eq 0 ]]; do + response=$(curl -k -H "Authorization: Bearer $TOKEN" -H "Content-type: application/json" "https://$THANOS_QUERIER_HOST/api/v1/query?query=$query") + count=$(echo "$response" | jq -r '.data.result | length') + + if [[ $count -eq 0 ]]; then + echo "No metric '$metric' with value present. Retrying..." + sleep 5 # Wait for 5 seconds before retrying + else + echo "Metric '$metric' with value is present." + fi + done +done diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/install-user-workload-monitoring.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/install-user-workload-monitoring.yaml new file mode 100644 index 0000000..0d76888 --- /dev/null +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/install-user-workload-monitoring.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: cluster-monitoring-config + namespace: openshift-monitoring +data: + config.yaml: | + enableUserWorkload: true diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/kubeadmin-traces-verify.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/kubeadmin-traces-verify.yaml index a5d31da..9eed183 100644 --- a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/kubeadmin-traces-verify.yaml +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/kubeadmin-traces-verify.yaml @@ -46,7 +46,7 @@ spec: echo "Check for the strings in the trace output - cluster-admin should see complete traces" stringsToSearch=( - "\"key\":\"net.peer.ip\"" + "\"key\":\"network.peer.address\"" "\"stringValue\":\"1.2.3.4\"" "\"key\":\"peer.service\"" "\"stringValue\":\"telemetrygen-client\"" @@ -147,7 +147,7 @@ spec: echo "Check for the strings in the trace output - cluster-admin should see complete traces" stringsToSearch=( - "\"key\":\"net.peer.ip\"" + "\"key\":\"network.peer.address\"" "\"stringValue\":\"1.2.3.4\"" "\"key\":\"peer.service\"" "\"stringValue\":\"telemetrygen-client\"" diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-1-traces-gen.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-1-traces-gen.yaml index f8bb17f..780d654 100644 --- a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-1-traces-gen.yaml +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-1-traces-gen.yaml @@ -8,13 +8,14 @@ spec: spec: containers: - name: telemetrygen - image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.92.0 + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.137.0 args: - traces - --otlp-endpoint=dev-collector.chainsaw-mmo-rbac.svc:4317 - --service=grpc-rbac-1 - --otlp-insecure - --traces=2 + - --span-links=2 - --otlp-attributes=k8s.container.name="telemetrygen" - --otlp-attributes=k8s.namespace.name="chainsaw-mono-rbac-1" restartPolicy: Never @@ -29,7 +30,7 @@ spec: spec: containers: - name: telemetrygen - image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.92.0 + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.137.0 args: - traces - --otlp-endpoint=dev-collector.chainsaw-mmo-rbac.svc:4318 @@ -37,6 +38,7 @@ spec: - --otlp-insecure - --service=http-rbac-1 - --traces=2 + - --span-links=2 - --otlp-attributes=k8s.container.name="telemetrygen" - --otlp-attributes=k8s.namespace.name="chainsaw-mono-rbac-1" restartPolicy: Never diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-1-traces-verify.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-1-traces-verify.yaml index f8fb3dd..eb714a0 100644 --- a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-1-traces-verify.yaml +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-1-traces-verify.yaml @@ -43,7 +43,7 @@ spec: echo "Check for the strings in the trace output" stringsToSearch=( - "\"key\":\"net.peer.ip\"" + "\"key\":\"network.peer.address\"" "\"stringValue\":\"1.2.3.4\"" "\"key\":\"peer.service\"" "\"stringValue\":\"telemetrygen-client\"" @@ -85,7 +85,7 @@ spec: echo "Check for the strings in the trace output" stringsToSearch=( - "\"key\":\"net.peer.ip\"" + "\"key\":\"network.peer.address\"" "\"stringValue\":\"1.2.3.4\"" "\"key\":\"peer.service\"" "\"stringValue\":\"telemetrygen-client\"" @@ -147,7 +147,7 @@ spec: echo "Check for the strings in the trace output" stringsToSearch=( - "\"key\":\"net.peer.ip\"" + "\"key\":\"network.peer.address\"" "\"stringValue\":\"1.2.3.4\"" "\"key\":\"peer.service\"" "\"stringValue\":\"telemetrygen-client\"" @@ -189,7 +189,7 @@ spec: echo "Check for the strings in the trace output" stringsToSearch=( - "\"key\":\"net.peer.ip\"" + "\"key\":\"network.peer.address\"" "\"stringValue\":\"1.2.3.4\"" "\"key\":\"peer.service\"" "\"stringValue\":\"telemetrygen-client\"" diff --git a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-2-traces-gen.yaml b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-2-traces-gen.yaml index 85f5a4c..5c01a19 100644 --- a/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-2-traces-gen.yaml +++ b/tests/fixtures/chainsaw-tests/monolithic-multitenancy-rbac/tempo-rbac-sa-2-traces-gen.yaml @@ -8,13 +8,14 @@ spec: spec: containers: - name: telemetrygen - image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.92.0 + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.137.0 args: - traces - --otlp-endpoint=dev-collector.chainsaw-mmo-rbac.svc:4317 - --service=grpc-rbac-2 - --otlp-insecure - --traces=2 + - --span-links=2 - --otlp-attributes=k8s.container.name="telemetrygen" - --otlp-attributes=k8s.namespace.name="chainsaw-mono-rbac-2" restartPolicy: Never @@ -29,7 +30,7 @@ spec: spec: containers: - name: telemetrygen - image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.92.0 + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.137.0 args: - traces - --otlp-endpoint=dev-collector.chainsaw-mmo-rbac.svc:4318 @@ -37,6 +38,7 @@ spec: - --otlp-insecure - --service=http-rbac-2 - --traces=2 + - --span-links=2 - --otlp-attributes=k8s.container.name="telemetrygen" - --otlp-attributes=k8s.namespace.name="chainsaw-mono-rbac-2" restartPolicy: Never diff --git a/tests/fixtures/chainsaw-tests/multitenancy-rbac/02-install-otelcol.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/02-install-otelcol.yaml index 496526d..a1b2555 100644 --- a/tests/fixtures/chainsaw-tests/multitenancy-rbac/02-install-otelcol.yaml +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/02-install-otelcol.yaml @@ -42,6 +42,10 @@ metadata: namespace: chainsaw-rbac spec: serviceAccount: otel-collector-deployment + mode: deployment + observability: + metrics: + enableMetrics: true config: | extensions: bearertokenauth: @@ -55,6 +59,19 @@ spec: protocols: http: + connectors: + spanmetrics: + histogram: + explicit: + buckets: [2ms, 4ms, 6ms, 8ms, 10ms, 50ms, 100ms, 200ms, 400ms, 800ms, 1s, 1400ms, 2s, 5s, 10s, 15s] + dimensions: + - name: http.method + default: GET + - name: http.status_code + dimensions_cache_size: 1000 + aggregation_temporality: "AGGREGATION_TEMPORALITY_CUMULATIVE" + metrics_flush_interval: 5s + processors: k8sattributes: extract: @@ -92,6 +109,11 @@ spec: authenticator: bearertokenauth headers: X-Scope-OrgID: "dev" + prometheus: + add_metric_suffixes: false + endpoint: 0.0.0.0:8889 + resource_to_telemetry_conversion: + enabled: true service: telemetry: @@ -103,11 +125,15 @@ spec: traces/grpc: receivers: [otlp/grpc] processors: [k8sattributes] - exporters: [otlp,debug] + exporters: [otlp,debug,spanmetrics] traces/http: receivers: [otlp/http] processors: [k8sattributes] - exporters: [otlphttp,debug] + exporters: [otlphttp,debug,spanmetrics] + metrics: + receivers: [spanmetrics] + exporters: [prometheus,debug] + --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-user-workload-monitoring.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-user-workload-monitoring.yaml new file mode 100644 index 0000000..a94fda1 --- /dev/null +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/assert-user-workload-monitoring.yaml @@ -0,0 +1,28 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: prometheus-operator + namespace: openshift-user-workload-monitoring +(status.replicas == spec.replicas): true +spec: + (replicas >= `1`): true + +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: prometheus-user-workload + namespace: openshift-user-workload-monitoring +(status.replicas == spec.replicas): true +spec: + (replicas >= `1`): true + +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: thanos-ruler-user-workload + namespace: openshift-user-workload-monitoring +(status.replicas == spec.replicas): true +spec: + (replicas >= `1`): true diff --git a/tests/fixtures/chainsaw-tests/multitenancy-rbac/chainsaw-test.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/chainsaw-test.yaml index e6ed21d..2c5c955 100755 --- a/tests/fixtures/chainsaw-tests/multitenancy-rbac/chainsaw-test.yaml +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/chainsaw-test.yaml @@ -5,6 +5,12 @@ metadata: spec: namespace: chainsaw-rbac steps: + - name: Enable user workload monitoring + try: + - apply: + file: install-user-workload-monitoring.yaml + - assert: + file: assert-user-workload-monitoring.yaml - name: step-00 try: - apply: @@ -64,4 +70,9 @@ spec: - apply: file: kubeadmin-traces-verify.yaml - assert: - file: assert-kubeadmin-traces-verify.yaml \ No newline at end of file + file: assert-kubeadmin-traces-verify.yaml + - name: Verify metrics + try: + - script: + timeout: 5m + content: ./check_metrics.sh diff --git a/tests/fixtures/chainsaw-tests/multitenancy-rbac/check_metrics.sh b/tests/fixtures/chainsaw-tests/multitenancy-rbac/check_metrics.sh new file mode 100755 index 0000000..e9140f8 --- /dev/null +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/check_metrics.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +oc create serviceaccount e2e-test-metrics-reader -n $NAMESPACE +oc adm policy add-cluster-role-to-user cluster-monitoring-view system:serviceaccount:$NAMESPACE:e2e-test-metrics-reader + +TOKEN=$(oc create token e2e-test-metrics-reader -n $NAMESPACE) +THANOS_QUERIER_HOST=$(oc get route thanos-querier -n openshift-monitoring -o json | jq -r '.spec.host') + +#Check metrics used in the prometheus rules created for TempoStack. Refer issue https://issues.redhat.com/browse/TRACING-3399 for skipped metrics. +metrics="traces_span_metrics_duration_bucket traces_span_metrics_duration_count traces_span_metrics_duration_sum traces_span_metrics_calls" + +for metric in $metrics; do +query="$metric" +count=0 + +# Keep fetching and checking the metrics until metrics with value is present. +while [[ $count -eq 0 ]]; do + response=$(curl -k -H "Authorization: Bearer $TOKEN" -H "Content-type: application/json" "https://$THANOS_QUERIER_HOST/api/v1/query?query=$query") + count=$(echo "$response" | jq -r '.data.result | length') + + if [[ $count -eq 0 ]]; then + echo "No metric '$metric' with value present. Retrying..." + sleep 5 # Wait for 5 seconds before retrying + else + echo "Metric '$metric' with value is present." + fi + done +done diff --git a/tests/fixtures/chainsaw-tests/multitenancy-rbac/install-user-workload-monitoring.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/install-user-workload-monitoring.yaml new file mode 100644 index 0000000..0d76888 --- /dev/null +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/install-user-workload-monitoring.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: cluster-monitoring-config + namespace: openshift-monitoring +data: + config.yaml: | + enableUserWorkload: true diff --git a/tests/fixtures/chainsaw-tests/multitenancy-rbac/kubeadmin-traces-verify.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/kubeadmin-traces-verify.yaml index 57a62f6..69ba061 100644 --- a/tests/fixtures/chainsaw-tests/multitenancy-rbac/kubeadmin-traces-verify.yaml +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/kubeadmin-traces-verify.yaml @@ -46,7 +46,7 @@ spec: echo "Check for the strings in the trace output - cluster-admin should see complete traces" stringsToSearch=( - "\"key\":\"net.peer.ip\"" + "\"key\":\"network.peer.address\"" "\"stringValue\":\"1.2.3.4\"" "\"key\":\"peer.service\"" "\"stringValue\":\"telemetrygen-client\"" @@ -147,7 +147,7 @@ spec: echo "Check for the strings in the trace output - cluster-admin should see complete traces" stringsToSearch=( - "\"key\":\"net.peer.ip\"" + "\"key\":\"network.peer.address\"" "\"stringValue\":\"1.2.3.4\"" "\"key\":\"peer.service\"" "\"stringValue\":\"telemetrygen-client\"" diff --git a/tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-1-traces-gen.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-1-traces-gen.yaml index d64e939..d7eeed9 100644 --- a/tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-1-traces-gen.yaml +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-1-traces-gen.yaml @@ -8,13 +8,14 @@ spec: spec: containers: - name: telemetrygen - image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.92.0 + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.137.0 args: - traces - --otlp-endpoint=dev-collector.chainsaw-rbac.svc:4317 - --service=grpc-rbac-1 - --otlp-insecure - --traces=2 + - --span-links=2 - --otlp-attributes=k8s.container.name="telemetrygen" - --otlp-attributes=k8s.namespace.name="chainsaw-test-rbac-1" restartPolicy: Never @@ -29,7 +30,7 @@ spec: spec: containers: - name: telemetrygen - image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.92.0 + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.137.0 args: - traces - --otlp-endpoint=dev-collector.chainsaw-rbac.svc:4318 @@ -37,6 +38,7 @@ spec: - --otlp-insecure - --service=http-rbac-1 - --traces=2 + - --span-links=2 - --otlp-attributes=k8s.container.name="telemetrygen" - --otlp-attributes=k8s.namespace.name="chainsaw-test-rbac-1" restartPolicy: Never diff --git a/tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-1-traces-verify.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-1-traces-verify.yaml index bc0ed2f..3eaff6d 100644 --- a/tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-1-traces-verify.yaml +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-1-traces-verify.yaml @@ -43,7 +43,7 @@ spec: echo "Check for the strings in the trace output" stringsToSearch=( - "\"key\":\"net.peer.ip\"" + "\"key\":\"network.peer.address\"" "\"stringValue\":\"1.2.3.4\"" "\"key\":\"peer.service\"" "\"stringValue\":\"telemetrygen-client\"" @@ -86,7 +86,7 @@ spec: echo "Check for the strings in the trace output" stringsToSearch=( - "\"key\":\"net.peer.ip\"" + "\"key\":\"network.peer.address\"" "\"stringValue\":\"1.2.3.4\"" "\"key\":\"peer.service\"" "\"stringValue\":\"telemetrygen-client\"" @@ -148,7 +148,7 @@ spec: echo "Check for the strings in the trace output" stringsToSearch=( - "\"key\":\"net.peer.ip\"" + "\"key\":\"network.peer.address\"" "\"stringValue\":\"1.2.3.4\"" "\"key\":\"peer.service\"" "\"stringValue\":\"telemetrygen-client\"" @@ -190,7 +190,7 @@ spec: echo "Check for the strings in the trace output" stringsToSearch=( - "\"key\":\"net.peer.ip\"" + "\"key\":\"network.peer.address\"" "\"stringValue\":\"1.2.3.4\"" "\"key\":\"peer.service\"" "\"stringValue\":\"telemetrygen-client\"" diff --git a/tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-2-traces-gen.yaml b/tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-2-traces-gen.yaml index 3633d50..5dab8e9 100644 --- a/tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-2-traces-gen.yaml +++ b/tests/fixtures/chainsaw-tests/multitenancy-rbac/tempo-rbac-sa-2-traces-gen.yaml @@ -8,13 +8,14 @@ spec: spec: containers: - name: telemetrygen - image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.92.0 + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.137.0 args: - traces - --otlp-endpoint=dev-collector.chainsaw-rbac.svc:4317 - --service=grpc-rbac-2 - --otlp-insecure - --traces=2 + - --span-links=2 - --otlp-attributes=k8s.container.name="telemetrygen" - --otlp-attributes=k8s.namespace.name="chainsaw-test-rbac-2" restartPolicy: Never @@ -29,7 +30,7 @@ spec: spec: containers: - name: telemetrygen - image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.92.0 + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.137.0 args: - traces - --otlp-endpoint=dev-collector.chainsaw-rbac.svc:4318 @@ -37,6 +38,7 @@ spec: - --otlp-insecure - --service=http-rbac-2 - --traces=2 + - --span-links=2 - --otlp-attributes=k8s.container.name="telemetrygen" - --otlp-attributes=k8s.namespace.name="chainsaw-test-rbac-2" restartPolicy: Never diff --git a/tests/fixtures/monitoring-with-perses.yaml b/tests/fixtures/monitoring-with-perses.yaml new file mode 100644 index 0000000..6f3bb21 --- /dev/null +++ b/tests/fixtures/monitoring-with-perses.yaml @@ -0,0 +1,9 @@ +apiVersion: observability.openshift.io/v1alpha1 +kind: UIPlugin +metadata: + name: monitoring +spec: + type: Monitoring + monitoring: + perses: + enabled: true diff --git a/tests/views/operator-hub-page.ts b/tests/views/operator-hub-page.ts index a8509f5..4df858c 100644 --- a/tests/views/operator-hub-page.ts +++ b/tests/views/operator-hub-page.ts @@ -41,7 +41,16 @@ export const operatorHubPage = { cy.log('Installing operator using recommended namespace'); // Click operator recommended namespace radio button - cy.get('[data-test="Operator recommended Namespace:-radio-input"]').click(); + // Support both old and new OpenShift versions + cy.get('body').then(($body) => { + if ($body.find('#operator-namespace-recommended').length > 0) { + // New method: use ID selector + cy.get('#operator-namespace-recommended').click(); + } else { + // Old method: use data-test attribute + cy.get('[data-test="Operator recommended Namespace:-radio-input"]').click(); + } + }); // Enable monitoring checkbox if it exists (only for recommended namespace) helperfuncs.clickIfExist('[data-test="enable-monitoring"]'); From d03237cb92577b6c05c88235d0a1e42014407a7b Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Thu, 16 Oct 2025 12:12:07 +0200 Subject: [PATCH 35/35] NO-JIRA: Regenerate locales Signed-off-by: Andreas Gerstmayr --- .../plugin__distributed-tracing-console-plugin.json | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/web/locales/en/plugin__distributed-tracing-console-plugin.json b/web/locales/en/plugin__distributed-tracing-console-plugin.json index 1fdaa38..8498ea1 100644 --- a/web/locales/en/plugin__distributed-tracing-console-plugin.json +++ b/web/locales/en/plugin__distributed-tracing-console-plugin.json @@ -1,14 +1,4 @@ { - "Last 5 minutes": "Last 5 minutes", - "Last 15 minutes": "Last 15 minutes", - "Last 30 minutes": "Last 30 minutes", - "Last 1 hour": "Last 1 hour", - "Last 6 hours": "Last 6 hours", - "Last 12 hours": "Last 12 hours", - "Last 1 day": "Last 1 day", - "Last 7 days": "Last 7 days", - "Time range": "Time range", - "Select a time range": "Select a time range", "No Tempo instance selected": "No Tempo instance selected", "To explore data, select a Tempo instance and run a query.": "To explore data, select a Tempo instance and run a query.", "Tempo instance": "Tempo instance", @@ -16,6 +6,7 @@ "TempoStack and TempoMonolithic instances with multi-tenancy are supported. Instances without multi-tenancy are not supported.": "TempoStack and TempoMonolithic instances with multi-tenancy are supported. Instances without multi-tenancy are not supported.", "Tenant": "Tenant", "Select a tenant": "Select a tenant", + "Time range": "Time range", "Traces": "Traces", "Trace details": "Trace details", "Trace": "Trace",