From b543ba0125318ff57e12082c8508c5edea58d080 Mon Sep 17 00:00:00 2001 From: Jason Madigan Date: Wed, 4 Mar 2026 09:07:18 +0000 Subject: [PATCH 01/11] Merge pull request #300 from Kuadrant/dependabot/npm_and_yarn/ajv-6.14.0 Bump ajv from 6.12.6 to 6.14.0 Signed-off-by: Thomas Maas --- yarn.lock | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6fc07733..600bed09 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3224,18 +3224,18 @@ __metadata: linkType: hard "ajv@npm:^6.0.1, ajv@npm:^6.10.0, ajv@npm:^6.12.3, ajv@npm:^6.12.4, ajv@npm:^6.12.5": - version: 6.12.6 - resolution: "ajv@npm:6.12.6" + version: 6.14.0 + resolution: "ajv@npm:6.14.0" dependencies: fast-deep-equal: "npm:^3.1.1" fast-json-stable-stringify: "npm:^2.0.0" json-schema-traverse: "npm:^0.4.1" uri-js: "npm:^4.2.2" - checksum: 10c0/41e23642cbe545889245b9d2a45854ebba51cda6c778ebced9649420d9205f2efb39cb43dbc41e358409223b1ea43303ae4839db682c848b891e4811da1a5a71 + checksum: 10c0/a2bc39b0555dc9802c899f86990eb8eed6e366cddbf65be43d5aa7e4f3c4e1a199d5460fd7ca4fb3d864000dbbc049253b72faa83b3b30e641ca52cb29a68c22 languageName: node linkType: hard -"ajv@npm:^8.0.0, ajv@npm:^8.9.0": +"ajv@npm:^8.0.0, ajv@npm:^8.0.1, ajv@npm:^8.9.0": version: 8.17.1 resolution: "ajv@npm:8.17.1" dependencies: @@ -3247,18 +3247,6 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^8.0.1": - version: 8.12.0 - resolution: "ajv@npm:8.12.0" - dependencies: - fast-deep-equal: "npm:^3.1.1" - json-schema-traverse: "npm:^1.0.0" - require-from-string: "npm:^2.0.2" - uri-js: "npm:^4.2.2" - checksum: 10c0/ac4f72adf727ee425e049bc9d8b31d4a57e1c90da8d28bcd23d60781b12fcd6fc3d68db5df16994c57b78b94eed7988f5a6b482fd376dc5b084125e20a0a622e - languageName: node - linkType: hard - "ansi-align@npm:^2.0.0": version: 2.0.0 resolution: "ansi-align@npm:2.0.0" From 28993058bb93486e21d0f4f002e276fc3f51e6e0 Mon Sep 17 00:00:00 2001 From: dd di cesare <4183971+didierofrivia@users.noreply.github.com> Date: Wed, 18 Mar 2026 09:41:18 +0000 Subject: [PATCH 02/11] Merge pull request #331 from Kuadrant/fix-use-history Compatible Navigation with OpenShift Console plugin Signed-off-by: Thomas Maas --- src/components/DropdownWithKebab.tsx | 8 ++++---- src/components/GatewayPoliciesPage.tsx | 2 +- src/components/HTTPRoutePoliciesPage.tsx | 2 +- src/components/KuadrantCreateUpdate.tsx | 10 +++++----- src/components/KuadrantDNSPolicyCreatePage.tsx | 8 ++++---- src/components/KuadrantOverviewPage.tsx | 11 +++++------ src/components/KuadrantPoliciesPage.tsx | 10 +++++----- src/components/KuadrantTLSCreatePage.tsx | 8 ++++---- src/components/dnspolicy/useDNSPolicyActions.tsx | 6 +++--- src/components/tlspolicy/useTLSPolicyActions.tsx | 6 +++--- src/utils/cancel.tsx | 6 +++--- 11 files changed, 38 insertions(+), 39 deletions(-) diff --git a/src/components/DropdownWithKebab.tsx b/src/components/DropdownWithKebab.tsx index 78b72b9e..52c14ab4 100644 --- a/src/components/DropdownWithKebab.tsx +++ b/src/components/DropdownWithKebab.tsx @@ -16,7 +16,7 @@ import { } from '@patternfly/react-core'; import { k8sDelete, K8sResourceCommon } from '@openshift-console/dynamic-plugin-sdk'; -import { useHistory } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom-v5-compat'; import { RESOURCES, ResourceKind } from '../utils/resources'; import useAccessReviews from '../utils/resourceRBAC'; import { getModelFromResource, getResourceNameFromKind } from '../utils/getModelFromResource'; @@ -27,7 +27,7 @@ type DropdownWithKebabProps = { const DropdownWithKebab: React.FC = ({ obj }) => { const [isOpen, setIsOpen] = React.useState(false); const [isDeleteModalOpen, setIsDeleteModalOpen] = React.useState(false); - const history = useHistory(); + const navigate = useNavigate(); const model = getModelFromResource(obj); const onToggleClick = () => { @@ -75,13 +75,13 @@ const DropdownWithKebab: React.FC = ({ obj }) => { obj.kind === 'Gateway' || obj.kind === 'HTTPRoute' ) { - history.push({ + navigate({ pathname: `/k8s/ns/${obj.metadata.namespace}/${obj.apiVersion.replace('/', '~')}~${ obj.kind }/${obj.metadata.name}/yaml`, }); } else { - history.push({ + navigate({ pathname: `/k8s/ns/${obj.metadata.namespace}/${policyType}/name/${obj.metadata.name}/edit`, }); } diff --git a/src/components/GatewayPoliciesPage.tsx b/src/components/GatewayPoliciesPage.tsx index d5a8330b..ec67712d 100644 --- a/src/components/GatewayPoliciesPage.tsx +++ b/src/components/GatewayPoliciesPage.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import Helmet from 'react-helmet'; import { useTranslation } from 'react-i18next'; import { PageSection, Title } from '@patternfly/react-core'; -import { useLocation } from 'react-router-dom'; +import { useLocation } from 'react-router-dom-v5-compat'; import './kuadrant.css'; import AssociatedResourceList from './AssociatedResourceList'; import { diff --git a/src/components/HTTPRoutePoliciesPage.tsx b/src/components/HTTPRoutePoliciesPage.tsx index 378b34b3..455db6bc 100644 --- a/src/components/HTTPRoutePoliciesPage.tsx +++ b/src/components/HTTPRoutePoliciesPage.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import Helmet from 'react-helmet'; import { useTranslation } from 'react-i18next'; import { PageSection, Title } from '@patternfly/react-core'; -import { useLocation } from 'react-router-dom'; +import { useLocation } from 'react-router-dom-v5-compat'; import './kuadrant.css'; import AssociatedResourceList from './AssociatedResourceList'; import { diff --git a/src/components/KuadrantCreateUpdate.tsx b/src/components/KuadrantCreateUpdate.tsx index 8066636f..136d6e0f 100644 --- a/src/components/KuadrantCreateUpdate.tsx +++ b/src/components/KuadrantCreateUpdate.tsx @@ -8,13 +8,13 @@ import { } from '@openshift-console/dynamic-plugin-sdk'; import { useTranslation } from 'react-i18next'; import { Button, AlertVariant, Alert, AlertGroup } from '@patternfly/react-core'; -import { History } from 'history'; +import { NavigateFunction } from 'react-router-dom-v5-compat'; interface GenericPolicyForm { model: K8sModel; resource: K8sResourceCommon; policyType: string; - history: History; + navigate: NavigateFunction; validation: boolean; } @@ -22,7 +22,7 @@ const KuadrantCreateUpdate: React.FC = ({ model, resource, policyType, - history, + navigate, validation, }) => { const { t } = useTranslation('plugin__kuadrant-console-plugin'); @@ -40,14 +40,14 @@ const KuadrantCreateUpdate: React.FC = ({ data: resource, }); console.log(`${policyType} updated successfully:`, response); - history.push(`/kuadrant/all-namespaces/policies/${policyType}`); + navigate(`/kuadrant/all-namespaces/policies/${policyType}`); } else { const response = await k8sCreate({ model: model, data: resource, }); console.log(`${policyType} created successfully:`, response); - history.push(`/kuadrant/all-namespaces/policies/${policyType}`); + navigate(`/kuadrant/all-namespaces/policies/${policyType}`); } } catch (error) { if (update == true) { diff --git a/src/components/KuadrantDNSPolicyCreatePage.tsx b/src/components/KuadrantDNSPolicyCreatePage.tsx index ff2e27f5..758483e6 100644 --- a/src/components/KuadrantDNSPolicyCreatePage.tsx +++ b/src/components/KuadrantDNSPolicyCreatePage.tsx @@ -24,7 +24,7 @@ import { K8sResourceCommon, useActiveNamespace, } from '@openshift-console/dynamic-plugin-sdk'; -import { useHistory, useLocation } from 'react-router-dom'; +import { useNavigate, useLocation } from 'react-router-dom-v5-compat'; import { LoadBalancing, HealthCheck } from './dnspolicy/types'; import LoadBalancingField from './dnspolicy/LoadBalancingField'; import HealthCheckField from './dnspolicy/HealthCheckField'; @@ -135,7 +135,7 @@ const KuadrantDNSPolicyCreatePage: React.FC = () => { kind: dnsPolicyGVK.kind, }); - const history = useHistory(); + const navigate = useNavigate(); interface dnsPolicyEdit extends K8sResourceCommon { spec?: { @@ -266,7 +266,7 @@ const KuadrantDNSPolicyCreatePage: React.FC = () => { }; const handleCancelResource = () => { - handleCancel(selectedNamespace, dnsPolicy, history); + handleCancel(selectedNamespace, dnsPolicy, navigate); }; const formValidation = () => { if ( @@ -391,7 +391,7 @@ const KuadrantDNSPolicyCreatePage: React.FC = () => { model={dnsPolicyModel} resource={dnsPolicy} policyType="dns" - history={history} + navigate={navigate} validation={formValidation()} /> + + ) : ( - ) : ( - - - )} @@ -925,8 +990,8 @@ const KuadrantOverviewPage: React.FC = () => { resources={[resourceGVKMapping['Gateway']]} columns={gatewayTrafficColumns} renderers={gatewayTrafficRenders} - namespace="#ALL_NS#" - emtpyResourceName="Gateways" + namespace={watchNamespace} + emptyResourceName="Gateways" /> @@ -1041,7 +1106,7 @@ const KuadrantOverviewPage: React.FC = () => { resourceGVKMapping['TLSPolicy'], ]} columns={columns} - namespace="#ALL_NS#" + namespace={watchNamespace} paginationLimit={5} /> @@ -1054,27 +1119,27 @@ const KuadrantOverviewPage: React.FC = () => { {t('HTTPRoutes')} - {resourceRBAC['HTTPRoute']?.create ? ( + {!resourceRBAC['HTTPRoute']?.create ? ( + + + + ) : ( - ) : ( - - - )} diff --git a/src/utils/metricsQueries.ts b/src/utils/metricsQueries.ts index 5763909e..edddc1b4 100644 --- a/src/utils/metricsQueries.ts +++ b/src/utils/metricsQueries.ts @@ -1,20 +1,68 @@ -export const TOTAL_REQUESTS_QUERY = `sum by (source_workload, source_workload_namespace) (increase(istio_requests_total[24h]))`; +/** + * Escapes special characters in PromQL label values + * @param value - Label value to escape + * @returns Escaped value safe for PromQL interpolation + */ +const escapePromQLLabelValue = (value: string): string => + value.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); + +/** + * Builds a namespace filter for PromQL queries + * @param namespace - Optional namespace to filter by + * @returns PromQL label filter or empty string + */ +const buildNamespaceFilter = (namespace?: string): string => + namespace ? `source_workload_namespace="${escapePromQLLabelValue(namespace)}"` : ''; -export const ERROR_REQUEST_QUERY = `sum by (source_workload, source_workload_namespace) (increase(istio_requests_total{response_code!~"2(.*)|3(.*)"}[24h]))`; +/** + * Builds a Prometheus query for total requests, optionally filtered by namespace + * @param namespace - Optional namespace to filter by (omit for cluster-wide) + * @returns Prometheus query string + */ +export const buildTotalRequestsQuery = (namespace?: string): string => { + const namespaceFilter = buildNamespaceFilter(namespace); + return namespaceFilter + ? `sum by (source_workload, source_workload_namespace) (increase(istio_requests_total{${namespaceFilter}}[24h]))` + : `sum by (source_workload, source_workload_namespace) (increase(istio_requests_total[24h]))`; +}; -export const ERRORS_BY_CODE_QUERY = `sum by (response_code, source_workload, source_workload_namespace) (increase(istio_requests_total{response_code!~"2(.*)|3(.*)"}[24h]))`; +/** + * Builds a Prometheus query for error requests, optionally filtered by namespace + * @param namespace - Optional namespace to filter by (omit for cluster-wide) + * @returns Prometheus query string + */ +export const buildErrorRequestQuery = (namespace?: string): string => { + const namespaceFilter = buildNamespaceFilter(namespace); + return namespaceFilter + ? `sum by (source_workload, source_workload_namespace) (increase(istio_requests_total{${namespaceFilter},response_code!~"2(.*)|3(.*)"}[24h]))` + : `sum by (source_workload, source_workload_namespace) (increase(istio_requests_total{response_code!~"2(.*)|3(.*)"}[24h]))`; +}; + +/** + * Builds a Prometheus query for errors by status code, optionally filtered by namespace + * @param namespace - Optional namespace to filter by (omit for cluster-wide) + * @returns Prometheus query string + */ +export const buildErrorsByCodeQuery = (namespace?: string): string => { + const namespaceFilter = buildNamespaceFilter(namespace); + return namespaceFilter + ? `sum by (response_code, source_workload, source_workload_namespace) (increase(istio_requests_total{${namespaceFilter},response_code!~"2(.*)|3(.*)"}[24h]))` + : `sum by (response_code, source_workload, source_workload_namespace) (increase(istio_requests_total{response_code!~"2(.*)|3(.*)"}[24h]))`; +}; /** * Constructs a gateway lookup key for metric data based on namespace, name, and workload suffix. * * @param namespace - Kubernetes namespace of the gateway * @param name - Name of the gateway resource - * @param workloadSuffix - Suffix appended to gateway name in metrics (e.g., "-openshift-default" for OSSM 3) + * @param workloadSuffix - Suffix appended to gateway name in metrics (e.g., "-openshift-default" for OSSM 3, "-istio" for Istio) * @returns Lookup key in format "namespace/name" * * @example * // OSSM 3 (with -openshift-default suffix): * buildGatewayKey('ingress-gateway', 'my-gateway', '-openshift-default') // "ingress-gateway/my-gateway-openshift-default" + * // Istio gateway (with -istio suffix): + * buildGatewayKey('api-gateway', 'external', '-istio') // "api-gateway/external-istio" */ export const buildGatewayKey = ( namespace: string, From 6abed8e220b9480d9c8019e830f8a216b2245a24 Mon Sep 17 00:00:00 2001 From: Thomas Maas Date: Thu, 16 Apr 2026 09:54:47 +0200 Subject: [PATCH 11/11] Prepared release v0.3.7 Co-Authored-By: Claude Opus 4.6 (1M context) Signed-off-by: Thomas Maas --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 5597eda7..54790798 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "kuadrant-console-plugin", - "version": "0.3.6", + "version": "0.3.7", "description": "Kuadrant OpenShift Console plugin", "private": true, "license": "Apache-2.0", @@ -74,7 +74,7 @@ }, "consolePlugin": { "name": "kuadrant-console-plugin", - "version": "0.3.6", + "version": "0.3.7", "displayName": "Kuadrant OpenShift Console Plugin", "description": "Kuadrant OpenShift Console Plugin", "latestSupportedOpenshiftVersion": "4.19",