Repository navigation
Topology routing: per-Pod Service membership, default backends, private Knative, and every Internet entrypoint #2045
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
6c0b672
Connect each Pod only to the Services that select it
nadaverell 155a1f5
Keep an IngressRoute's backend when it names a ServersTransport
nadaverell aa2af37
Route Ingresses to their default backend, once per Service
nadaverell db00e92
Don't draw cluster-local Knative Services as reachable from the Internet
nadaverell 0a7f237
Keep every Internet entrypoint when the traffic view groups by namespace
nadaverell 5a46e1a
Check and show an Ingress's default backend
nadaverell 0ffc8cb
Keep a route's ServersTransport when its backend is missing
nadaverell 9436ce7
Test the cluster-local label apart from the private URL
nadaverell 1463fe0
Show resource default backends, and read only the default cluster-loc…
nadaverell 9e677a7
Resolve Knative visibility and Traefik transports as their controller…
nadaverell ccadc34
Require the label key when a selector value is empty
nadaverell 93d4f12
Say why a Knative Service is or isn't on the Internet
nadaverell File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
33 changes: 33 additions & 0 deletions
33
packages/k8s-ui/src/components/resources/renderers/IngressRenderer.defaultBackend.test.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import { describe, expect, it } from 'vitest' | ||
| import { renderToString } from 'react-dom/server' | ||
| import { IngressRenderer } from './IngressRenderer' | ||
|
|
||
| describe('IngressRenderer default backend', () => { | ||
| it('shows the default backend and no "not routed" warning', () => { | ||
| const html = renderToString( | ||
| <IngressRenderer data={{ | ||
| metadata: { name: 'catch-all', namespace: 'team' }, | ||
| spec: { ingressClassName: 'nginx', defaultBackend: { service: { name: 'fallback', port: { number: 80 } } } }, | ||
| }} />, | ||
| ) | ||
| expect(html).toContain('Default Backend') | ||
| expect(html.replace(/<!-- -->/g, '')).toContain('fallback:80') | ||
| expect(html).not.toContain('Traffic will not be routed') | ||
| }) | ||
|
|
||
| it('still warns when an Ingress routes nowhere', () => { | ||
| const html = renderToString(<IngressRenderer data={{ metadata: { name: 'empty', namespace: 'team' }, spec: { ingressClassName: 'nginx' } }} />) | ||
| expect(html).toContain('Traffic will not be routed') | ||
| }) | ||
|
|
||
| it('shows a resource default backend', () => { | ||
| const html = renderToString( | ||
| <IngressRenderer data={{ | ||
| metadata: { name: 'static', namespace: 'team' }, | ||
| spec: { ingressClassName: 'nginx', defaultBackend: { resource: { apiGroup: 'k8s.example.com', kind: 'StorageBucket', name: 'static-assets' } } }, | ||
| }} />, | ||
| ) | ||
| expect(html).toContain('Default Backend') | ||
| expect(html).toContain('StorageBucket/static-assets') | ||
| }) | ||
| }) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
packages/k8s-ui/src/components/topology/internet-groups.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| import { describe, expect, it } from 'vitest' | ||
| import type { TopologyEdge, TopologyNode } from '../../types' | ||
| import { splitInternetByGroup } from './internet-groups' | ||
|
|
||
| const node = (id: string, kind: string, namespace?: string): TopologyNode => | ||
| ({ id, kind, name: id, status: 'healthy', data: namespace ? { namespace } : {} }) as TopologyNode | ||
| const fromInternet = (target: string): TopologyEdge => ({ id: `internet-to-${target}`, source: 'internet', target, type: 'routes-to' }) | ||
|
|
||
| describe('splitInternetByGroup', () => { | ||
| it('keeps every entrypoint connected, not only Ingresses and Gateways', () => { | ||
| const nodes = [ | ||
| node('internet', 'Internet'), | ||
| node('ingress/shop/web', 'Ingress', 'shop'), | ||
| node('knativeservice/shop/api', 'KnativeService', 'shop'), | ||
| node('ingressroute/edge/dash', 'IngressRoute', 'edge'), | ||
| ] | ||
| const edges = nodes.slice(1).map(n => fromInternet(n.id)) | ||
| const out = splitInternetByGroup(nodes, edges, 'namespace') | ||
|
|
||
| const reached = out.edges.filter(e => out.nodes.find(n => n.id === e.source)?.kind === 'Internet').map(e => `${e.source} -> ${e.target}`) | ||
| expect(reached.sort()).toEqual([ | ||
| 'internet-namespace-edge -> ingressroute/edge/dash', | ||
| 'internet-namespace-shop -> ingress/shop/web', | ||
| 'internet-namespace-shop -> knativeservice/shop/api', | ||
| ]) | ||
| expect(out.nodes.some(n => n.id === 'internet')).toBe(false) | ||
| }) | ||
|
|
||
| it('leaves an ungrouped entrypoint on the original Internet node', () => { | ||
| const nodes = [node('internet', 'Internet'), node('ingress/shop/web', 'Ingress', 'shop'), node('gateway//shared', 'Gateway')] | ||
| const out = splitInternetByGroup(nodes, nodes.slice(1).map(n => fromInternet(n.id)), 'namespace') | ||
| expect(out.edges.some(e => e.source === 'internet' && e.target === 'gateway//shared')).toBe(true) | ||
| expect(out.nodes.some(n => n.id === 'internet')).toBe(true) | ||
| }) | ||
| }) |
55 changes: 55 additions & 0 deletions
55
packages/k8s-ui/src/components/topology/internet-groups.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| import type { GroupingMode, TopologyEdge, TopologyNode } from '../../types' | ||
| import { getGroupKey } from './layout' | ||
|
|
||
| /** | ||
| * Splits the traffic view's single Internet node into one per group, so each | ||
| * namespace or app shows its own entry from outside. Every original Internet | ||
| * edge is kept, whatever kind its entrypoint is (Ingress, Gateway, Istio | ||
| * Gateway, Knative Service, Traefik route, Contour HTTPProxy); an entrypoint | ||
| * with no group stays connected to the original Internet node. | ||
| */ | ||
| export function splitInternetByGroup( | ||
| nodes: TopologyNode[], | ||
| edges: TopologyEdge[], | ||
| groupMode: GroupingMode, | ||
| ): { nodes: TopologyNode[]; edges: TopologyEdge[] } { | ||
| if (groupMode === 'none') return { nodes, edges } | ||
| const internetNode = nodes.find(n => n.kind === 'Internet') | ||
| if (!internetNode) return { nodes, edges } | ||
|
|
||
| const nodeById = new Map(nodes.map(n => [n.id, n])) | ||
| const groups = new Map<string, { entry: TopologyNode; edges: TopologyEdge[] }>() | ||
| const regrouped = new Set<TopologyEdge>() | ||
| for (const edge of edges) { | ||
| if (edge.source !== internetNode.id) continue | ||
| const target = nodeById.get(edge.target) | ||
| const groupKey = target ? getGroupKey(target, groupMode) : null | ||
| if (!target || !groupKey) continue | ||
| if (!groups.has(groupKey)) groups.set(groupKey, { entry: target, edges: [] }) | ||
| groups.get(groupKey)!.edges.push(edge) | ||
| regrouped.add(edge) | ||
| } | ||
| if (groups.size === 0) return { nodes, edges } | ||
|
|
||
| const keepOriginal = edges.some(e => e.source === internetNode.id && !regrouped.has(e)) | ||
| const newNodes = keepOriginal ? [...nodes] : nodes.filter(n => n.id !== internetNode.id) | ||
| const newEdges = edges.filter(e => !regrouped.has(e)) | ||
| for (const [groupKey, group] of groups) { | ||
| const internetId = `internet-${groupMode}-${groupKey}` | ||
| newNodes.push({ | ||
| id: internetId, | ||
| kind: 'Internet', | ||
| name: 'Internet', | ||
| status: 'healthy', | ||
| data: { | ||
| // Group metadata so the node lands inside its entrypoints' group. | ||
| namespace: groupMode === 'namespace' ? groupKey : group.entry.data?.namespace, | ||
| labels: groupMode === 'app' ? { 'app.kubernetes.io/name': groupKey } : {}, | ||
| }, | ||
| }) | ||
| for (const edge of group.edges) { | ||
| newEdges.push({ ...edge, id: `${internetId}-to-${edge.target}`, source: internetId }) | ||
| } | ||
| } | ||
| return { nodes: newNodes, edges: newEdges } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.