@skyhook-io/k8s-ui 1.12.0 → 1.13.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -4
- package/src/components/applications/ApplicationDetail.tsx +2 -2
- package/src/components/cluster-switcher/ClusterSwitcher.tsx +32 -7
- package/src/components/issues/severity.ts +1 -0
- package/src/components/resources/ResourcesView.empty-note.test.ts +41 -0
- package/src/components/resources/ResourcesView.tsx +419 -9
- package/src/components/resources/index.ts +27 -0
- package/src/components/resources/kyverno-legacy-action.test.ts +98 -0
- package/src/components/resources/renderers/AnalysisRunRenderer.test.tsx +241 -0
- package/src/components/resources/renderers/AnalysisRunRenderer.tsx +166 -0
- package/src/components/resources/renderers/CNPGBackupRenderer.tsx +31 -2
- package/src/components/resources/renderers/CNPGClusterRenderer.test.tsx +33 -0
- package/src/components/resources/renderers/CNPGClusterRenderer.tsx +47 -4
- package/src/components/resources/renderers/CNPGDeclarativeRenderer.tsx +243 -0
- package/src/components/resources/renderers/CNPGObjectStoreRenderer.test.tsx +99 -0
- package/src/components/resources/renderers/CNPGObjectStoreRenderer.tsx +235 -0
- package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +23 -1
- package/src/components/resources/renderers/CNPGShared.tsx +27 -0
- package/src/components/resources/renderers/CalicoHostEndpointRenderer.tsx +71 -0
- package/src/components/resources/renderers/CalicoIPPoolRenderer.tsx +32 -0
- package/src/components/resources/renderers/CalicoNetworkPolicyDiagram.test.tsx +205 -0
- package/src/components/resources/renderers/CalicoNetworkPolicyDiagram.tsx +537 -0
- package/src/components/resources/renderers/CalicoNetworkPolicyRenderer.test.tsx +223 -0
- package/src/components/resources/renderers/CalicoNetworkPolicyRenderer.tsx +670 -0
- package/src/components/resources/renderers/CalicoTierRenderer.tsx +19 -0
- package/src/components/resources/renderers/KyvernoCELPolicyRenderers.tsx +13 -6
- package/src/components/resources/renderers/KyvernoExceptionRenderers.tsx +3 -2
- package/src/components/resources/renderers/KyvernoGlobalContextRenderer.test.ts +31 -0
- package/src/components/resources/renderers/KyvernoGlobalContextRenderer.tsx +124 -0
- package/src/components/resources/renderers/KyvernoPolicyReportRenderer.tsx +28 -2
- package/src/components/resources/renderers/KyvernoQueueRenderers.tsx +303 -0
- package/src/components/resources/renderers/LookupFailureNote.test.tsx +60 -0
- package/src/components/resources/renderers/LookupFailureNote.tsx +42 -0
- package/src/components/resources/renderers/NamespaceRenderer.test.ts +30 -0
- package/src/components/resources/renderers/NamespaceRenderer.tsx +7 -3
- package/src/components/resources/renderers/NetworkPolicyDiagram.tsx +27 -26
- package/src/components/resources/renderers/NetworkPolicyRenderer.tsx +6 -3
- package/src/components/resources/renderers/PolicyCoverageSection.test.tsx +348 -0
- package/src/components/resources/renderers/PolicyCoverageSection.tsx +939 -0
- package/src/components/resources/renderers/PolicySection.test.tsx +59 -2
- package/src/components/resources/renderers/PolicySection.tsx +32 -5
- package/src/components/resources/renderers/RolloutRenderer.test.tsx +491 -0
- package/src/components/resources/renderers/RolloutRenderer.tsx +391 -92
- package/src/components/resources/renderers/VeleroBSLRenderer.test.tsx +374 -0
- package/src/components/resources/renderers/VeleroBSLRenderer.tsx +179 -3
- package/src/components/resources/renderers/VeleroBackupRenderer.test.tsx +221 -0
- package/src/components/resources/renderers/VeleroBackupRenderer.tsx +119 -6
- package/src/components/resources/renderers/VeleroBackupRepositoryRenderer.test.tsx +69 -0
- package/src/components/resources/renderers/VeleroBackupRepositoryRenderer.tsx +96 -0
- package/src/components/resources/renderers/VeleroRestoreRenderer.test.tsx +55 -0
- package/src/components/resources/renderers/VeleroRestoreRenderer.tsx +22 -4
- package/src/components/resources/renderers/VeleroRunMessages.test.tsx +152 -0
- package/src/components/resources/renderers/VeleroRunMessages.tsx +139 -0
- package/src/components/resources/renderers/VeleroScheduleRenderer.test.tsx +88 -0
- package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +16 -6
- package/src/components/resources/renderers/calico-cells.tsx +177 -0
- package/src/components/resources/renderers/cnpg-cells.tsx +100 -0
- package/src/components/resources/renderers/index.ts +16 -1
- package/src/components/resources/renderers/kyverno-cells.tsx +81 -0
- package/src/components/resources/renderers/network-policy-peer-styles.ts +25 -0
- package/src/components/resources/renderers/velero-cells.tsx +29 -7
- package/src/components/resources/renderers/velero-dead-ends.test.tsx +142 -0
- package/src/components/resources/resource-utils-calico.test.ts +215 -0
- package/src/components/resources/resource-utils-calico.ts +261 -0
- package/src/components/resources/resource-utils-cnpg-declarative.test.ts +104 -0
- package/src/components/resources/resource-utils-cnpg-objectstore.test.ts +122 -0
- package/src/components/resources/resource-utils-cnpg.test.ts +54 -0
- package/src/components/resources/resource-utils-cnpg.ts +264 -3
- package/src/components/resources/resource-utils-kyverno-modern.ts +24 -0
- package/src/components/resources/resource-utils-kyverno-queue.test.ts +164 -0
- package/src/components/resources/resource-utils-kyverno-queue.ts +198 -0
- package/src/components/resources/resource-utils-kyverno.ts +43 -7
- package/src/components/resources/resource-utils-velero.test.ts +26 -0
- package/src/components/resources/resource-utils-velero.ts +68 -0
- package/src/components/resources/resource-utils.ts +98 -0
- package/src/components/shared/ResourceActionsBar.tsx +101 -16
- package/src/components/shared/ResourceRendererDispatch.test.tsx +379 -3
- package/src/components/shared/ResourceRendererDispatch.tsx +199 -22
- package/src/components/shared/index.ts +1 -1
- package/src/components/shared/revision-role-badges.test.ts +120 -0
- package/src/components/topology/K8sResourceNode.test.ts +10 -0
- package/src/components/topology/K8sResourceNode.tsx +22 -0
- package/src/components/topology/TopologyFilterSidebar.test.tsx +31 -0
- package/src/components/topology/TopologyFilterSidebar.tsx +5 -0
- package/src/components/topology/TopologyGraph.tsx +6 -6
- package/src/components/topology/TopologySearch.tsx +7 -0
- package/src/components/topology/layout.ts +3 -0
- package/src/components/trace/ReachabilityView.tsx +1 -0
- package/src/components/trace/TracePanel.tsx +13 -2
- package/src/components/trace/reachFixtures.ts +26 -1
- package/src/components/trace/reachGraphModel.test.ts +20 -0
- package/src/components/trace/reachGraphModel.ts +8 -1
- package/src/components/trace/reachOrigins.test.ts +31 -0
- package/src/components/trace/reachOrigins.ts +11 -1
- package/src/components/ui/Badge.tsx +10 -0
- package/src/components/ui/InClusterConsentDialog.tsx +5 -4
- package/src/components/ui/ScheduleValue.test.tsx +80 -0
- package/src/components/ui/ScheduleValue.tsx +85 -0
- package/src/components/ui/SelectMenu.tsx +202 -28
- package/src/components/ui/drawer-components.test.tsx +17 -1
- package/src/components/ui/drawer-components.tsx +9 -2
- package/src/components/ui/index.ts +2 -0
- package/src/components/workload/WorkloadView.tsx +5 -2
- package/src/types/core.ts +36 -4
- package/src/types/policy.ts +153 -2
- package/src/utils/clipboard.test.ts +86 -0
- package/src/utils/clipboard.ts +50 -0
- package/src/utils/extended-resources.ts +4 -7
- package/src/utils/format.test.ts +83 -1
- package/src/utils/format.ts +80 -1
- package/src/utils/inClusterConsent.test.ts +9 -0
- package/src/utils/inClusterConsent.ts +9 -9
- package/src/utils/index.ts +1 -0
- package/src/utils/navigation.test.ts +5 -0
- package/src/utils/navigation.ts +5 -0
- package/src/utils/resource-icons.test.ts +49 -2
- package/src/utils/resource-icons.ts +29 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Clock, Database } from 'lucide-react'
|
|
2
2
|
import { Section, PropertyList, Property, AlertBanner, ResourceLink } from '../../ui/drawer-components'
|
|
3
|
+
import { CronValue } from '../../ui/ScheduleValue'
|
|
3
4
|
import {
|
|
4
5
|
getCNPGScheduledBackupCluster,
|
|
5
6
|
getCNPGScheduleCron,
|
|
@@ -8,7 +9,9 @@ import {
|
|
|
8
9
|
getCNPGScheduledBackupNextSchedule,
|
|
9
10
|
getCNPGScheduledBackupIsSuspended,
|
|
10
11
|
getCNPGScheduledBackupIsImmediate,
|
|
12
|
+
getCNPGBackupPlugin,
|
|
11
13
|
getCNPGScheduledBackupOwnerRef,
|
|
14
|
+
CNPG_BARMAN_OBJECTSTORE_GROUP,
|
|
12
15
|
} from '../resource-utils-cnpg'
|
|
13
16
|
|
|
14
17
|
interface CNPGScheduledBackupRendererProps {
|
|
@@ -19,6 +22,7 @@ interface CNPGScheduledBackupRendererProps {
|
|
|
19
22
|
export function CNPGScheduledBackupRenderer({ data, onNavigate }: CNPGScheduledBackupRendererProps) {
|
|
20
23
|
const isSuspended = getCNPGScheduledBackupIsSuspended(data)
|
|
21
24
|
const clusterName = getCNPGScheduledBackupCluster(data)
|
|
25
|
+
const schedulePlugin = getCNPGBackupPlugin(data)
|
|
22
26
|
|
|
23
27
|
return (
|
|
24
28
|
<>
|
|
@@ -34,7 +38,7 @@ export function CNPGScheduledBackupRenderer({ data, onNavigate }: CNPGScheduledB
|
|
|
34
38
|
{/* Schedule */}
|
|
35
39
|
<Section title="Schedule" icon={Clock} defaultExpanded>
|
|
36
40
|
<PropertyList>
|
|
37
|
-
<Property label="Cron Expression" value={getCNPGScheduleCron(data)} />
|
|
41
|
+
<Property label="Cron Expression" value={<CronValue cron={getCNPGScheduleCron(data)} dialect="seconds" />} />
|
|
38
42
|
<Property label="Last Schedule" value={getCNPGScheduledBackupLastSchedule(data)} />
|
|
39
43
|
<Property label="Next Schedule" value={getCNPGScheduledBackupNextSchedule(data)} />
|
|
40
44
|
<Property label="Suspended" value={isSuspended ? 'Yes' : 'No'} />
|
|
@@ -60,6 +64,24 @@ export function CNPGScheduledBackupRenderer({ data, onNavigate }: CNPGScheduledB
|
|
|
60
64
|
return clusterName
|
|
61
65
|
})()} />
|
|
62
66
|
<Property label="Method" value={getCNPGScheduledBackupMethod(data)} />
|
|
67
|
+
{/* Under the plugin method the destination lives on an ObjectStore in
|
|
68
|
+
another API group, so naming it is the only route from here to
|
|
69
|
+
where these backups will actually land. */}
|
|
70
|
+
{schedulePlugin && <Property label="Plugin" value={schedulePlugin.name} />}
|
|
71
|
+
{schedulePlugin?.parameters?.barmanObjectName && (
|
|
72
|
+
<Property
|
|
73
|
+
label="Object Store"
|
|
74
|
+
value={
|
|
75
|
+
<ResourceLink
|
|
76
|
+
name={schedulePlugin.parameters.barmanObjectName}
|
|
77
|
+
kind="objectstores"
|
|
78
|
+
group={CNPG_BARMAN_OBJECTSTORE_GROUP}
|
|
79
|
+
namespace={data.metadata?.namespace || ''}
|
|
80
|
+
onNavigate={onNavigate}
|
|
81
|
+
/>
|
|
82
|
+
}
|
|
83
|
+
/>
|
|
84
|
+
)}
|
|
63
85
|
<Property label="Owner Reference" value={getCNPGScheduledBackupOwnerRef(data)} />
|
|
64
86
|
</PropertyList>
|
|
65
87
|
</Section>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { formatAge } from '../resource-utils'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A recovery point, stated absolutely with the interval beside it.
|
|
5
|
+
*
|
|
6
|
+
* "1d ago" is the wrong unit for this question on its own. An RPO conversation
|
|
7
|
+
* needs a timestamp — and "Last Successful Backup 1d ago" next to "Last Failed
|
|
8
|
+
* Attempt 1d ago" is the same string for events a week apart in the failure
|
|
9
|
+
* history, which is precisely when you are reading this screen. Same treatment
|
|
10
|
+
* the certificate rows already use.
|
|
11
|
+
*
|
|
12
|
+
* Shared by the Cluster and ObjectStore pages: the same recovery point is
|
|
13
|
+
* reachable from both depending on whether backups run through the plugin, and
|
|
14
|
+
* the two must not describe it differently.
|
|
15
|
+
*/
|
|
16
|
+
export function RecoveryTime({ at }: { at: string }) {
|
|
17
|
+
const d = new Date(at)
|
|
18
|
+
if (Number.isNaN(d.getTime())) return <>{at}</>
|
|
19
|
+
return (
|
|
20
|
+
<span className="inline-flex items-baseline gap-2 min-w-0">
|
|
21
|
+
<span>{d.toISOString().replace('T', ' ').replace(/\.\d+Z$/, ' UTC')}</span>
|
|
22
|
+
{/* One interpolation, so the age and its unit stay a single text node
|
|
23
|
+
rather than being split by a renderer comment marker. */}
|
|
24
|
+
<span className="text-xs text-theme-text-tertiary shrink-0">{`${formatAge(at)} ago`}</span>
|
|
25
|
+
</span>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Network } from 'lucide-react'
|
|
2
|
+
import { Badge } from '../../ui/Badge'
|
|
3
|
+
import { ResourceLink, Section } from '../../ui/drawer-components'
|
|
4
|
+
|
|
5
|
+
interface CalicoHostEndpointRendererProps {
|
|
6
|
+
data: any
|
|
7
|
+
onNavigate?: (ref: { kind: string; namespace: string; name: string }) => void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function CalicoHostEndpointRenderer({ data, onNavigate }: CalicoHostEndpointRendererProps) {
|
|
11
|
+
const spec = data?.spec ?? {}
|
|
12
|
+
const expectedIPs: string[] = spec.expectedIPs ?? []
|
|
13
|
+
const profiles: string[] = spec.profiles ?? []
|
|
14
|
+
const ports: Array<{ name?: string; port?: number; protocol?: string }> = spec.ports ?? []
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<Section title="Host Endpoint" icon={Network}>
|
|
18
|
+
<div className="card-inner text-sm space-y-3">
|
|
19
|
+
<div className="flex items-start justify-between gap-3">
|
|
20
|
+
<div className="min-w-0">
|
|
21
|
+
<div className="text-xs text-theme-text-tertiary mb-0.5">Node</div>
|
|
22
|
+
<div className="font-medium text-theme-text-primary break-all">
|
|
23
|
+
{spec.node ? <ResourceLink name={spec.node} kind="nodes" namespace="" onNavigate={onNavigate} /> : 'Not specified'}
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
{spec.interfaceName && (
|
|
27
|
+
<div className="shrink-0 text-right">
|
|
28
|
+
<div className="text-xs text-theme-text-tertiary mb-0.5">Interface Name</div>
|
|
29
|
+
<Badge tone="structural" size="sm" className="font-mono">{spec.interfaceName}</Badge>
|
|
30
|
+
</div>
|
|
31
|
+
)}
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
{expectedIPs.length > 0 && (
|
|
35
|
+
<div className="border-t border-theme-border-subtle pt-3">
|
|
36
|
+
<div className="text-xs text-theme-text-tertiary mb-1.5">Expected IPs</div>
|
|
37
|
+
<div className="flex flex-wrap gap-1">
|
|
38
|
+
{expectedIPs.map((ip) => (
|
|
39
|
+
<Badge key={ip} tone="structural" size="sm" className="font-mono">{ip}</Badge>
|
|
40
|
+
))}
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
)}
|
|
44
|
+
|
|
45
|
+
{profiles.length > 0 && (
|
|
46
|
+
<div className="border-t border-theme-border-subtle pt-3">
|
|
47
|
+
<div className="text-xs text-theme-text-tertiary mb-1.5">Profiles</div>
|
|
48
|
+
<div className="flex flex-wrap gap-1">
|
|
49
|
+
{profiles.map((profile) => (
|
|
50
|
+
<Badge key={profile} tone="structural" size="sm">{profile}</Badge>
|
|
51
|
+
))}
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
)}
|
|
55
|
+
|
|
56
|
+
{ports.length > 0 && (
|
|
57
|
+
<div className="border-t border-theme-border-subtle pt-3">
|
|
58
|
+
<div className="text-xs text-theme-text-tertiary mb-1.5">Named Ports</div>
|
|
59
|
+
<div className="flex flex-wrap gap-1">
|
|
60
|
+
{ports.map((port, index) => (
|
|
61
|
+
<Badge key={`${port.name}-${port.protocol}-${port.port}-${index}`} tone="structural" size="sm" className="font-mono">
|
|
62
|
+
{`${port.name ? `${port.name}: ` : ''}${port.protocol}/${port.port}`}
|
|
63
|
+
</Badge>
|
|
64
|
+
))}
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
)}
|
|
68
|
+
</div>
|
|
69
|
+
</Section>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Network } from 'lucide-react'
|
|
2
|
+
import { Property, PropertyList, Section } from '../../ui/drawer-components'
|
|
3
|
+
import {
|
|
4
|
+
getCalicoIPPoolAllowedUses,
|
|
5
|
+
getCalicoIPPoolBlockSize,
|
|
6
|
+
} from '../resource-utils-calico'
|
|
7
|
+
|
|
8
|
+
interface CalicoIPPoolRendererProps {
|
|
9
|
+
data: any
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function CalicoIPPoolRenderer({ data }: CalicoIPPoolRendererProps) {
|
|
13
|
+
const spec = data?.spec ?? {}
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<Section title="IP Pool" icon={Network}>
|
|
17
|
+
<PropertyList>
|
|
18
|
+
<Property label="Allowed Uses" value={getCalicoIPPoolAllowedUses(data)} />
|
|
19
|
+
<Property label="Assignment Mode" value={spec.assignmentMode ?? 'Automatic'} />
|
|
20
|
+
<Property label="Block Size" value={getCalicoIPPoolBlockSize(data)} />
|
|
21
|
+
<Property label="CIDR" value={spec.cidr} />
|
|
22
|
+
<Property label="IP-in-IP Mode" value={spec.ipipMode ?? 'Never'} />
|
|
23
|
+
<Property label="VXLAN Mode" value={spec.vxlanMode ?? 'Never'} />
|
|
24
|
+
<Property label="NAT Outgoing" value={spec.natOutgoing ? 'Yes' : 'No'} />
|
|
25
|
+
<Property label="Disabled" value={spec.disabled ? 'Yes' : 'No'} />
|
|
26
|
+
<Property label="BGP Export Disabled" value={spec.disableBGPExport ? 'Yes' : 'No'} />
|
|
27
|
+
<Property label="Node Selector" value={spec.nodeSelector ?? 'all()'} />
|
|
28
|
+
<Property label="Namespace Selector" value={spec.namespaceSelector} />
|
|
29
|
+
</PropertyList>
|
|
30
|
+
</Section>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { renderToString } from 'react-dom/server'
|
|
3
|
+
import { CalicoNetworkPolicyDiagram } from './CalicoNetworkPolicyDiagram'
|
|
4
|
+
|
|
5
|
+
describe('CalicoNetworkPolicyDiagram', () => {
|
|
6
|
+
it('shows ordered actions and Calico match fields in both directions', () => {
|
|
7
|
+
const html = renderToString(
|
|
8
|
+
<CalicoNetworkPolicyDiagram
|
|
9
|
+
spec={{
|
|
10
|
+
selector: "app == 'api'",
|
|
11
|
+
namespaceSelector: "project == 'prod'",
|
|
12
|
+
serviceAccountSelector: "role == 'api'",
|
|
13
|
+
types: ['Ingress', 'Egress'],
|
|
14
|
+
ingress: [
|
|
15
|
+
{
|
|
16
|
+
action: 'Allow',
|
|
17
|
+
protocol: 'TCP',
|
|
18
|
+
source: {
|
|
19
|
+
selector: "role == 'frontend'",
|
|
20
|
+
nets: ['10.0.0.0/8'],
|
|
21
|
+
serviceAccounts: { names: ['frontend'] },
|
|
22
|
+
},
|
|
23
|
+
destination: { ports: [80] },
|
|
24
|
+
},
|
|
25
|
+
{ action: 'Deny', source: { notSelector: "app == 'debug'" } },
|
|
26
|
+
],
|
|
27
|
+
egress: [
|
|
28
|
+
{
|
|
29
|
+
action: 'Log',
|
|
30
|
+
protocol: 'UDP',
|
|
31
|
+
source: { serviceAccounts: { selector: "role == 'api'" } },
|
|
32
|
+
destination: {
|
|
33
|
+
selector: "app == 'db'",
|
|
34
|
+
nets: ['192.168.0.0/16'],
|
|
35
|
+
ports: [{ port: 5432, endPort: 5433 }],
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{ action: 'Pass', destination: { notNets: ['0.0.0.0/0'] } },
|
|
39
|
+
],
|
|
40
|
+
}}
|
|
41
|
+
/>,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
for (const value of [
|
|
45
|
+
'app == 'api'',
|
|
46
|
+
'role == 'api'',
|
|
47
|
+
'Ingress',
|
|
48
|
+
'Egress',
|
|
49
|
+
'Deny',
|
|
50
|
+
'Log',
|
|
51
|
+
'Pass',
|
|
52
|
+
'TCP',
|
|
53
|
+
'UDP',
|
|
54
|
+
'role == 'frontend'',
|
|
55
|
+
'10.0.0.0/8',
|
|
56
|
+
'frontend',
|
|
57
|
+
'TCP/80',
|
|
58
|
+
'app == 'db'',
|
|
59
|
+
'192.168.0.0/16',
|
|
60
|
+
'UDP/5432-5433',
|
|
61
|
+
'Not Nets',
|
|
62
|
+
'0.0.0.0/0',
|
|
63
|
+
]) {
|
|
64
|
+
expect(html).toContain(value)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
expect(html).not.toContain('Policy target')
|
|
68
|
+
expect(html).not.toContain('Source')
|
|
69
|
+
expect(html).not.toContain('Destination')
|
|
70
|
+
expect(html).not.toContain('Rule 1')
|
|
71
|
+
expect(html).toContain('Allow')
|
|
72
|
+
expect(html).not.toMatch(/badge-sm[^>]*>TCP<\/span>/)
|
|
73
|
+
expect(html).not.toMatch(/badge-sm[^>]*>UDP<\/span>/)
|
|
74
|
+
expect(html).toContain('text-warning-text')
|
|
75
|
+
expect(html).toContain('line-through')
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('keeps port constraints readable and distinguishes source exclusions', () => {
|
|
79
|
+
const html = renderToString(
|
|
80
|
+
<CalicoNetworkPolicyDiagram
|
|
81
|
+
spec={{
|
|
82
|
+
types: ['Egress'],
|
|
83
|
+
egress: [
|
|
84
|
+
{
|
|
85
|
+
protocol: 'UDP',
|
|
86
|
+
source: {
|
|
87
|
+
ports: [8080],
|
|
88
|
+
notPorts: [{ protocol: 'UDP', port: 5353 }],
|
|
89
|
+
},
|
|
90
|
+
destination: {
|
|
91
|
+
ports: [
|
|
92
|
+
{ protocol: 'TCP', port: 8080 },
|
|
93
|
+
{ protocol: 'TCP', port: 8443 },
|
|
94
|
+
],
|
|
95
|
+
notPorts: [443],
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
}}
|
|
100
|
+
/>,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
for (const value of [
|
|
104
|
+
'src:UDP/8080',
|
|
105
|
+
'src:!UDP/5353',
|
|
106
|
+
'TCP/8080',
|
|
107
|
+
'TCP/8443',
|
|
108
|
+
'!UDP/443',
|
|
109
|
+
]) {
|
|
110
|
+
expect(html).toContain(value)
|
|
111
|
+
}
|
|
112
|
+
expect(html).not.toContain('dst:')
|
|
113
|
+
expect(html).not.toContain('max-w-14')
|
|
114
|
+
expect(html).not.toContain('truncate font-mono text-[8px]')
|
|
115
|
+
expect(html).not.toMatch(/badge-sm[^>]*>UDP<\/span>/)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('renders protocol-only rules as wildcard arrow labels', () => {
|
|
119
|
+
const html = renderToString(
|
|
120
|
+
<CalicoNetworkPolicyDiagram
|
|
121
|
+
spec={{
|
|
122
|
+
types: ['Ingress'],
|
|
123
|
+
ingress: [
|
|
124
|
+
{
|
|
125
|
+
action: 'Deny',
|
|
126
|
+
protocol: 'TCP',
|
|
127
|
+
notProtocol: 'UDP',
|
|
128
|
+
source: { selector: "app == 'client'" },
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
}}
|
|
132
|
+
/>,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
expect(html).toContain('TCP/*')
|
|
136
|
+
expect(html).not.toMatch(/badge-sm[^>]*>TCP<\/span>/)
|
|
137
|
+
expect(html).toContain('not')
|
|
138
|
+
expect(html).toContain('UDP')
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('uses native peer colors with stable match priority', () => {
|
|
142
|
+
const renderDestination = (destination: any) =>
|
|
143
|
+
renderToString(
|
|
144
|
+
<CalicoNetworkPolicyDiagram
|
|
145
|
+
spec={{
|
|
146
|
+
selector: "app == 'target'",
|
|
147
|
+
types: ['Egress'],
|
|
148
|
+
egress: [{ destination }],
|
|
149
|
+
}}
|
|
150
|
+
/>,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
const combined = renderDestination({
|
|
154
|
+
selector: "app == 'workload'",
|
|
155
|
+
namespaceSelector: "team == 'prod'",
|
|
156
|
+
nets: ['10.0.0.0/8'],
|
|
157
|
+
})
|
|
158
|
+
const namespace = renderDestination({
|
|
159
|
+
namespaceSelector: "team == 'prod'",
|
|
160
|
+
})
|
|
161
|
+
const network = renderDestination({ nets: ['10.0.0.0/8'] })
|
|
162
|
+
const serviceAccount = renderDestination({
|
|
163
|
+
serviceAccountSelector: "role == 'api'",
|
|
164
|
+
})
|
|
165
|
+
const unknown = renderDestination({ unsupportedMatch: 'value' })
|
|
166
|
+
|
|
167
|
+
expect(combined).toContain('border-emerald-500/30 bg-emerald-500/8')
|
|
168
|
+
expect(combined).not.toContain('border-sky-500/30 bg-sky-500/8')
|
|
169
|
+
expect(combined).not.toContain('border-amber-500/30 bg-amber-500/8')
|
|
170
|
+
expect(namespace).toContain('border-sky-500/30 bg-sky-500/8')
|
|
171
|
+
expect(network).toContain('border-amber-500/30 bg-amber-500/8')
|
|
172
|
+
expect(serviceAccount).toContain('border-emerald-500/30 bg-emerald-500/8')
|
|
173
|
+
expect(unknown).toContain('border-theme-border bg-theme-elevated/50')
|
|
174
|
+
expect(combined).toContain('border-indigo-500/30 bg-indigo-500/8')
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
it('does not imply deny-all when a policy has no explicit rules', () => {
|
|
178
|
+
const html = renderToString(
|
|
179
|
+
<CalicoNetworkPolicyDiagram spec={{ types: ['Ingress'], ingress: [] }} />,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
expect(html).toContain('No explicit')
|
|
183
|
+
expect(html).toContain('ingress')
|
|
184
|
+
expect(html).not.toContain('Deny all')
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('renders staged rules as non-enforced dashed paths', () => {
|
|
188
|
+
const html = renderToString(
|
|
189
|
+
<CalicoNetworkPolicyDiagram
|
|
190
|
+
staged
|
|
191
|
+
spec={{
|
|
192
|
+
selector: "app == 'api'",
|
|
193
|
+
types: ['Ingress'],
|
|
194
|
+
ingress: [{ action: 'Allow', source: {} }],
|
|
195
|
+
}}
|
|
196
|
+
/>,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
expect(html).toContain('Staged preview')
|
|
200
|
+
expect(html).toContain('not enforced')
|
|
201
|
+
expect(html).toContain('stroke-dasharray="4 3"')
|
|
202
|
+
expect(html).toContain('Allow')
|
|
203
|
+
expect(html).not.toContain('Rule 1')
|
|
204
|
+
})
|
|
205
|
+
})
|