@kinvolk/headlamp-plugin
Version:
The needed infrastructure for building Headlamp plugins.
197 lines (196 loc) • 8.45 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/*
* Copyright 2025 The Kubernetes Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Box from '@mui/material/Box';
import MuiLink from '@mui/material/Link';
import Typography from '@mui/material/Typography';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import Ingress from '../../lib/k8s/ingress';
import { useSettings } from '../App/Settings/hook';
import LabelListItem from '../common/LabelListItem';
import Link from '../common/Link';
import { DetailsGrid } from '../common/Resource';
import { SectionBox } from '../common/SectionBox';
import SimpleTable from '../common/SimpleTable';
/**
* Is https used in the ingress item
*/
function isHttpsUsed(item, url) {
const hostList = item.jsonData.spec?.tls?.map(({ ...hosts }) => `${hosts.hosts}`) ?? [];
const isHttps = hostList.includes(`${url}`);
return isHttps;
}
/**
* Format the url to be used in the Link component
*/
export function LinkStringFormat({ url, item, urlPath }) {
let urlProtocol;
let formatURL;
/**
* Renders the "host" field as we intentionally do not supply a path.
* If the url is set to * or undefined/empty, and the optional path is not used, print url
*/
if ((url === '*' || !url) && !urlPath) {
return _jsx(Typography, { children: url || '' });
}
/**
* Renders the "path" field as we intentionally supply both a path and url.
*/
if ((url === '*' || !url) && urlPath) {
return _jsx(Typography, { children: urlPath });
}
else {
/*
* If the ingress does not use tls, then the url should be http
*/
if (!item.jsonData.spec?.tls) {
urlProtocol = 'http://';
}
else {
/*
* If the url is in the tls array, then the url should be https
*/
isHttpsUsed(item, url) ? (urlProtocol = 'https://') : (urlProtocol = 'http://');
}
/*
* Since we cannot access the prefix from the ingress object, we have to access it from the rules array
*/
const rules = item.spec.rules;
let currentPathType;
if (rules) {
for (let i = 0; i < rules.length; i++) {
for (let j = 0; j < rules[i].http.paths.length; j++) {
if (rules[i].http.paths[j].path === urlPath) {
currentPathType = rules[i].http.paths[j].pathType;
}
}
}
}
function isValidURL(url) {
try {
new URL(url);
}
catch (e) {
return false;
}
return true;
}
/**
* Adding the protocol to the url string is required for the URL constructor to work because data.host is only the host name
*/
if (!isValidURL(urlProtocol + url)) {
console.debug(`Ingress' host ${url} is not a valid URL; displaying it without a link.`);
return _jsx(Typography, { children: url });
}
formatURL = new URL(urlProtocol + url);
if (formatURL && urlPath) {
return (_jsxs(Box, { style: { display: 'flex', marginBottom: '5px' }, children: [_jsx(MuiLink, { href: `${formatURL.protocol}//${formatURL.hostname}${urlPath}`, style: { marginRight: '5px' }, children: urlPath }), `(${currentPathType})`] }));
}
return _jsx(MuiLink, { href: formatURL.toString(), children: `${formatURL.toString()}` });
}
}
/**
* Used to format the backend to be used in the backends field
*/
export function BackendFormat({ backend }) {
const backendList = [];
for (const backendItem of backend) {
let currentBackend = '';
if (!!backendItem.resource) {
currentBackend = backendItem.resource.kind + ':' + backendItem.resource.name;
}
if (!!backendItem.service) {
currentBackend =
backendItem.service.name +
':' +
(backendItem.service.port.number ?? backendItem.service.port.name ?? '').toString();
}
backendList.push(currentBackend);
}
return (_jsx(Box, { style: {
display: 'flex',
flexDirection: 'column',
flexWrap: 'nowrap',
marginBottom: '5px',
}, children: backendList.map(backendItem => (_jsx(Typography, { children: backendItem }, backendItem))) }));
}
export default function IngressDetails(props) {
const params = useParams();
const { name = params.name, namespace = params.namespace, cluster } = props;
const { t } = useTranslation(['glossary', 'translation']);
const storeRowsPerPageOptions = useSettings('tableRowsPerPageOptions');
function getPorts(item) {
const ports = [];
item.getRules().forEach(rule => {
rule.http?.paths.forEach(path => {
if (!!path.backend.service) {
const portNumber = path.backend.service.port.number ?? path.backend.service.port.name ?? '';
ports.push(portNumber.toString());
}
else if (!!path.backend.resource) {
ports.push(path.backend.resource.kind + ':' + path.backend.resource.name);
}
});
});
if (!!item.spec?.tls) {
ports.push('443');
}
return ports.sort((a, b) => a.localeCompare(b));
}
function getDefaultBackend(item) {
const { service, resource } = item.spec?.defaultBackend || {};
return ((service && service.name + ':' + service.port.toString()) ||
(resource && resource.kind + '/' + resource.name) ||
'-');
}
return (_jsx(DetailsGrid, { resourceType: Ingress, name: name, namespace: namespace, cluster: cluster, withEvents: true, extraInfo: ingress => ingress && [
{
name: t('Default Backend'),
value: getDefaultBackend(ingress),
},
{
name: t('Ports'),
value: getPorts(ingress).join(', '),
},
{
name: 'TLS',
value: (_jsx(LabelListItem, { labels: ingress.spec?.tls?.map((tls) => `${tls.secretName} 🞂 ${tls.hosts.join(', ')}`) })),
},
{
name: t('Class Name'),
value: ingress.spec?.ingressClassName ? (_jsx(Link, { routeName: "ingressclass", params: { name: ingress.spec?.ingressClassName }, activeCluster: ingress.cluster, children: ingress.spec?.ingressClassName })) : null,
},
], extraSections: item => [
{
id: 'headlamp.ingress-rules',
section: item && (_jsx(SectionBox, { title: t('Rules'), children: _jsx(SimpleTable, { rowsPerPage: storeRowsPerPageOptions, emptyMessage: t('translation|No rules data to be shown.'), columns: [
{
label: t('translation|Host'),
getter: (data) => (_jsx(LinkStringFormat, { url: data.host || '*', item: item })),
},
{
label: t('translation|Path'),
getter: (data) => data.http?.paths.map(({ path }) => (_jsx(LinkStringFormat, { url: data.host || '*', item: item, urlPath: path }, path))),
},
{
label: t('Backends'),
getter: (data) => (_jsx(BackendFormat, { backend: data.http?.paths.map(({ backend }) => backend) ?? [] })),
},
], data: item?.getRules() || [], reflectInURL: "rules" }) })),
},
] }));
}