@rancher/shell 3.0.12-rc.2 → 3.0.12-rc.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/apis/impl/apis.ts +6 -0
- package/apis/index.ts +26 -0
- package/apis/intf/resources-api/cluster-api.ts +18 -0
- package/apis/intf/resources-api/mgmt-api.ts +15 -0
- package/apis/intf/resources-api/resource-base.ts +107 -0
- package/apis/intf/resources-api/resource-constants.ts +147 -0
- package/apis/intf/resources-api/resources-api.ts +143 -0
- package/apis/intf/resources.ts +49 -0
- package/apis/intf/{modal.ts → shell-api/modal.ts} +21 -26
- package/apis/intf/shell-api/proxy.ts +216 -0
- package/apis/intf/{slide-in.ts → shell-api/slide-in.ts} +4 -3
- package/apis/intf/{system.ts → shell-api/system.ts} +4 -1
- package/apis/intf/shell.ts +12 -6
- package/apis/resources/__tests__/resources-api-class.test.ts +550 -0
- package/apis/resources/index.ts +22 -0
- package/apis/resources/resources-api-class.ts +187 -0
- package/apis/shell/__tests__/proxy.test.ts +369 -0
- package/apis/shell/index.ts +8 -1
- package/apis/shell/modal.ts +4 -1
- package/apis/shell/notifications.ts +9 -6
- package/apis/shell/proxy.ts +256 -0
- package/apis/shell/slide-in.ts +4 -1
- package/apis/vue-shim.d.ts +2 -1
- package/assets/data/aws-regions.json +4 -0
- package/assets/fonts/lato/LatoLatin-Black.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Black.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-BlackItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-BlackItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Bold.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Bold.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-BoldItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-BoldItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Heavy.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Heavy.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-HeavyItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-HeavyItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Italic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Italic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Light.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Light.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-LightItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-LightItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Medium.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Medium.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-MediumItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-MediumItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Regular.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Regular.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Semibold.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Semibold.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff2 +0 -0
- package/assets/styles/base/_variables.scss +2 -0
- package/assets/styles/fonts/_fontstack.scss +132 -8
- package/assets/translations/en-us.yaml +22 -5
- package/chart/monitoring/index.vue +10 -1
- package/components/ActionDropdownShell.vue +2 -1
- package/components/CruResourceFooter.vue +9 -5
- package/components/ExplorerProjectsNamespaces.vue +1 -1
- package/components/InstallHelmCharts.vue +2 -2
- package/components/LandingPagePreference.vue +14 -5
- package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +15 -1
- package/components/Resource/Detail/Metadata/index.vue +6 -0
- package/components/Resource/Detail/ResourcePopover/index.vue +12 -1
- package/components/Resource/Detail/SpacedRow.vue +3 -1
- package/components/Resource/Detail/TitleBar/index.vue +10 -11
- package/components/ResourceList/Masthead.vue +12 -8
- package/components/SelectIconGrid.vue +0 -10
- package/components/SingleClusterInfo.vue +1 -0
- package/components/SortableTable/__tests__/sorting.test.ts +126 -0
- package/components/SortableTable/index.vue +6 -9
- package/components/SortableTable/selection.js +23 -5
- package/components/SortableTable/sorting.js +6 -3
- package/components/Wizard.vue +14 -13
- package/components/fleet/FleetBundles.vue +100 -12
- package/components/fleet/FleetClusterTargets/index.vue +37 -15
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +149 -115
- package/components/fleet/__tests__/FleetClusters.test.ts +12 -12
- package/components/form/LabeledSelect.vue +20 -3
- package/components/form/NameNsDescription.vue +11 -0
- package/components/form/Security.vue +6 -2
- package/components/form/WorkloadPorts.vue +2 -7
- package/components/form/__tests__/Security.test.ts +76 -0
- package/components/formatter/Autoscaler.vue +4 -4
- package/components/formatter/ClusterKubeVersion.vue +27 -0
- package/components/formatter/ClusterLink.vue +1 -7
- package/components/formatter/ClusterProvider.vue +6 -10
- package/components/formatter/FleetSummaryGraph.vue +0 -3
- package/components/formatter/MachineSummaryGraph.vue +1 -1
- package/components/formatter/PodsUsage.vue +2 -2
- package/components/formatter/__tests__/Autoscaler.test.ts +19 -22
- package/components/formatter/__tests__/FleetSummaryGraph.test.ts +216 -0
- package/components/formatter/__tests__/PodsUsage.test.ts +6 -10
- package/components/nav/NamespaceFilter.vue +2 -2
- package/components/nav/TopLevelMenu.helper.ts +15 -3
- package/components/nav/TopLevelMenu.vue +16 -5
- package/components/nav/__tests__/TopLevelMenu.test.ts +145 -21
- package/components/templates/home.vue +18 -0
- package/components/templates/plain.vue +18 -0
- package/components/templates/standalone.vue +17 -0
- package/composables/useFormValidation.ts +93 -0
- package/composables/useVeeValidateField.test.ts +159 -0
- package/composables/useVeeValidateField.ts +67 -0
- package/config/pagination-table-headers.js +18 -1
- package/config/product/manager.js +82 -21
- package/config/router/routes.js +6 -0
- package/config/table-headers.js +20 -1
- package/config/types.js +2 -1
- package/core/__tests__/plugin-products.test.ts +904 -20
- package/core/plugin-products-base.ts +107 -7
- package/core/plugin-products.ts +4 -0
- package/core/plugin-types.ts +111 -1
- package/core/plugin.ts +15 -7
- package/core/productDebugger.js +9 -4
- package/core/types-provisioning.ts +43 -30
- package/core/types.ts +57 -20
- package/detail/__tests__/pod.test.ts +41 -0
- package/detail/harvesterhci.io.management.cluster.vue +6 -2
- package/detail/pod.vue +1 -1
- package/detail/provisioning.cattle.io.cluster.vue +4 -10
- package/edit/auth/__tests__/azuread.test.ts +217 -34
- package/edit/auth/azuread.vue +122 -14
- package/edit/auth/oidc.vue +2 -2
- package/edit/networking.k8s.io.ingress/DefaultBackend.vue +13 -4
- package/edit/networking.k8s.io.ingress/RulePath.vue +8 -4
- package/edit/networking.k8s.io.ingress/index.vue +75 -20
- package/edit/provisioning.cattle.io.cluster/__tests__/MachinePool.test.ts +104 -0
- package/edit/provisioning.cattle.io.cluster/index.vue +11 -7
- package/edit/provisioning.cattle.io.cluster/rke2.vue +8 -4
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
- package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +37 -4
- package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +132 -7
- package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +2 -1
- package/edit/secret/__tests__/ssh.test.ts +5 -6
- package/edit/secret/basic.vue +31 -0
- package/edit/secret/index.vue +68 -17
- package/edit/secret/registry.vue +38 -0
- package/edit/secret/ssh.vue +29 -0
- package/edit/secret/tls.vue +30 -0
- package/edit/service.vue +4 -4
- package/edit/workload/Upgrading.vue +3 -3
- package/edit/workload/__tests__/Upgrading.test.ts +6 -9
- package/edit/workload/mixins/workload.js +2 -1
- package/list/fleet.cattle.io.bundle.vue +7 -104
- package/list/fleet.cattle.io.clusterregistrationtoken.vue +20 -0
- package/list/provisioning.cattle.io.cluster.vue +262 -180
- package/list/utils/management.cattle.io.cluster.utils.ts +128 -0
- package/mixins/__tests__/chart.test.ts +112 -0
- package/mixins/brand.js +2 -1
- package/mixins/chart.js +12 -8
- package/mixins/resource-fetch-api-pagination.js +41 -5
- package/models/__tests__/ext.cattle.io.kubeconfig.test.ts +67 -67
- package/models/__tests__/management.cattle.io.cluster.test.ts +1 -1
- package/models/__tests__/management.cattle.io.node.ts +6 -5
- package/models/__tests__/management.cattle.io.nodepool.ts +5 -4
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +32 -11
- package/models/base-cluster.x-k8s.io.js +26 -0
- package/models/cluster.js +1 -1
- package/models/cluster.x-k8s.io.machine.js +4 -22
- package/models/cluster.x-k8s.io.machinedeployment.js +2 -20
- package/models/cluster.x-k8s.io.machineset.js +2 -20
- package/models/compliance.cattle.io.clusterscan.js +130 -2
- package/models/ext.cattle.io.kubeconfig.ts +4 -7
- package/models/fleet-application.js +3 -1
- package/models/management.cattle.io.cluster.js +417 -40
- package/models/management.cattle.io.node.js +6 -4
- package/models/management.cattle.io.nodepool.js +1 -1
- package/models/networking.k8s.io.ingress.js +12 -4
- package/models/provisioning.cattle.io.cluster.js +47 -330
- package/models/rke.cattle.io.etcdsnapshot.js +1 -2
- package/package.json +11 -29
- package/pages/__tests__/readme.test.ts +49 -0
- package/pages/auth/setup.vue +2 -3
- package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +76 -0
- package/pages/c/_cluster/apps/charts/chart.vue +60 -8
- package/pages/c/_cluster/apps/charts/install.vue +10 -7
- package/pages/c/_cluster/explorer/__tests__/index.test.ts +23 -25
- package/pages/c/_cluster/explorer/index.vue +5 -49
- package/pages/c/_cluster/istio/__tests__/istio.index.test.ts +194 -0
- package/pages/c/_cluster/istio/index.vue +21 -6
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -0
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +719 -2
- package/pages/c/_cluster/uiplugins/index.vue +203 -197
- package/pages/diagnostic.vue +13 -17
- package/pages/fail-whale.vue +18 -0
- package/pages/home.vue +77 -260
- package/pages/readme.vue +88 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +88 -0
- package/plugins/dashboard-store/actions.js +40 -18
- package/plugins/dashboard-store/resource-class.js +5 -2
- package/plugins/steve/__tests__/subscribe.spec.ts +6 -3
- package/plugins/steve/steve-pagination-utils.ts +11 -3
- package/plugins/steve/subscribe.js +35 -5
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +10 -4
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +7 -52
- package/rancher-components/RcButton/RcButton.test.ts +37 -1
- package/rancher-components/RcButton/RcButton.vue +38 -8
- package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -8
- package/store/__tests__/catalog.test.ts +115 -1
- package/store/__tests__/type-map.test.ts +556 -1
- package/store/action-menu.js +8 -3
- package/store/auth.js +1 -1
- package/store/aws.js +27 -16
- package/store/catalog.js +27 -3
- package/store/digitalocean.js +20 -38
- package/store/index.js +2 -0
- package/store/linode.js +25 -40
- package/store/pnap.js +1 -0
- package/store/type-map.js +111 -29
- package/tsconfig.paths.json +8 -8
- package/types/kube/kube-api.ts +14 -1
- package/types/rancher/steve.api.ts +12 -12
- package/types/resources/settings.d.ts +2 -1
- package/types/shell/index.d.ts +102 -2
- package/types/store/dashboard-store.types.ts +108 -11
- package/types/store/pagination.types.ts +6 -3
- package/utils/__tests__/alertmanagerconfig.test.ts +117 -0
- package/utils/__tests__/async.test.ts +87 -0
- package/utils/__tests__/aws.test.ts +140 -0
- package/utils/__tests__/banners.test.ts +176 -0
- package/utils/__tests__/chart.test.ts +64 -1
- package/utils/__tests__/color.test.ts +226 -0
- package/utils/__tests__/duration.test.ts +140 -0
- package/utils/__tests__/fleet.test.ts +340 -0
- package/utils/__tests__/ingress.test.ts +553 -0
- package/utils/__tests__/kube.test.ts +68 -0
- package/utils/__tests__/namespace-filter.test.ts +109 -0
- package/utils/__tests__/pagination-utils.test.ts +361 -0
- package/utils/__tests__/parse-externalid.test.ts +137 -0
- package/utils/__tests__/perf-setting.utils.test.ts +98 -0
- package/utils/__tests__/poller-sequential.test.ts +177 -0
- package/utils/__tests__/poller.test.ts +170 -0
- package/utils/__tests__/promise.test.ts +346 -0
- package/utils/__tests__/settings.test.ts +140 -0
- package/utils/__tests__/sort-utils.test.ts +301 -0
- package/utils/__tests__/string-utils.test.ts +798 -0
- package/utils/__tests__/string.test.ts +23 -1
- package/utils/__tests__/style.test.ts +154 -0
- package/utils/__tests__/svg-filter.test.ts +184 -0
- package/utils/__tests__/units.test.ts +417 -0
- package/utils/__tests__/versions.test.ts +128 -0
- package/utils/__tests__/xccdf.test.ts +391 -0
- package/utils/chart.js +36 -0
- package/utils/fleet.ts +13 -3
- package/utils/gatekeeper/__tests__/util.test.ts +174 -0
- package/utils/gc/__tests__/gc-interval.test.ts +119 -0
- package/utils/gc/__tests__/gc-root-store.test.ts +225 -0
- package/utils/gc/__tests__/gc-route-changed.test.ts +96 -0
- package/utils/gc/__tests__/gc.test.ts +487 -0
- package/utils/ingress.ts +9 -1
- package/utils/pagination-utils.ts +2 -1
- package/utils/string.js +25 -2
- package/utils/uiplugins.ts +5 -5
- package/utils/validators/__tests__/cluster-name.test.ts +110 -0
- package/utils/validators/__tests__/cron-schedule.test.ts +79 -0
- package/utils/validators/__tests__/index.test.ts +481 -0
- package/utils/validators/__tests__/kubernetes-name.test.ts +163 -0
- package/utils/validators/__tests__/misc-validators.test.ts +246 -0
- package/utils/validators/__tests__/pod-affinity.test.ts +382 -0
- package/utils/validators/__tests__/prometheusrule.test.ts +211 -0
- package/utils/validators/__tests__/role-template.test.ts +149 -0
- package/utils/validators/__tests__/service.test.ts +283 -0
- package/utils/validators/__tests__/setting.test.js +32 -0
- package/utils/validators/formRules/__tests__/index.test.ts +50 -0
- package/utils/validators/formRules/index.ts +5 -5
- package/utils/validators/machine-pool.ts +1 -1
- package/utils/validators/setting.js +18 -3
- package/utils/xccdf.ts +418 -0
- package/assets/fonts/lato/lato-v17-latin-700.woff +0 -0
- package/assets/fonts/lato/lato-v17-latin-700.woff2 +0 -0
- package/assets/fonts/lato/lato-v17-latin-regular.woff +0 -0
- package/assets/fonts/lato/lato-v17-latin-regular.woff2 +0 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import { generateXCCDF, generateXCCDFPerNode } from '@shell/utils/xccdf';
|
|
2
|
+
|
|
3
|
+
describe('xccdf util: generateXCCDF', () => {
|
|
4
|
+
const baseReport = {
|
|
5
|
+
version: '1.0',
|
|
6
|
+
total: 2,
|
|
7
|
+
pass: 1,
|
|
8
|
+
nodes: { master: ['node-a'], node: ['node-b'] },
|
|
9
|
+
results: [{
|
|
10
|
+
id: '5.1',
|
|
11
|
+
description: 'RBAC and Service Accounts',
|
|
12
|
+
checks: [{
|
|
13
|
+
id: '5.1.1',
|
|
14
|
+
description: 'Ensure that the cluster-admin role is only used where required',
|
|
15
|
+
audit: 'kubectl get clusterrolebindings',
|
|
16
|
+
remediation: 'Identify all clusterrolebindings to the cluster-admin role',
|
|
17
|
+
scored: true,
|
|
18
|
+
state: 'pass',
|
|
19
|
+
}, {
|
|
20
|
+
id: '5.1.2',
|
|
21
|
+
description: 'Minimize access to secrets',
|
|
22
|
+
audit: 'kubectl get roles',
|
|
23
|
+
remediation: 'Where possible, remove get, list and watch access to Secret objects',
|
|
24
|
+
scored: false,
|
|
25
|
+
state: 'fail',
|
|
26
|
+
}],
|
|
27
|
+
}],
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
it('produces an XML document with an XML header and a Benchmark root', () => {
|
|
31
|
+
const xml = generateXCCDF({ report: baseReport, benchmarkVersion: 'cis-1.7' });
|
|
32
|
+
|
|
33
|
+
expect(xml).toMatch(/^<\?xml version="1\.0" encoding="UTF-8"\?>/);
|
|
34
|
+
expect(xml).toContain('<Benchmark');
|
|
35
|
+
expect(xml).toContain('xmlns="http://checklists.nist.gov/xccdf/1.2"');
|
|
36
|
+
expect(xml).toContain('id="xccdf_compliance-operator_benchmark_kubernetes"');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('uses the metadata title when provided, otherwise falls back to a CIS title from benchmarkVersion', () => {
|
|
40
|
+
const fallback = generateXCCDF({ report: baseReport, benchmarkVersion: 'cis-1.7' });
|
|
41
|
+
|
|
42
|
+
expect(fallback).toContain('<title>Kubernetes CIS Benchmark cis-1.7</title>');
|
|
43
|
+
|
|
44
|
+
const withTitle = generateXCCDF({
|
|
45
|
+
report: baseReport,
|
|
46
|
+
benchmarkVersion: 'cis-1.7',
|
|
47
|
+
metadata: { title: 'Custom Benchmark Title' },
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
expect(withTitle).toContain('<title>Custom Benchmark Title</title>');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('emits one Group per result and one Rule per check', () => {
|
|
54
|
+
const xml = generateXCCDF({ report: baseReport, benchmarkVersion: 'cis-1.7' });
|
|
55
|
+
|
|
56
|
+
expect(xml).toContain('<Group id="5.1">');
|
|
57
|
+
expect(xml).toContain('id="xccdf_compliance-operator_rule_5.1.1"');
|
|
58
|
+
expect(xml).toContain('id="xccdf_compliance-operator_rule_5.1.2"');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('maps state values to XCCDF result strings', () => {
|
|
62
|
+
const report = {
|
|
63
|
+
...baseReport,
|
|
64
|
+
results: [{
|
|
65
|
+
id: '1',
|
|
66
|
+
description: 'g',
|
|
67
|
+
checks: [
|
|
68
|
+
{
|
|
69
|
+
id: 'a', description: 'a', state: 'pass' as const
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: 'b', description: 'b', state: 'fail' as const
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: 'c', description: 'c', state: 'skip' as const
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'd', description: 'd', state: 'warn' as const
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: 'e', description: 'e', state: 'notApplicable' as const
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
}],
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const xml = generateXCCDF({ report, benchmarkVersion: 'cis-1.7' });
|
|
88
|
+
|
|
89
|
+
expect(xml).toContain('<result>pass</result>');
|
|
90
|
+
expect(xml).toContain('<result>fail</result>');
|
|
91
|
+
expect(xml).toContain('<result>notselected</result>');
|
|
92
|
+
expect(xml).toContain('<result>informational</result>');
|
|
93
|
+
expect(xml).toContain('<result>notapplicable</result>');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('marks scored checks with severity=medium and weight=10; unscored as low and 0', () => {
|
|
97
|
+
const xml = generateXCCDF({ report: baseReport, benchmarkVersion: 'cis-1.7' });
|
|
98
|
+
|
|
99
|
+
expect(xml).toMatch(/id="xccdf_compliance-operator_rule_5\.1\.1"[^>]*severity="medium"/);
|
|
100
|
+
expect(xml).toMatch(/id="xccdf_compliance-operator_rule_5\.1\.1"[^>]*weight="10"/);
|
|
101
|
+
expect(xml).toMatch(/id="xccdf_compliance-operator_rule_5\.1\.2"[^>]*severity="low"/);
|
|
102
|
+
expect(xml).toMatch(/id="xccdf_compliance-operator_rule_5\.1\.2"[^>]*weight="0"/);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('computes score as pass/total*100, formatted to one decimal', () => {
|
|
106
|
+
const xml = generateXCCDF({ report: baseReport, benchmarkVersion: 'cis-1.7' });
|
|
107
|
+
|
|
108
|
+
expect(xml).toMatch(/<score[^>]*maximum="100\.0"[^>]*>50\.0<\/score>/);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('returns score 0.0 when total is 0', () => {
|
|
112
|
+
const xml = generateXCCDF({
|
|
113
|
+
report: {
|
|
114
|
+
...baseReport, total: 0, pass: 0, results: [],
|
|
115
|
+
},
|
|
116
|
+
benchmarkVersion: 'cis-1.7',
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
expect(xml).toMatch(/<score[^>]*>0\.0<\/score>/);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('uses report.nodes for <target> elements when no clusterName is set', () => {
|
|
123
|
+
const xml = generateXCCDF({ report: baseReport, benchmarkVersion: 'cis-1.7' });
|
|
124
|
+
|
|
125
|
+
expect(xml).toContain('<target>node-a</target>');
|
|
126
|
+
expect(xml).toContain('<target>node-b</target>');
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('clusterName argument overrides metadata.clusterName and node-derived targets', () => {
|
|
130
|
+
const xml = generateXCCDF({
|
|
131
|
+
report: baseReport,
|
|
132
|
+
benchmarkVersion: 'cis-1.7',
|
|
133
|
+
metadata: { clusterName: 'from-metadata' },
|
|
134
|
+
clusterName: 'from-arg',
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
expect(xml).toContain('<target>from-arg</target>');
|
|
138
|
+
expect(xml).not.toContain('<target>from-metadata</target>');
|
|
139
|
+
expect(xml).not.toContain('<target>node-a</target>');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('falls back to "not-applicable" for missing target addresses and target facts', () => {
|
|
143
|
+
const xml = generateXCCDF({ report: baseReport, benchmarkVersion: 'cis-1.7' });
|
|
144
|
+
|
|
145
|
+
expect(xml).toContain('<target-address>not-applicable</target-address>');
|
|
146
|
+
expect(xml).toContain('<fact name="not-applicable" type="string">not-applicable</fact>');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('emits a single placeholder Group when results is empty', () => {
|
|
150
|
+
const xml = generateXCCDF({
|
|
151
|
+
report: {
|
|
152
|
+
version: '1.0', total: 0, pass: 0, results: [],
|
|
153
|
+
},
|
|
154
|
+
benchmarkVersion: 'cis-1.7',
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
expect(xml).toContain('<Group id="not-applicable">');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('uses STIG metadata to override rule id, version, severity, fix, check system, and CCI idents', () => {
|
|
161
|
+
const xml = generateXCCDF({
|
|
162
|
+
report: {
|
|
163
|
+
...baseReport,
|
|
164
|
+
results: [{
|
|
165
|
+
id: 'g1',
|
|
166
|
+
description: 'g',
|
|
167
|
+
checks: [{
|
|
168
|
+
id: 'V-254553-TLS-apiserver', description: 'STIG check', scored: true, state: 'pass',
|
|
169
|
+
}],
|
|
170
|
+
}],
|
|
171
|
+
},
|
|
172
|
+
benchmarkVersion: 'rke2-stig-1.31',
|
|
173
|
+
stigChecks: {
|
|
174
|
+
'V-254553': {
|
|
175
|
+
ruleId: 'SV-254553r1016525_rule', version: 'SV-254553r1016525_rule', severity: 'high', fixId: 'F-254553', checkId: 'C-254553', cci: ['CCI-000366'],
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
expect(xml).toContain('idref="SV-254553r1016525_rule_TLS-apiserver"');
|
|
181
|
+
expect(xml).toContain('severity="high"');
|
|
182
|
+
expect(xml).toContain('<ident system="http://cyber.mil/cci">CCI-000366</ident>');
|
|
183
|
+
expect(xml).toContain('fixref="F-254553"');
|
|
184
|
+
expect(xml).toContain('<check system="C-254553">');
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('preserves the operator-style rule id when there is no STIG metadata', () => {
|
|
188
|
+
const xml = generateXCCDF({
|
|
189
|
+
report: baseReport,
|
|
190
|
+
benchmarkVersion: 'cis-1.7',
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
expect(xml).toContain('id="xccdf_compliance-operator_rule_5.1.1"');
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe('xccdf util: generateXCCDFPerNode', () => {
|
|
198
|
+
const multiNodeReport = {
|
|
199
|
+
version: '1.0',
|
|
200
|
+
total: 3,
|
|
201
|
+
pass: 1,
|
|
202
|
+
nodes: { master: ['m-1'], node: ['w-1', 'w-2'] },
|
|
203
|
+
results: [{
|
|
204
|
+
id: '1.1',
|
|
205
|
+
description: 'Master Node Configuration',
|
|
206
|
+
checks: [{
|
|
207
|
+
id: '1.1.1',
|
|
208
|
+
description: 'master check',
|
|
209
|
+
scored: true,
|
|
210
|
+
state: 'pass' as const,
|
|
211
|
+
}],
|
|
212
|
+
}, {
|
|
213
|
+
id: '4.1',
|
|
214
|
+
description: 'Worker Node Configuration',
|
|
215
|
+
checks: [{
|
|
216
|
+
id: '4.1.1',
|
|
217
|
+
description: 'mixed check',
|
|
218
|
+
scored: true,
|
|
219
|
+
state: 'mixed' as const,
|
|
220
|
+
nodes: ['w-2'],
|
|
221
|
+
}, {
|
|
222
|
+
id: '4.1.2',
|
|
223
|
+
description: 'failing check',
|
|
224
|
+
scored: false,
|
|
225
|
+
state: 'fail' as const,
|
|
226
|
+
}],
|
|
227
|
+
}],
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
it('emits a single <target> equal to the hostname', () => {
|
|
231
|
+
const xml = generateXCCDFPerNode({
|
|
232
|
+
report: multiNodeReport, benchmarkVersion: 'cis-1.7', hostname: 'w-1', role: 'node',
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
expect(xml).toContain('<target>w-1</target>');
|
|
236
|
+
expect(xml).not.toContain('<target>w-2</target>');
|
|
237
|
+
expect(xml).not.toContain('<target>m-1</target>');
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('assigns each per-node document a TestResult id suffixed with the hostname so co-loaded files do not collide', () => {
|
|
241
|
+
const a = generateXCCDFPerNode({
|
|
242
|
+
report: multiNodeReport, benchmarkVersion: 'cis-1.7', hostname: 'w-1', role: 'node',
|
|
243
|
+
});
|
|
244
|
+
const b = generateXCCDFPerNode({
|
|
245
|
+
report: multiNodeReport, benchmarkVersion: 'cis-1.7', hostname: 'w-2', role: 'node',
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
expect(a).toContain('<TestResult id="xccdf_compliance-operator_testresult_1_w-1"');
|
|
249
|
+
expect(b).toContain('<TestResult id="xccdf_compliance-operator_testresult_1_w-2"');
|
|
250
|
+
expect(a).not.toContain('id="xccdf_compliance-operator_testresult_1_w-2"');
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('emits every rule from the cluster report regardless of role', () => {
|
|
254
|
+
const workerXml = generateXCCDFPerNode({
|
|
255
|
+
report: multiNodeReport, benchmarkVersion: 'cis-1.7', hostname: 'w-1', role: 'node',
|
|
256
|
+
});
|
|
257
|
+
const masterXml = generateXCCDFPerNode({
|
|
258
|
+
report: multiNodeReport, benchmarkVersion: 'cis-1.7', hostname: 'm-1', role: 'master',
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
[workerXml, masterXml].forEach((xml) => {
|
|
262
|
+
expect(xml).toContain('xccdf_compliance-operator_rule_1.1.1');
|
|
263
|
+
expect(xml).toContain('xccdf_compliance-operator_rule_4.1.1');
|
|
264
|
+
expect(xml).toContain('xccdf_compliance-operator_rule_4.1.2');
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('maps mixed-state checks to fail for dissenting hosts and pass for the rest', () => {
|
|
269
|
+
const dissenter = generateXCCDFPerNode({
|
|
270
|
+
report: multiNodeReport, benchmarkVersion: 'cis-1.7', hostname: 'w-2', role: 'node',
|
|
271
|
+
});
|
|
272
|
+
const compliant = generateXCCDFPerNode({
|
|
273
|
+
report: multiNodeReport, benchmarkVersion: 'cis-1.7', hostname: 'w-1', role: 'node',
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
expect(dissenter).toMatch(/idref="xccdf_compliance-operator_rule_4\.1\.1"[\s\S]*?<result>fail<\/result>/);
|
|
277
|
+
expect(compliant).toMatch(/idref="xccdf_compliance-operator_rule_4\.1\.1"[\s\S]*?<result>pass<\/result>/);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('treats mixed-state checks with no dissent list as pass for all nodes', () => {
|
|
281
|
+
const report = {
|
|
282
|
+
...multiNodeReport,
|
|
283
|
+
results: [{
|
|
284
|
+
id: '4.1',
|
|
285
|
+
description: 'g',
|
|
286
|
+
checks: [{
|
|
287
|
+
id: '4.1.9', description: 'm', state: 'mixed' as const,
|
|
288
|
+
}],
|
|
289
|
+
}],
|
|
290
|
+
};
|
|
291
|
+
const xml = generateXCCDFPerNode({
|
|
292
|
+
report, benchmarkVersion: 'cis-1.7', hostname: 'w-1', role: 'node',
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
expect(xml).toMatch(/idref="xccdf_compliance-operator_rule_4\.1\.9"[\s\S]*?<result>pass<\/result>/);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it('recomputes pass count per node while preserving cluster total as the scoring denominator', () => {
|
|
299
|
+
const report = {
|
|
300
|
+
version: '1.0',
|
|
301
|
+
total: 2,
|
|
302
|
+
pass: 1,
|
|
303
|
+
nodes: { node: ['w-1', 'w-2'] },
|
|
304
|
+
results: [{
|
|
305
|
+
id: '1',
|
|
306
|
+
description: 'g',
|
|
307
|
+
checks: [
|
|
308
|
+
{
|
|
309
|
+
id: 'a', description: 'a', state: 'pass' as const
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
id: 'b', description: 'b', state: 'mixed' as const, nodes: ['w-2'],
|
|
313
|
+
},
|
|
314
|
+
],
|
|
315
|
+
}],
|
|
316
|
+
};
|
|
317
|
+
const compliant = generateXCCDFPerNode({
|
|
318
|
+
report, benchmarkVersion: 'cis-1.7', hostname: 'w-1', role: 'node',
|
|
319
|
+
});
|
|
320
|
+
const dissenter = generateXCCDFPerNode({
|
|
321
|
+
report, benchmarkVersion: 'cis-1.7', hostname: 'w-2', role: 'node',
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
expect(compliant).toMatch(/<score[^>]*>100\.0<\/score>/);
|
|
325
|
+
expect(dissenter).toMatch(/<score[^>]*>50\.0<\/score>/);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('preserves full rule metadata (title, fixtext, idents, check) from the cluster report', () => {
|
|
329
|
+
const report = {
|
|
330
|
+
version: '1.0',
|
|
331
|
+
total: 1,
|
|
332
|
+
pass: 1,
|
|
333
|
+
nodes: { node: ['w-1'] },
|
|
334
|
+
results: [{
|
|
335
|
+
id: 'V-254554',
|
|
336
|
+
description: 'controller manager group',
|
|
337
|
+
checks: [{
|
|
338
|
+
id: 'V-254554',
|
|
339
|
+
description: 'use-service-account-credentials',
|
|
340
|
+
audit: '/bin/ps -fC kube-controller-manager',
|
|
341
|
+
remediation: 'set use-service-account-credentials=true',
|
|
342
|
+
scored: true,
|
|
343
|
+
state: 'pass' as const,
|
|
344
|
+
}],
|
|
345
|
+
}],
|
|
346
|
+
};
|
|
347
|
+
const xml = generateXCCDFPerNode({
|
|
348
|
+
report, benchmarkVersion: 'rke2-stig-1.31-rgs', hostname: 'w-1', role: 'node',
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
expect(xml).toContain('<Group id="V-254554">');
|
|
352
|
+
expect(xml).toContain('<check-content>/bin/ps -fC kube-controller-manager</check-content>');
|
|
353
|
+
expect(xml).toContain('set use-service-account-credentials=true');
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it('passes through non-mixed states unchanged', () => {
|
|
357
|
+
const report = {
|
|
358
|
+
...multiNodeReport,
|
|
359
|
+
results: [{
|
|
360
|
+
id: '4.1',
|
|
361
|
+
description: 'g',
|
|
362
|
+
checks: [
|
|
363
|
+
{
|
|
364
|
+
id: 'a', description: 'a', state: 'pass' as const
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
id: 'b', description: 'b', state: 'fail' as const
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
id: 'c', description: 'c', state: 'skip' as const
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
id: 'd', description: 'd', state: 'warn' as const
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
id: 'e', description: 'e', state: 'notApplicable' as const
|
|
377
|
+
},
|
|
378
|
+
],
|
|
379
|
+
}],
|
|
380
|
+
};
|
|
381
|
+
const xml = generateXCCDFPerNode({
|
|
382
|
+
report, benchmarkVersion: 'cis-1.7', hostname: 'w-1', role: 'node',
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
expect(xml).toContain('<result>pass</result>');
|
|
386
|
+
expect(xml).toContain('<result>fail</result>');
|
|
387
|
+
expect(xml).toContain('<result>notselected</result>');
|
|
388
|
+
expect(xml).toContain('<result>informational</result>');
|
|
389
|
+
expect(xml).toContain('<result>notapplicable</result>');
|
|
390
|
+
});
|
|
391
|
+
});
|
package/utils/chart.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import semver from 'semver';
|
|
2
2
|
import { compare } from '@shell/utils/version';
|
|
3
3
|
import { compatibleVersionsFor } from '@shell/store/catalog';
|
|
4
|
+
import {
|
|
5
|
+
CHART, REPO, REPO_TYPE, VERSION, DEPRECATED
|
|
6
|
+
} from '@shell/config/query-params';
|
|
4
7
|
|
|
5
8
|
/**
|
|
6
9
|
* Compares two chart versions using SemVer logic, with special handling for Rancher's "up" build metadata.
|
|
@@ -80,3 +83,36 @@ export function getLatestCompatibleVersion(chart, workerOSs, showPrerelease) {
|
|
|
80
83
|
|
|
81
84
|
return (compatible.length ? compatible[0] : chart.versions[0]) || {};
|
|
82
85
|
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Builds the route URL for the standalone chart readme page.
|
|
89
|
+
*
|
|
90
|
+
* The helper maps chart context into query params so the readme page can fetch
|
|
91
|
+
* version info directly (instead of relying on local/session storage transfer).
|
|
92
|
+
*/
|
|
93
|
+
export function getStandaloneReadmeUrl(router, {
|
|
94
|
+
cluster,
|
|
95
|
+
repoType,
|
|
96
|
+
repoName,
|
|
97
|
+
chartName,
|
|
98
|
+
versionName,
|
|
99
|
+
deprecated,
|
|
100
|
+
showAppReadme = true,
|
|
101
|
+
hideReadmeFirstTitle = true,
|
|
102
|
+
} = {}) {
|
|
103
|
+
const { href } = router.resolve({
|
|
104
|
+
name: 'readme',
|
|
105
|
+
params: { cluster },
|
|
106
|
+
query: {
|
|
107
|
+
[REPO_TYPE]: repoType,
|
|
108
|
+
[REPO]: repoName,
|
|
109
|
+
[CHART]: chartName,
|
|
110
|
+
[VERSION]: versionName,
|
|
111
|
+
[DEPRECATED]: deprecated,
|
|
112
|
+
showAppReadme: String(showAppReadme),
|
|
113
|
+
hideReadmeFirstTitle: String(hideReadmeFirstTitle),
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
return href;
|
|
118
|
+
}
|
package/utils/fleet.ts
CHANGED
|
@@ -33,6 +33,13 @@ function conditionIsTrue(conditions: Condition[] | undefined, type: string): boo
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
class Application {
|
|
36
|
+
/**
|
|
37
|
+
* gitrepos/helmops are already restricted to clusters in their own namespace
|
|
38
|
+
*
|
|
39
|
+
* this empty selector means all applicable clusters will be selected
|
|
40
|
+
*/
|
|
41
|
+
includeAllWorkgroupRule = { clusterSelector: { matchExpressions: [] } }
|
|
42
|
+
|
|
36
43
|
excludeHarvesterRule = {
|
|
37
44
|
clusterSelector: {
|
|
38
45
|
matchExpressions: [{
|
|
@@ -45,7 +52,7 @@ class Application {
|
|
|
45
52
|
},
|
|
46
53
|
};
|
|
47
54
|
|
|
48
|
-
getTargetMode(targets: Target[], namespace: string): TargetMode {
|
|
55
|
+
getTargetMode(targets: Target[], namespace: string, areHarvesterHostsVisible: boolean): TargetMode {
|
|
49
56
|
if (namespace === 'fleet-local') {
|
|
50
57
|
return 'local';
|
|
51
58
|
}
|
|
@@ -83,8 +90,11 @@ class Application {
|
|
|
83
90
|
return target;
|
|
84
91
|
});
|
|
85
92
|
|
|
86
|
-
// Check if targets contains only harvester rule after name normalizing
|
|
87
|
-
|
|
93
|
+
// Check if targets contains only harvester rule or no rule at all after name normalizing
|
|
94
|
+
// That means the ALL option has been selected previously
|
|
95
|
+
// one case for feature harvester-baremetal-container-workload ON
|
|
96
|
+
// and another for feature harvester-baremetal-container-workload OFF
|
|
97
|
+
if (isEqual(normalized, [this.includeAllWorkgroupRule]) || isEqual(normalized, [this.excludeHarvesterRule])) {
|
|
88
98
|
mode = 'all';
|
|
89
99
|
}
|
|
90
100
|
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { findTemplateType, findAllConstraintTypes, findAllTemplates, findAllConstraints } from '../util';
|
|
2
|
+
import { GATEKEEPER, SCHEMA } from '@shell/config/types';
|
|
3
|
+
|
|
4
|
+
describe('gatekeeper/util.js', () => {
|
|
5
|
+
describe('findTemplateType', () => {
|
|
6
|
+
it('returns the schema id when a matching schema exists', () => {
|
|
7
|
+
const schemas = [
|
|
8
|
+
{
|
|
9
|
+
id: 'templates.gatekeeper.sh.constrainttemplate',
|
|
10
|
+
attributes: { group: 'templates.gatekeeper.sh', kind: 'ConstraintTemplate' },
|
|
11
|
+
},
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
expect(findTemplateType(schemas)).toStrictEqual('templates.gatekeeper.sh.constrainttemplate');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('returns undefined when no schemas match', () => {
|
|
18
|
+
const schemas = [
|
|
19
|
+
{
|
|
20
|
+
id: 'other.group.somekind',
|
|
21
|
+
attributes: { group: 'other.group', kind: 'SomeKind' },
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
expect(findTemplateType(schemas)).toBeUndefined();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('returns undefined for an empty schemas array', () => {
|
|
29
|
+
expect(findTemplateType([])).toBeUndefined();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('matches only on the correct group and kind together', () => {
|
|
33
|
+
const schemas = [
|
|
34
|
+
{
|
|
35
|
+
id: 'templates.gatekeeper.sh.other',
|
|
36
|
+
attributes: { group: 'templates.gatekeeper.sh', kind: 'Other' },
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: 'other.group.constrainttemplate',
|
|
40
|
+
attributes: { group: 'other.group', kind: 'ConstraintTemplate' },
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
expect(findTemplateType(schemas)).toBeUndefined();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('returns undefined when schema has no attributes', () => {
|
|
48
|
+
const schemas = [{ id: 'some.id' }];
|
|
49
|
+
|
|
50
|
+
expect(findTemplateType(schemas)).toBeUndefined();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('returns the first matching schema id when multiple matches exist', () => {
|
|
54
|
+
const schemas = [
|
|
55
|
+
{
|
|
56
|
+
id: 'first-match',
|
|
57
|
+
attributes: { group: 'templates.gatekeeper.sh', kind: 'ConstraintTemplate' },
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: 'second-match',
|
|
61
|
+
attributes: { group: 'templates.gatekeeper.sh', kind: 'ConstraintTemplate' },
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
expect(findTemplateType(schemas)).toStrictEqual('first-match');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('handles schemas with null attributes gracefully', () => {
|
|
69
|
+
const schemas = [
|
|
70
|
+
{ id: 'null-attrs', attributes: null },
|
|
71
|
+
{
|
|
72
|
+
id: 'templates.gatekeeper.sh.constrainttemplate',
|
|
73
|
+
attributes: { group: 'templates.gatekeeper.sh', kind: 'ConstraintTemplate' },
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
expect(findTemplateType(schemas)).toStrictEqual('templates.gatekeeper.sh.constrainttemplate');
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('findAllConstraintTypes', () => {
|
|
82
|
+
it('returns constraint type schema ids filtered by constraints.gatekeeper.sh group', () => {
|
|
83
|
+
const schemas = [
|
|
84
|
+
{ id: 'constraints.gatekeeper.sh.k8srequiredlabels', attributes: { group: 'constraints.gatekeeper.sh' } },
|
|
85
|
+
{ id: 'constraints.gatekeeper.sh.k8sallowedrepos', attributes: { group: 'constraints.gatekeeper.sh' } },
|
|
86
|
+
{ id: 'other.group.something', attributes: { group: 'other.group' } },
|
|
87
|
+
];
|
|
88
|
+
const store = { getters: { [`cluster/all`]: () => schemas } };
|
|
89
|
+
|
|
90
|
+
const result = findAllConstraintTypes(store);
|
|
91
|
+
|
|
92
|
+
expect(result).toStrictEqual([
|
|
93
|
+
'constraints.gatekeeper.sh.k8srequiredlabels',
|
|
94
|
+
'constraints.gatekeeper.sh.k8sallowedrepos',
|
|
95
|
+
]);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('returns an empty array when no constraint schemas exist', () => {
|
|
99
|
+
const store = { getters: { [`cluster/all`]: () => [] } };
|
|
100
|
+
|
|
101
|
+
expect(findAllConstraintTypes(store)).toStrictEqual([]);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('calls store.getters["cluster/all"] with SCHEMA constant', () => {
|
|
105
|
+
const mockAll = jest.fn().mockReturnValue([]);
|
|
106
|
+
const store = { getters: { [`cluster/all`]: mockAll } };
|
|
107
|
+
|
|
108
|
+
findAllConstraintTypes(store);
|
|
109
|
+
|
|
110
|
+
expect(mockAll).toHaveBeenCalledWith(SCHEMA);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('filters out schemas with missing or non-matching group', () => {
|
|
114
|
+
const schemas = [
|
|
115
|
+
{ id: 'no-attrs' },
|
|
116
|
+
{ id: 'null-group', attributes: { group: null } },
|
|
117
|
+
{ id: 'constraints.gatekeeper.sh.valid', attributes: { group: 'constraints.gatekeeper.sh' } },
|
|
118
|
+
];
|
|
119
|
+
const store = { getters: { [`cluster/all`]: () => schemas } };
|
|
120
|
+
|
|
121
|
+
const result = findAllConstraintTypes(store);
|
|
122
|
+
|
|
123
|
+
expect(result).toStrictEqual(['constraints.gatekeeper.sh.valid']);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('findAllTemplates', () => {
|
|
128
|
+
it('dispatches cluster/findAll with the constraint template type', async() => {
|
|
129
|
+
const expectedResult = [{ id: GATEKEEPER.CONSTRAINT_TEMPLATE }];
|
|
130
|
+
const mockDispatch = jest.fn().mockResolvedValue(expectedResult);
|
|
131
|
+
const store = { dispatch: mockDispatch };
|
|
132
|
+
|
|
133
|
+
const result = await findAllTemplates(store);
|
|
134
|
+
|
|
135
|
+
expect(mockDispatch).toHaveBeenCalledWith('cluster/findAll', { type: GATEKEEPER.CONSTRAINT_TEMPLATE });
|
|
136
|
+
expect(result).toStrictEqual(expectedResult);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe('findAllConstraints', () => {
|
|
141
|
+
it('returns all constraint resources flattened across all constraint types', async() => {
|
|
142
|
+
const schemas = [
|
|
143
|
+
{ id: 'constraints.gatekeeper.sh.type1', attributes: { group: 'constraints.gatekeeper.sh' } },
|
|
144
|
+
{ id: 'constraints.gatekeeper.sh.type2', attributes: { group: 'constraints.gatekeeper.sh' } },
|
|
145
|
+
];
|
|
146
|
+
const type1Resources = [{ id: 'r1' }, { id: 'r2' }];
|
|
147
|
+
const type2Resources = [{ id: 'r3' }];
|
|
148
|
+
const mockDispatch = jest.fn()
|
|
149
|
+
.mockResolvedValueOnce(type1Resources)
|
|
150
|
+
.mockResolvedValueOnce(type2Resources);
|
|
151
|
+
const store = {
|
|
152
|
+
getters: { [`cluster/all`]: () => schemas },
|
|
153
|
+
dispatch: mockDispatch,
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const result = await findAllConstraints(store);
|
|
157
|
+
|
|
158
|
+
expect(mockDispatch).toHaveBeenCalledWith('cluster/findAll', { type: 'constraints.gatekeeper.sh.type1', opt: { force: true } });
|
|
159
|
+
expect(mockDispatch).toHaveBeenCalledWith('cluster/findAll', { type: 'constraints.gatekeeper.sh.type2', opt: { force: true } });
|
|
160
|
+
expect(result).toStrictEqual([{ id: 'r1' }, { id: 'r2' }, { id: 'r3' }]);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('returns an empty array when there are no constraint types', async() => {
|
|
164
|
+
const store = {
|
|
165
|
+
getters: { [`cluster/all`]: () => [] },
|
|
166
|
+
dispatch: jest.fn(),
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const result = await findAllConstraints(store);
|
|
170
|
+
|
|
171
|
+
expect(result).toStrictEqual([]);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
});
|