Skip to content

Commit 38bde21

Browse files
Add Kyverno policy and audit results to the discovered policies table (stolostron#4016)
Ref: https://issues.redhat.com/browse/ACM-15244 Signed-off-by: yiraeChristineKim <yikim@redhat.com>
1 parent 4db4057 commit 38bde21

14 files changed

+5324
-33
lines changed

frontend/public/locales/en/translation.json

+1
Original file line numberDiff line numberDiff line change
@@ -3178,6 +3178,7 @@
31783178
"View logs in Search details": "View logs in Search details",
31793179
"View MultiClusterHubs": "View MultiClusterHubs",
31803180
"View Pod YAML and Logs": "View Pod YAML and Logs",
3181+
"View policy report": "View policy report",
31813182
"View Red Hat Insights": "View Red Hat Insights",
31823183
"View related resources": "View related resources",
31833184
"View resource YAML": "View resource YAML",

frontend/src/logos/kyverno.svg

+4,515
Loading

frontend/src/routes/Governance/common/util.test.tsx

+56-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
/* Copyright Contributors to the Open Cluster Management project */
22
'use strict'
33

4-
import { hasInformOnlyPolicies, getPolicyRemediation, resolveExternalStatus, parseStringMap } from './util'
4+
import {
5+
hasInformOnlyPolicies,
6+
getPolicyRemediation,
7+
resolveExternalStatus,
8+
parseStringMap,
9+
collectKinds,
10+
} from './util'
511
import { PolicyTableItem } from '../policies/Policies'
612
import { Policy, PolicyTemplate, REMEDIATION_ACTION } from '../../../resources'
713
import { cloneDeep } from 'lodash'
@@ -901,3 +907,52 @@ describe('Test parseStringValue', () => {
901907
)
902908
})
903909
})
910+
911+
describe('Test collectKinds', () => {
912+
const mock = {
913+
rules: [
914+
{
915+
name: 'require-team-label',
916+
match: {
917+
any: [
918+
{
919+
resources: {
920+
kinds: ['Pod', 'Certificate'],
921+
},
922+
},
923+
],
924+
},
925+
},
926+
{
927+
name: 'require-team-label',
928+
match: {
929+
all: [
930+
{
931+
resources: {
932+
kinds: ['ConfigurationPolicy'],
933+
},
934+
},
935+
{
936+
resources: {
937+
kinds: ['ConfigMap'],
938+
},
939+
},
940+
],
941+
},
942+
},
943+
{
944+
name: 'require-team-label',
945+
match: {
946+
resources: {
947+
resources: {
948+
kinds: ['ConfigurationPolicy'],
949+
},
950+
},
951+
},
952+
},
953+
],
954+
}
955+
test('findKey should correctly find kinds values in Kyverno', () => {
956+
expect(collectKinds(mock)).toMatchObject(['Pod', 'Certificate', 'ConfigurationPolicy', 'ConfigMap'])
957+
})
958+
})

frontend/src/routes/Governance/common/util.tsx

+25
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ import { DiscoveredPolicyItem } from '../discovered/useFetchPolicies'
3131
import GatekeeperSvg from '../../../logos/gatekeeper.svg'
3232
import OcmSvg from '../../../logos/ocm.svg'
3333
import Kubernetes from '../../../logos/kubernetes.svg'
34+
import KyvernoSvg from '../../../logos/kyverno.svg'
35+
import { uniq } from 'lodash'
3436
export interface PolicyCompliance {
3537
policyName: string
3638
policyNamespace: string
@@ -714,6 +716,8 @@ export function getEngineString(apiGroup: string): string {
714716
return 'Gatekeeper'
715717
case 'admissionregistration.k8s.io':
716718
return 'Kubernetes'
719+
case 'kyverno.io':
720+
return 'Kyverno'
717721
default:
718722
return 'Unknown'
719723
}
@@ -733,6 +737,9 @@ export function getEngineWithSvg(apiGroup: string): JSX.Element {
733737
case 'Kubernetes':
734738
logo = <Kubernetes />
735739
break
740+
case 'Kyverno':
741+
logo = <KyvernoSvg />
742+
break
736743
default:
737744
return <>Unknown</>
738745
}
@@ -772,3 +779,21 @@ export const parseDiscoveredPolicies = (data: any): any => {
772779
return v
773780
})
774781
}
782+
783+
// Used for collecting all kinds in Kyverno Policy
784+
export const collectKinds = (obj: object): string[] => {
785+
let collected: string[] = []
786+
const fnd = (obj: object) => {
787+
for (const [k, v] of Object.entries(obj)) {
788+
if (k === 'kinds') {
789+
collected = collected.concat(v)
790+
}
791+
if (typeof v === 'object') {
792+
fnd(v)
793+
}
794+
}
795+
}
796+
797+
fnd(obj)
798+
return uniq(collected)
799+
}

frontend/src/routes/Governance/discovered/ByCluster/DiscoveredByCluster.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
getSeverityFilter,
1212
getSourceFilterOptions,
1313
DiscoveredViolationsCard,
14-
getConstraintCompliance,
14+
getTotalViolationsCompliance,
1515
policyViolationSummary,
1616
} from './common'
1717
import { useMemo } from 'react'
@@ -127,7 +127,7 @@ export default function DiscoveredByCluster({
127127
let compliant: string
128128

129129
if (item.apigroup === 'constraints.gatekeeper.sh') {
130-
compliant = getConstraintCompliance(item?.totalViolations)
130+
compliant = getTotalViolationsCompliance(item?.totalViolations)
131131
} else {
132132
compliant = item?.compliant?.toLowerCase() ?? ''
133133
}

frontend/src/routes/Governance/discovered/ByCluster/common.test.tsx

+11-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
/* Copyright Contributors to the Open Cluster Management project */
22
import { render } from '@testing-library/react'
33
import { DiscoveredPolicyItem } from '../useFetchPolicies'
4-
import { convertYesNoCell, getConstraintCompliance, DiscoveredViolationsCard, policyViolationSummary } from './common'
4+
import {
5+
convertYesNoCell,
6+
getTotalViolationsCompliance,
7+
DiscoveredViolationsCard,
8+
policyViolationSummary,
9+
} from './common'
510
import { waitForText } from '../../../../lib/test-util'
611
import i18next from 'i18next'
712
import { MemoryRouter } from 'react-router-dom-v5-compat'
@@ -150,10 +155,10 @@ describe('ByCluster common component test', () => {
150155
})
151156
})
152157

153-
describe('getConstraintCompliance', () => {
154-
test('getConstraintCompliance should work properly', () => {
155-
expect(getConstraintCompliance(0)).toEqual('compliant')
156-
expect(getConstraintCompliance(1)).toEqual('noncompliant')
157-
expect(getConstraintCompliance(undefined)).toEqual('-')
158+
describe('getTotalViolationsCompliance', () => {
159+
test('getTotalViolationsCompliance should work properly', () => {
160+
expect(getTotalViolationsCompliance(0)).toEqual('compliant')
161+
expect(getTotalViolationsCompliance(1)).toEqual('noncompliant')
162+
expect(getTotalViolationsCompliance(undefined)).toEqual('-')
158163
})
159164
})

frontend/src/routes/Governance/discovered/ByCluster/common.tsx

+7-7
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ export const policyViolationSummary = (discoveredPolicyItems: DiscoveredPolicyIt
2323
let unknown = 0
2424
for (const policy of discoveredPolicyItems) {
2525
let compliance: string
26-
27-
if (policy.apigroup === 'constraints.gatekeeper.sh') {
28-
compliance = getConstraintCompliance(policy?.totalViolations)
26+
// Kyverno resources also use the totalViolations field
27+
if (['constraints.gatekeeper.sh', 'kyverno.io'].includes(policy.apigroup)) {
28+
compliance = getTotalViolationsCompliance(policy?.totalViolations)
2929
} else {
3030
compliance = policy?.compliant?.toLowerCase() ?? ''
3131
}
@@ -49,7 +49,7 @@ export const policyViolationSummary = (discoveredPolicyItems: DiscoveredPolicyIt
4949
return { noncompliant, compliant, pending, unknown }
5050
}
5151

52-
export const getConstraintCompliance = (totalViolations?: number): string => {
52+
export const getTotalViolationsCompliance = (totalViolations?: number): string => {
5353
totalViolations = totalViolations ?? -1
5454

5555
if (totalViolations === 0) {
@@ -135,8 +135,8 @@ export const byClusterCols = (
135135
cell: (item: DiscoveredPolicyItem) => {
136136
let compliant: string
137137

138-
if (item.apigroup === 'constraints.gatekeeper.sh') {
139-
compliant = getConstraintCompliance(item?.totalViolations)
138+
if (['constraints.gatekeeper.sh', 'kyverno.io'].includes(item.apigroup)) {
139+
compliant = getTotalViolationsCompliance(item?.totalViolations)
140140
} else {
141141
compliant = item?.compliant?.toLowerCase() ?? ''
142142
}
@@ -180,7 +180,7 @@ export const byClusterCols = (
180180
id: 'violations',
181181
exportContent: (item: DiscoveredPolicyItem) => {
182182
if (item.apigroup === 'constraints.gatekeeper.sh') {
183-
const compliant = getConstraintCompliance(item?.totalViolations)
183+
const compliant = getTotalViolationsCompliance(item?.totalViolations)
184184

185185
if (compliant === 'noncompliant') {
186186
return compliant + ' (' + item.totalViolations + ')'

0 commit comments

Comments
 (0)