diff --git a/web/ui/react-app/src/pages/targets/EndpointLink.test.tsx b/web/ui/react-app/src/pages/targets/EndpointLink.test.tsx
index 6ec682942..9133219f6 100644
--- a/web/ui/react-app/src/pages/targets/EndpointLink.test.tsx
+++ b/web/ui/react-app/src/pages/targets/EndpointLink.test.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import { shallow, mount } from 'enzyme';
-import { Badge, Alert } from 'reactstrap';
+import { Badge } from 'reactstrap';
import EndpointLink from './EndpointLink';
describe('EndpointLink', () => {
@@ -29,11 +29,24 @@ describe('EndpointLink', () => {
const targetLabel = badges.filterWhere((badge) => badge.children().text() === 'target="http://some-service"');
expect(targetLabel.length).toEqual(1);
});
-
- it('renders an alert if url is invalid', () => {
- const endpointLink = shallow();
- const err = endpointLink.find(Alert);
- expect(err.render().text()).toEqual('Error: Invalid URL: afdsacas');
+ // In cases of IPv6 addresses with a Zone ID, URL may not be parseable.
+ // See https://github.com/prometheus/prometheus/issues/9760
+ it('renders an anchor for IPv6 link with zone ID including labels for query params', () => {
+ const endpoint =
+ 'http://[fe80::f1ee:adeb:371d:983%eth1]:9100/stats/prometheus?module=http_2xx&target=http://some-service';
+ const globalURL =
+ 'http://[fe80::f1ee:adeb:371d:983%eth1]:9100/stats/prometheus?module=http_2xx&target=http://some-service';
+ const endpointLink = shallow();
+ const anchor = endpointLink.find('a');
+ const badges = endpointLink.find(Badge);
+ expect(anchor.prop('href')).toEqual(globalURL);
+ expect(anchor.children().text()).toEqual('http://[fe80::f1ee:adeb:371d:983%eth1]:9100/stats/prometheus');
+ expect(endpointLink.find('br')).toHaveLength(1);
+ expect(badges).toHaveLength(2);
+ const moduleLabel = badges.filterWhere((badge) => badge.children().text() === 'module="http_2xx"');
+ expect(moduleLabel.length).toEqual(1);
+ const targetLabel = badges.filterWhere((badge) => badge.children().text() === 'target="http://some-service"');
+ expect(targetLabel.length).toEqual(1);
});
it('handles params with multiple values correctly', () => {
diff --git a/web/ui/react-app/src/pages/targets/EndpointLink.tsx b/web/ui/react-app/src/pages/targets/EndpointLink.tsx
index 083017225..504fbc337 100644
--- a/web/ui/react-app/src/pages/targets/EndpointLink.tsx
+++ b/web/ui/react-app/src/pages/targets/EndpointLink.tsx
@@ -1,5 +1,5 @@
import React, { FC } from 'react';
-import { Badge, Alert } from 'reactstrap';
+import { Badge } from 'reactstrap';
export interface EndpointLinkProps {
endpoint: string;
@@ -8,23 +8,28 @@ export interface EndpointLinkProps {
const EndpointLink: FC = ({ endpoint, globalUrl }) => {
let url: URL;
+ let search = '';
+ let invalidURL = false;
try {
url = new URL(endpoint);
} catch (err: unknown) {
- const error = err as Error;
- return (
-
- Error: {error.message}
-
- );
+ // In cases of IPv6 addresses with a Zone ID, URL may not be parseable.
+ // See https://github.com/prometheus/prometheus/issues/9760
+ // In this case, we attempt to prepare a synthetic URL with the
+ // same query parameters, for rendering purposes.
+ invalidURL = true;
+ if (endpoint.indexOf('?') > -1) {
+ search = endpoint.substring(endpoint.indexOf('?'));
+ }
+ url = new URL('http://0.0.0.0' + search);
}
const { host, pathname, protocol, searchParams }: URL = url;
const params = Array.from(searchParams.entries());
-
+ const displayLink = invalidURL ? endpoint.replace(search, '') : `${protocol}//${host}${pathname}`;
return (
<>
- {`${protocol}//${host}${pathname}`}
+ {displayLink}
{params.length > 0 ?
: null}
{params.map(([labelName, labelValue]: [string, string]) => {
return (