UNPKG

@kinvolk/headlamp-plugin

Version:

The needed infrastructure for building Headlamp plugins.

197 lines (196 loc) 8.48 kB
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.number ?? service.port.name ?? '-')) || (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" }) })), }, ] })); }