@rancher/shell 3.0.12-rc.3 → 3.0.12-rc.5
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/assets/styles/global/_button.scss +1 -1
- package/assets/styles/global/_layout.scss +4 -0
- package/assets/translations/en-us.yaml +183 -51
- package/assets/translations/zh-hans.yaml +1 -7
- package/chart/monitoring/ClusterSelector.vue +0 -21
- package/chart/monitoring/prometheus/index.vue +6 -3
- package/components/ActionDropdownShell.vue +5 -3
- package/components/ButtonGroup.vue +26 -1
- package/components/CruResource.vue +212 -16
- package/components/ExplorerMembers.vue +8 -4
- package/components/ExplorerProjectsNamespaces.vue +10 -6
- package/components/GrowlManager.vue +4 -0
- package/components/MgmtNodeList.vue +184 -0
- package/components/PromptRestore.vue +93 -32
- package/components/Questions/index.vue +1 -0
- package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +90 -1
- package/components/Resource/Detail/Card/StateCard/composables.ts +57 -87
- package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +61 -0
- package/components/Resource/Detail/Card/StatusCard/index.vue +61 -15
- package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +2 -0
- package/components/Resource/Detail/Metadata/KeyValue.vue +5 -2
- package/components/Resource/Detail/Metadata/KeyValueRow.vue +2 -6
- package/components/ResourceDetail/index.vue +1 -1
- package/components/ResourceList/Masthead.vue +7 -1
- package/components/ResourceList/index.vue +82 -1
- package/components/ResourceTable.vue +1 -0
- package/components/RichTranslation.vue +5 -2
- package/components/Setting.vue +1 -0
- package/components/SortableTable/index.vue +4 -3
- package/components/SubtleLink.vue +31 -6
- package/components/Tabbed/Tab.vue +29 -3
- package/components/Tabbed/index.vue +25 -3
- package/components/TableOfContents/TableOfContents.vue +109 -0
- package/components/TableOfContents/composables.ts +258 -0
- package/components/Window/ContainerShell.vue +21 -11
- package/components/Window/__tests__/ContainerShell.test.ts +107 -37
- package/components/Wizard.vue +23 -5
- package/components/__tests__/ButtonGroup.test.ts +56 -0
- package/components/__tests__/PromptRestore.test.ts +169 -19
- package/components/fleet/AppCoChartGrid.vue +401 -0
- package/components/fleet/AppCoEmptyState.vue +127 -0
- package/components/fleet/AppCoPageHeader.vue +119 -0
- package/components/fleet/AppCoVersionSelect.vue +70 -0
- package/components/fleet/FleetClusterTargets/ClusterSelectionFields.vue +217 -0
- package/components/fleet/FleetClusterTargets/TargetsList.vue +123 -35
- package/components/fleet/FleetClusterTargets/index.vue +189 -146
- package/components/fleet/FleetIntro.vue +7 -3
- package/components/fleet/FleetNoWorkspaces.vue +7 -3
- package/components/fleet/FleetSecretSelector.vue +5 -3
- package/components/fleet/FleetValuesFrom.vue +8 -2
- package/components/fleet/GitRepoAdvancedTab.vue +1 -0
- package/components/fleet/GitRepoMetadataTab.vue +5 -0
- package/components/fleet/GitRepoTargetTab.vue +0 -2
- package/components/fleet/HelmOpAdvancedTab.vue +19 -53
- package/components/fleet/HelmOpAppCoConfigTab.vue +597 -0
- package/components/fleet/HelmOpAppCoResourcesSection.vue +162 -0
- package/components/fleet/HelmOpMetadataTab.vue +5 -0
- package/components/fleet/HelmOpResourcesSection.vue +82 -0
- package/components/fleet/HelmOpTargetOptionsSection.vue +89 -0
- package/components/fleet/HelmOpTargetTab.vue +64 -60
- package/components/fleet/HelmOpValuesTab.vue +129 -105
- package/components/fleet/__tests__/AppCoEmptyState.test.ts +71 -0
- package/components/fleet/__tests__/AppCoVersionSelect.test.ts +36 -0
- package/components/fleet/__tests__/ClusterSelectionFields.test.ts +62 -0
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +253 -0
- package/components/fleet/__tests__/FleetSecretSelector.test.ts +16 -0
- package/components/fleet/__tests__/FleetValuesFrom.test.ts +44 -0
- package/components/fleet/__tests__/HelmOpAppCoConfigTab.test.ts +59 -0
- package/components/fleet/__tests__/HelmOpAppCoResourcesSection.test.ts +62 -0
- package/components/fleet/__tests__/HelmOpResourcesSection.test.ts +43 -0
- package/components/fleet/__tests__/HelmOpTargetOptionsSection.test.ts +34 -0
- package/components/fleet/__tests__/HelmOpValuesTab.test.ts +39 -0
- package/components/fleet/__tests__/__snapshots__/AppCoEmptyState.test.ts.snap +97 -0
- package/components/fleet/__tests__/__snapshots__/AppCoVersionSelect.test.ts.snap +30 -0
- package/components/fleet/__tests__/__snapshots__/ClusterSelectionFields.test.ts.snap +209 -0
- package/components/fleet/__tests__/__snapshots__/HelmOpTargetOptionsSection.test.ts.snap +140 -0
- package/components/fleet/dashboard/Empty.vue +8 -4
- package/components/fleet/dashboard/ResourceCard.vue +28 -0
- package/components/fleet/dashboard/ResourceDetails.vue +28 -0
- package/components/fleet/dashboard/__tests__/ResourceCard.test.ts +87 -0
- package/components/form/ArrayList.vue +61 -4
- package/components/form/FileSelector.vue +39 -1
- package/components/form/KeyValue.vue +23 -2
- package/components/form/LabeledSelect.vue +39 -1
- package/components/form/Labels.vue +22 -3
- package/components/form/NameNsDescription.vue +13 -5
- package/components/form/PrivateRegistry.constants.ts +7 -0
- package/components/form/PrivateRegistry.vue +253 -18
- package/components/form/ResourceTabs/index.vue +1 -0
- package/components/form/SelectOrCreateAuthSecret.vue +140 -17
- package/components/form/__tests__/FileSelector.test.ts +23 -0
- package/components/form/__tests__/NameNsDescription.test.ts +75 -0
- package/components/form/__tests__/PrivateRegistry.test.ts +463 -73
- package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +122 -0
- package/components/formatter/EtcdSnapshotName.vue +73 -0
- package/components/formatter/InternalExternalIP.vue +10 -4
- package/components/formatter/ServiceTargets.vue +26 -7
- package/components/formatter/__tests__/InternalExternalIP.test.ts +132 -0
- package/components/formatter/__tests__/ServiceTargets.test.ts +412 -0
- package/components/nav/Header.vue +12 -1
- package/components/nav/TopLevelMenu.vue +7 -2
- package/components/nav/__tests__/Header.test.ts +15 -0
- package/components/nav/__tests__/TopLevelMenu.test.ts +120 -2
- package/components/templates/default.vue +16 -4
- package/components/templates/home.vue +9 -4
- package/components/templates/plain.vue +9 -4
- package/composables/useHelmOpResources.test.ts +56 -0
- package/composables/useHelmOpResources.ts +32 -0
- package/composables/useStateColor.test.ts +325 -0
- package/composables/useStateColor.ts +128 -0
- package/config/features.js +1 -0
- package/config/home-links.js +1 -1
- package/config/labels-annotations.js +3 -0
- package/config/product/explorer.js +17 -4
- package/config/product/manager.js +8 -0
- package/config/router/index.js +16 -0
- package/config/router/navigation-guards/__tests__/authentication.test.ts +130 -0
- package/config/router/navigation-guards/authentication.js +10 -4
- package/config/router/routes.js +20 -6
- package/config/secret.ts +10 -0
- package/config/settings.ts +6 -4
- package/config/table-headers.js +3 -4
- package/config/types.js +16 -0
- package/core/plugin-products-base.ts +3 -3
- package/core/plugin-types.ts +83 -30
- package/core/plugin.ts +3 -0
- package/core/types-provisioning.ts +34 -1
- package/core/types.ts +15 -2
- package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +114 -0
- package/detail/__tests__/workload.test.ts +3 -152
- package/detail/catalog.cattle.io.clusterrepo.vue +1 -1
- package/detail/provisioning.cattle.io.cluster.vue +109 -7
- package/detail/workload/index.vue +12 -55
- package/dialog/RotateEncryptionKeyDialog.vue +33 -9
- package/dialog/__tests__/RotateEncryptionKeyDialog.test.ts +78 -0
- package/edit/__tests__/catalog.cattle.io.clusterrepo.test.ts +248 -0
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +92 -0
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +206 -0
- package/edit/__tests__/management.cattle.io.setting.test.ts +2 -1
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +1 -0
- package/edit/auth/__tests__/azuread.test.ts +34 -9
- package/edit/auth/__tests__/github.test.ts +234 -0
- package/edit/auth/__tests__/oidc.test.ts +26 -6
- package/edit/auth/__tests__/saml.test.ts +196 -0
- package/edit/auth/azuread.vue +128 -95
- package/edit/auth/github.vue +72 -13
- package/edit/auth/ldap/__tests__/index.test.ts +206 -0
- package/edit/auth/ldap/config.vue +8 -0
- package/edit/auth/ldap/index.vue +75 -1
- package/edit/auth/oidc.vue +119 -73
- package/edit/auth/saml.vue +76 -12
- package/edit/catalog.cattle.io.clusterrepo.vue +140 -32
- package/edit/compliance.cattle.io.clusterscanprofile.vue +39 -41
- package/edit/fleet.cattle.io.gitrepo.vue +70 -16
- package/edit/fleet.cattle.io.helmop.vue +542 -141
- package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
- package/edit/{management.cattle.io.setting.vue → management.cattle.io.setting/index.vue} +32 -9
- package/edit/management.cattle.io.setting/system-default-registry-pull-secrets.vue +81 -0
- package/edit/management.cattle.io.user.vue +5 -2
- package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +3 -12
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +18 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +89 -11
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +0 -1
- package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +14 -55
- package/list/group.principal.vue +5 -4
- package/list/harvesterhci.io.management.cluster.vue +8 -9
- package/list/management.cattle.io.user.vue +12 -9
- package/list/provisioning.cattle.io.cluster.vue +16 -10
- package/mixins/__tests__/auth-config.test.ts +90 -0
- package/mixins/__tests__/chart.test.ts +94 -0
- package/mixins/__tests__/resource-fetch-api-pagination.test.ts +48 -0
- package/mixins/auth-config.js +7 -0
- package/mixins/chart.js +11 -2
- package/mixins/child-hook.js +12 -6
- package/mixins/create-edit-view/impl.js +5 -3
- package/mixins/resource-fetch-api-pagination.js +21 -1
- package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +57 -0
- package/models/__tests__/compliance.cattle.io.clusterscan.test.ts +144 -0
- package/models/__tests__/fleet-application.test.ts +175 -0
- package/models/__tests__/fleet.cattle.io.bundle.test.ts +169 -0
- package/models/__tests__/fleet.cattle.io.helmop.test.ts +84 -0
- package/models/__tests__/management.cattle.io.node.ts +22 -0
- package/models/__tests__/namespace.test.ts +36 -0
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +205 -0
- package/models/__tests__/secret.test.ts +68 -1
- package/models/__tests__/workload.test.ts +401 -26
- package/models/catalog.cattle.io.clusterrepo.js +28 -4
- package/models/compliance.cattle.io.clusterscan.js +39 -4
- package/models/fleet-application.js +4 -0
- package/models/fleet.cattle.io.helmop.js +20 -1
- package/models/management.cattle.io.cluster.js +39 -5
- package/models/management.cattle.io.node.js +44 -3
- package/models/namespace.js +1 -1
- package/models/pod.js +46 -3
- package/models/provisioning.cattle.io.cluster.js +64 -14
- package/models/rke.cattle.io.etcdsnapshot.js +17 -9
- package/models/secret.js +19 -0
- package/models/workload.js +120 -20
- package/models/workload.service.js +5 -0
- package/package.json +14 -13
- package/pages/about.vue +5 -6
- package/pages/auth/login.vue +0 -35
- package/pages/auth/setup.vue +11 -0
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +2 -2
- package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +10 -1
- package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +93 -0
- package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +485 -107
- package/pages/c/_cluster/apps/charts/chart.vue +2 -1
- package/pages/c/_cluster/apps/charts/index.vue +48 -10
- package/pages/c/_cluster/apps/charts/install.vue +236 -144
- package/pages/c/_cluster/auth/roles/index.vue +5 -4
- package/pages/c/_cluster/explorer/workload-dashboard/ByNamespaceSection.vue +31 -0
- package/pages/c/_cluster/explorer/workload-dashboard/ByStateSection.vue +138 -0
- package/pages/c/_cluster/explorer/workload-dashboard/ByTypeSection.vue +30 -0
- package/pages/c/_cluster/explorer/workload-dashboard/WorkloadCard.vue +155 -0
- package/pages/c/_cluster/explorer/workload-dashboard/WorkloadNamespaceCard.vue +142 -0
- package/pages/c/_cluster/explorer/workload-dashboard/WorkloadTypeCard.vue +159 -0
- package/pages/c/_cluster/explorer/workload-dashboard/__tests__/composable.test.ts +561 -0
- package/pages/c/_cluster/explorer/workload-dashboard/composable.ts +440 -0
- package/pages/c/_cluster/explorer/workload-dashboard/index.vue +187 -0
- package/pages/c/_cluster/explorer/workload-dashboard/types.ts +80 -0
- package/pages/c/_cluster/fleet/application/create.vue +187 -136
- package/pages/c/_cluster/fleet/application/index.vue +5 -3
- package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailBody.vue +338 -0
- package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailHeader.vue +121 -0
- package/pages/c/_cluster/fleet/application/suse-app-collection/chart.vue +369 -0
- package/pages/c/_cluster/fleet/application/suse-app-collection/charts.vue +248 -0
- package/pages/c/_cluster/fleet/application/suse-app-collection/credentials.vue +310 -0
- package/pages/c/_cluster/fleet/index.vue +2 -2
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +96 -0
- package/pages/c/_cluster/uiplugins/index.vue +15 -0
- package/pages/fail-whale.vue +16 -11
- package/pages/home.vue +16 -46
- package/pkg/require-asset.lib.js +25 -0
- package/pkg/vue.config.js +7 -0
- package/plugins/clean-html.d.ts +9 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +177 -0
- package/plugins/dashboard-store/getters.js +0 -1
- package/plugins/dashboard-store/resource-class.js +114 -19
- package/plugins/steve/__tests__/actions.test.ts +212 -0
- package/plugins/steve/actions.js +96 -0
- package/plugins/steve/steve-pagination-utils.ts +1 -1
- package/rancher-components/Accordion/Accordion.vue +53 -9
- package/rancher-components/Form/Checkbox/Checkbox.vue +14 -0
- package/rancher-components/Form/Radio/RadioButton.vue +17 -1
- package/rancher-components/Form/Radio/RadioGroup.vue +10 -0
- package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +30 -0
- package/rancher-components/Form/TextArea/__tests__/TextAreaAutoGrow.test.ts +95 -0
- package/rancher-components/Pill/RcTag/RcTag.vue +3 -2
- package/rancher-components/RcButton/RcButton.test.ts +103 -0
- package/rancher-components/RcButton/RcButton.vue +94 -15
- package/rancher-components/RcButton/index.ts +1 -1
- package/rancher-components/RcButton/types.ts +3 -0
- package/rancher-components/RcDropdown/RcDropdownTrigger.vue +6 -1
- package/rancher-components/RcItemCard/RcItemCard.test.ts +18 -0
- package/rancher-components/RcItemCard/RcItemCard.vue +2 -2
- package/rancher-components/RcSection/RcSection.vue +28 -3
- package/scripts/extension/helm/package/Dockerfile +1 -1
- package/scripts/test-plugins-build.sh +2 -1
- package/store/__tests__/features.test.ts +131 -0
- package/store/__tests__/growl.test.ts +374 -0
- package/store/__tests__/modal.test.ts +131 -0
- package/store/__tests__/notifications.test.ts +434 -0
- package/store/__tests__/slideInPanel.test.ts +88 -0
- package/store/__tests__/type-map.utils.test.ts +433 -0
- package/store/catalog.js +57 -0
- package/store/features.js +4 -0
- package/store/plugins.js +7 -4
- package/types/components/buttonGroup.ts +5 -0
- package/types/shell/index.d.ts +166 -70
- package/utils/__tests__/auth.test.ts +273 -0
- package/utils/__tests__/computed.test.ts +193 -0
- package/utils/__tests__/cspAdaptor.test.ts +163 -0
- package/utils/__tests__/dom.test.ts +81 -0
- package/utils/__tests__/duration.test.ts +37 -1
- package/utils/__tests__/dynamic-importer.test.ts +102 -0
- package/utils/__tests__/fleet-appco.test.ts +312 -0
- package/utils/__tests__/monitoring.test.ts +130 -0
- package/utils/__tests__/object.test.ts +22 -0
- package/utils/__tests__/operation-cr.test.ts +34 -0
- package/utils/__tests__/platform.test.ts +91 -0
- package/utils/__tests__/position.test.ts +237 -0
- package/utils/__tests__/provider.test.ts +51 -1
- package/utils/__tests__/queue.test.ts +232 -0
- package/utils/__tests__/release-notes.test.ts +221 -0
- package/utils/__tests__/router.test.js +254 -1
- package/utils/__tests__/select.test.ts +208 -0
- package/utils/__tests__/time.test.ts +265 -1
- package/utils/__tests__/title.test.ts +47 -0
- package/utils/__tests__/width.test.ts +53 -0
- package/utils/__tests__/window.test.ts +158 -0
- package/utils/__tests__/xccdf.test.ts +126 -6
- package/utils/crypto/__tests__/browserHashUtils.test.ts +98 -0
- package/utils/crypto/__tests__/index.test.ts +144 -0
- package/utils/duration.ts +104 -0
- package/utils/dynamic-content/__tests__/notification-handler.test.ts +196 -0
- package/utils/dynamic-content/info.ts +2 -1
- package/utils/error.js +13 -0
- package/utils/fleet-appco.ts +323 -0
- package/utils/object.js +22 -2
- package/utils/operation-cr.js +19 -0
- package/utils/provider.ts +12 -0
- package/utils/require-asset.ts +7 -0
- package/utils/validators/__tests__/container-images.test.ts +104 -0
- package/utils/validators/__tests__/flow-output.test.ts +91 -0
- package/utils/validators/__tests__/logging-outputs.test.ts +58 -0
- package/utils/validators/__tests__/monitoring-route.test.ts +119 -0
- package/utils/validators/__tests__/private-registry.test.ts +27 -15
- package/utils/validators/private-registry.ts +15 -4
- package/utils/xccdf.ts +39 -42
- package/vue.config.js +1 -1
- package/pages/support/index.vue +0 -264
- package/utils/duration.js +0 -43
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Workload from '@shell/models/workload.js';
|
|
2
2
|
import { steveClassJunkObject } from '@shell/plugins/steve/__tests__/utils/steve-mocks';
|
|
3
|
-
import { WORKLOAD_TYPES, SERVICE } from '@shell/config/types';
|
|
3
|
+
import { WORKLOAD_TYPES, SERVICE, INGRESS } from '@shell/config/types';
|
|
4
4
|
|
|
5
5
|
describe('class: Workload', () => {
|
|
6
6
|
describe('given custom workload keys', () => {
|
|
@@ -160,12 +160,7 @@ describe('class: Workload', () => {
|
|
|
160
160
|
});
|
|
161
161
|
|
|
162
162
|
describe('getter: relatedServices', () => {
|
|
163
|
-
it('should return services that match workload
|
|
164
|
-
const mockPod = {
|
|
165
|
-
metadata: {
|
|
166
|
-
name: 'pod-1', namespace: 'default', labels: { app: 'my-app' }
|
|
167
|
-
}
|
|
168
|
-
};
|
|
163
|
+
it('should return services that match workload pod template labels', () => {
|
|
169
164
|
const mockService = {
|
|
170
165
|
metadata: { name: 'my-service', namespace: 'default' },
|
|
171
166
|
spec: { selector: { app: 'my-app' } }
|
|
@@ -173,7 +168,7 @@ describe('class: Workload', () => {
|
|
|
173
168
|
const workload = new Workload({
|
|
174
169
|
type: WORKLOAD_TYPES.DEPLOYMENT,
|
|
175
170
|
metadata: { name: 'test', namespace: 'default' },
|
|
176
|
-
spec: {}
|
|
171
|
+
spec: { template: { metadata: { labels: { app: 'my-app' } } } }
|
|
177
172
|
}, {
|
|
178
173
|
getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
|
|
179
174
|
dispatch: jest.fn(),
|
|
@@ -183,20 +178,12 @@ describe('class: Workload', () => {
|
|
|
183
178
|
},
|
|
184
179
|
});
|
|
185
180
|
|
|
186
|
-
// Mock pods getter
|
|
187
|
-
Object.defineProperty(workload, 'pods', { get: () => [mockPod] });
|
|
188
|
-
|
|
189
181
|
const related = workload.relatedServices;
|
|
190
182
|
|
|
191
183
|
expect(related).toContain(mockService);
|
|
192
184
|
});
|
|
193
185
|
|
|
194
186
|
it('should not return services from different namespace', () => {
|
|
195
|
-
const mockPod = {
|
|
196
|
-
metadata: {
|
|
197
|
-
name: 'pod-1', namespace: 'default', labels: { app: 'my-app' }
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
187
|
const mockService = {
|
|
201
188
|
metadata: { name: 'my-service', namespace: 'other-namespace' },
|
|
202
189
|
spec: { selector: { app: 'my-app' } }
|
|
@@ -204,7 +191,7 @@ describe('class: Workload', () => {
|
|
|
204
191
|
const workload = new Workload({
|
|
205
192
|
type: WORKLOAD_TYPES.DEPLOYMENT,
|
|
206
193
|
metadata: { name: 'test', namespace: 'default' },
|
|
207
|
-
spec: {}
|
|
194
|
+
spec: { template: { metadata: { labels: { app: 'my-app' } } } }
|
|
208
195
|
}, {
|
|
209
196
|
getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
|
|
210
197
|
dispatch: jest.fn(),
|
|
@@ -214,19 +201,12 @@ describe('class: Workload', () => {
|
|
|
214
201
|
},
|
|
215
202
|
});
|
|
216
203
|
|
|
217
|
-
Object.defineProperty(workload, 'pods', { get: () => [mockPod] });
|
|
218
|
-
|
|
219
204
|
const related = workload.relatedServices;
|
|
220
205
|
|
|
221
206
|
expect(related).toHaveLength(0);
|
|
222
207
|
});
|
|
223
208
|
|
|
224
209
|
it('should not return services with non-matching selectors', () => {
|
|
225
|
-
const mockPod = {
|
|
226
|
-
metadata: {
|
|
227
|
-
name: 'pod-1', namespace: 'default', labels: { app: 'my-app' }
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
210
|
const mockService = {
|
|
231
211
|
metadata: { name: 'my-service', namespace: 'default' },
|
|
232
212
|
spec: { selector: { app: 'different-app' } }
|
|
@@ -234,7 +214,7 @@ describe('class: Workload', () => {
|
|
|
234
214
|
const workload = new Workload({
|
|
235
215
|
type: WORKLOAD_TYPES.DEPLOYMENT,
|
|
236
216
|
metadata: { name: 'test', namespace: 'default' },
|
|
237
|
-
spec: {}
|
|
217
|
+
spec: { template: { metadata: { labels: { app: 'my-app' } } } }
|
|
238
218
|
}, {
|
|
239
219
|
getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
|
|
240
220
|
dispatch: jest.fn(),
|
|
@@ -244,7 +224,28 @@ describe('class: Workload', () => {
|
|
|
244
224
|
},
|
|
245
225
|
});
|
|
246
226
|
|
|
247
|
-
|
|
227
|
+
const related = workload.relatedServices;
|
|
228
|
+
|
|
229
|
+
expect(related).toHaveLength(0);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('should return empty array when pod template has no labels', () => {
|
|
233
|
+
const mockService = {
|
|
234
|
+
metadata: { name: 'my-service', namespace: 'default' },
|
|
235
|
+
spec: { selector: { app: 'my-app' } }
|
|
236
|
+
};
|
|
237
|
+
const workload = new Workload({
|
|
238
|
+
type: WORKLOAD_TYPES.DEPLOYMENT,
|
|
239
|
+
metadata: { name: 'test', namespace: 'default' },
|
|
240
|
+
spec: { template: { metadata: {} } }
|
|
241
|
+
}, {
|
|
242
|
+
getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
|
|
243
|
+
dispatch: jest.fn(),
|
|
244
|
+
rootGetters: {
|
|
245
|
+
'i18n/t': jest.fn(),
|
|
246
|
+
'cluster/all': (type: string) => (type === SERVICE ? [mockService] : [])
|
|
247
|
+
},
|
|
248
|
+
});
|
|
248
249
|
|
|
249
250
|
const related = workload.relatedServices;
|
|
250
251
|
|
|
@@ -506,4 +507,378 @@ describe('class: Workload', () => {
|
|
|
506
507
|
expect(jobsCard).toBeDefined();
|
|
507
508
|
});
|
|
508
509
|
});
|
|
510
|
+
|
|
511
|
+
describe('getter: matchingIngresses', () => {
|
|
512
|
+
const makeWorkload = (services: any[], ingresses: any[], pods: any[] = []) => {
|
|
513
|
+
const workload = new Workload({
|
|
514
|
+
type: WORKLOAD_TYPES.DEPLOYMENT,
|
|
515
|
+
metadata: { name: 'test', namespace: 'default' },
|
|
516
|
+
spec: { template: { metadata: { labels: { app: 'my-app' } } } }
|
|
517
|
+
}, {
|
|
518
|
+
getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
|
|
519
|
+
dispatch: jest.fn(),
|
|
520
|
+
rootGetters: {
|
|
521
|
+
'i18n/t': jest.fn(),
|
|
522
|
+
'cluster/all': (type: string) => {
|
|
523
|
+
if (type === SERVICE) {
|
|
524
|
+
return services;
|
|
525
|
+
}
|
|
526
|
+
if (type === INGRESS) {
|
|
527
|
+
return ingresses;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
return [];
|
|
531
|
+
}
|
|
532
|
+
},
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
Object.defineProperty(workload, 'pods', { get: () => pods });
|
|
536
|
+
|
|
537
|
+
return workload;
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
it('should return empty array when no related services', () => {
|
|
541
|
+
const workload = makeWorkload([], [
|
|
542
|
+
{
|
|
543
|
+
metadata: { namespace: 'default' },
|
|
544
|
+
spec: { rules: [{ http: { paths: [{ backend: { service: { name: 'svc1' } } }] } }] }
|
|
545
|
+
}
|
|
546
|
+
]);
|
|
547
|
+
|
|
548
|
+
expect(workload.matchingIngresses).toStrictEqual([]);
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
it('should find matching ingresses', () => {
|
|
552
|
+
const mockPod = {
|
|
553
|
+
metadata: {
|
|
554
|
+
name: 'pod-1', namespace: 'default', labels: { app: 'my-app' }
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
const mockService = {
|
|
558
|
+
metadata: { name: 'svc1', namespace: 'default' },
|
|
559
|
+
spec: { selector: { app: 'my-app' } }
|
|
560
|
+
};
|
|
561
|
+
const mockIngress = {
|
|
562
|
+
metadata: { namespace: 'default' },
|
|
563
|
+
spec: { rules: [{ http: { paths: [{ backend: { service: { name: 'svc1' } } }] } }] }
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
const workload = makeWorkload([mockService], [mockIngress], [mockPod]);
|
|
567
|
+
|
|
568
|
+
expect(workload.matchingIngresses).toHaveLength(1);
|
|
569
|
+
expect(workload.matchingIngresses[0]).toStrictEqual(mockIngress);
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
it('should not match ingresses from other namespaces', () => {
|
|
573
|
+
const mockPod = {
|
|
574
|
+
metadata: {
|
|
575
|
+
name: 'pod-1', namespace: 'default', labels: { app: 'my-app' }
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
const mockService = {
|
|
579
|
+
metadata: { name: 'svc1', namespace: 'default' },
|
|
580
|
+
spec: { selector: { app: 'my-app' } }
|
|
581
|
+
};
|
|
582
|
+
const mockIngress = {
|
|
583
|
+
metadata: { namespace: 'other' },
|
|
584
|
+
spec: { rules: [{ http: { paths: [{ backend: { service: { name: 'svc1' } } }] } }] }
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
const workload = makeWorkload([mockService], [mockIngress], [mockPod]);
|
|
588
|
+
|
|
589
|
+
expect(workload.matchingIngresses).toHaveLength(0);
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
it('should not match ingresses pointing to other services', () => {
|
|
593
|
+
const mockPod = {
|
|
594
|
+
metadata: {
|
|
595
|
+
name: 'pod-1', namespace: 'default', labels: { app: 'my-app' }
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
const mockService = {
|
|
599
|
+
metadata: { name: 'svc1', namespace: 'default' },
|
|
600
|
+
spec: { selector: { app: 'my-app' } }
|
|
601
|
+
};
|
|
602
|
+
const mockIngress = {
|
|
603
|
+
metadata: { namespace: 'default' },
|
|
604
|
+
spec: { rules: [{ http: { paths: [{ backend: { service: { name: 'svc2' } } }] } }] }
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
const workload = makeWorkload([mockService], [mockIngress], [mockPod]);
|
|
608
|
+
|
|
609
|
+
expect(workload.matchingIngresses).toHaveLength(0);
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
it('should handle ingresses with no rules', () => {
|
|
613
|
+
const mockPod = {
|
|
614
|
+
metadata: {
|
|
615
|
+
name: 'pod-1', namespace: 'default', labels: { app: 'my-app' }
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
const mockService = {
|
|
619
|
+
metadata: { name: 'svc1', namespace: 'default' },
|
|
620
|
+
spec: { selector: { app: 'my-app' } }
|
|
621
|
+
};
|
|
622
|
+
const mockIngress = {
|
|
623
|
+
metadata: { namespace: 'default' },
|
|
624
|
+
spec: {}
|
|
625
|
+
};
|
|
626
|
+
|
|
627
|
+
const workload = makeWorkload([mockService], [mockIngress], [mockPod]);
|
|
628
|
+
|
|
629
|
+
expect(workload.matchingIngresses).toHaveLength(0);
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
it('should handle ingress rules with no paths', () => {
|
|
633
|
+
const mockPod = {
|
|
634
|
+
metadata: {
|
|
635
|
+
name: 'pod-1', namespace: 'default', labels: { app: 'my-app' }
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
const mockService = {
|
|
639
|
+
metadata: { name: 'svc1', namespace: 'default' },
|
|
640
|
+
spec: { selector: { app: 'my-app' } }
|
|
641
|
+
};
|
|
642
|
+
const mockIngress = {
|
|
643
|
+
metadata: { namespace: 'default' },
|
|
644
|
+
spec: { rules: [{ http: {} }] }
|
|
645
|
+
};
|
|
646
|
+
|
|
647
|
+
const workload = makeWorkload([mockService], [mockIngress], [mockPod]);
|
|
648
|
+
|
|
649
|
+
expect(workload.matchingIngresses).toHaveLength(0);
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
it('should handle ingress paths with no backend service', () => {
|
|
653
|
+
const mockPod = {
|
|
654
|
+
metadata: {
|
|
655
|
+
name: 'pod-1', namespace: 'default', labels: { app: 'my-app' }
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
const mockService = {
|
|
659
|
+
metadata: { name: 'svc1', namespace: 'default' },
|
|
660
|
+
spec: { selector: { app: 'my-app' } }
|
|
661
|
+
};
|
|
662
|
+
const mockIngress = {
|
|
663
|
+
metadata: { namespace: 'default' },
|
|
664
|
+
spec: { rules: [{ http: { paths: [{ backend: {} }] } }] }
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
const workload = makeWorkload([mockService], [mockIngress], [mockPod]);
|
|
668
|
+
|
|
669
|
+
expect(workload.matchingIngresses).toHaveLength(0);
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
it('should find one of many ingresses', () => {
|
|
673
|
+
const mockPod = {
|
|
674
|
+
metadata: {
|
|
675
|
+
name: 'pod-1', namespace: 'default', labels: { app: 'my-app' }
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
const mockService = {
|
|
679
|
+
metadata: { name: 'svc1', namespace: 'default' },
|
|
680
|
+
spec: { selector: { app: 'my-app' } }
|
|
681
|
+
};
|
|
682
|
+
const ingresses = [
|
|
683
|
+
{
|
|
684
|
+
metadata: { namespace: 'other' },
|
|
685
|
+
spec: { rules: [{ http: { paths: [{ backend: { service: { name: 'svc1' } } }] } }] }
|
|
686
|
+
},
|
|
687
|
+
{
|
|
688
|
+
metadata: { namespace: 'default' },
|
|
689
|
+
spec: { rules: [{ http: { paths: [{ backend: { service: { name: 'svc1' } } }] } }] }
|
|
690
|
+
},
|
|
691
|
+
{
|
|
692
|
+
metadata: { namespace: 'default' },
|
|
693
|
+
spec: { rules: [{ http: { paths: [{ backend: { service: { name: 'svc2' } } }] } }] }
|
|
694
|
+
}
|
|
695
|
+
];
|
|
696
|
+
|
|
697
|
+
const workload = makeWorkload([mockService], ingresses, [mockPod]);
|
|
698
|
+
|
|
699
|
+
expect(workload.matchingIngresses).toHaveLength(1);
|
|
700
|
+
expect(workload.matchingIngresses[0]).toStrictEqual(ingresses[1]);
|
|
701
|
+
});
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
describe('getter: resourcesCardRows', () => {
|
|
705
|
+
it('should include services row when related services exist', () => {
|
|
706
|
+
const mockPod = {
|
|
707
|
+
metadata: {
|
|
708
|
+
name: 'pod-1', namespace: 'default', labels: { app: 'my-app' }
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
const mockService = {
|
|
712
|
+
metadata: { name: 'svc1', namespace: 'default' },
|
|
713
|
+
spec: { selector: { app: 'my-app' } },
|
|
714
|
+
stateDisplay: 'Active',
|
|
715
|
+
stateSimpleColor: 'success'
|
|
716
|
+
};
|
|
717
|
+
|
|
718
|
+
const workload = new Workload({
|
|
719
|
+
type: WORKLOAD_TYPES.DEPLOYMENT,
|
|
720
|
+
metadata: { name: 'test', namespace: 'default' },
|
|
721
|
+
spec: { template: { metadata: { labels: { app: 'my-app' } } } }
|
|
722
|
+
}, {
|
|
723
|
+
getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
|
|
724
|
+
dispatch: jest.fn(),
|
|
725
|
+
rootGetters: {
|
|
726
|
+
'i18n/t': (key: string) => key,
|
|
727
|
+
'cluster/all': (type: string) => {
|
|
728
|
+
if (type === SERVICE) {
|
|
729
|
+
return [mockService];
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
return [];
|
|
733
|
+
}
|
|
734
|
+
},
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
Object.defineProperty(workload, 'pods', { get: () => [mockPod] });
|
|
738
|
+
|
|
739
|
+
const rows = workload.resourcesCardRows;
|
|
740
|
+
const servicesRow = rows.find((r: any) => r.label === 'component.resource.detail.card.resourcesCard.rows.services');
|
|
741
|
+
|
|
742
|
+
expect(servicesRow).toBeDefined();
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
it('should include ingresses row when matching ingresses exist', () => {
|
|
746
|
+
const mockPod = {
|
|
747
|
+
metadata: {
|
|
748
|
+
name: 'pod-1', namespace: 'default', labels: { app: 'my-app' }
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
const mockService = {
|
|
752
|
+
metadata: { name: 'svc1', namespace: 'default' },
|
|
753
|
+
spec: { selector: { app: 'my-app' } },
|
|
754
|
+
stateDisplay: 'Active',
|
|
755
|
+
stateSimpleColor: 'success'
|
|
756
|
+
};
|
|
757
|
+
const mockIngress = {
|
|
758
|
+
metadata: { namespace: 'default' },
|
|
759
|
+
spec: { rules: [{ http: { paths: [{ backend: { service: { name: 'svc1' } } }] } }] },
|
|
760
|
+
stateDisplay: 'Active',
|
|
761
|
+
stateSimpleColor: 'success'
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
const workload = new Workload({
|
|
765
|
+
type: WORKLOAD_TYPES.DEPLOYMENT,
|
|
766
|
+
metadata: { name: 'test', namespace: 'default' },
|
|
767
|
+
spec: { template: { metadata: { labels: { app: 'my-app' } } } }
|
|
768
|
+
}, {
|
|
769
|
+
getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
|
|
770
|
+
dispatch: jest.fn(),
|
|
771
|
+
rootGetters: {
|
|
772
|
+
'i18n/t': (key: string) => key,
|
|
773
|
+
'cluster/all': (type: string) => {
|
|
774
|
+
if (type === SERVICE) {
|
|
775
|
+
return [mockService];
|
|
776
|
+
}
|
|
777
|
+
if (type === INGRESS) {
|
|
778
|
+
return [mockIngress];
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
return [];
|
|
782
|
+
}
|
|
783
|
+
},
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
Object.defineProperty(workload, 'pods', { get: () => [mockPod] });
|
|
787
|
+
|
|
788
|
+
const rows = workload.resourcesCardRows;
|
|
789
|
+
const ingressesRow = rows.find((r: any) => r.label === 'component.resource.detail.card.resourcesCard.rows.ingresses');
|
|
790
|
+
|
|
791
|
+
expect(ingressesRow).toBeDefined();
|
|
792
|
+
expect(ingressesRow.to).toBe('#ingresses');
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
it('should order services before ingresses', () => {
|
|
796
|
+
const mockPod = {
|
|
797
|
+
metadata: {
|
|
798
|
+
name: 'pod-1', namespace: 'default', labels: { app: 'my-app' }
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
const mockService = {
|
|
802
|
+
metadata: { name: 'svc1', namespace: 'default' },
|
|
803
|
+
spec: { selector: { app: 'my-app' } },
|
|
804
|
+
stateDisplay: 'Active',
|
|
805
|
+
stateSimpleColor: 'success'
|
|
806
|
+
};
|
|
807
|
+
const mockIngress = {
|
|
808
|
+
metadata: { namespace: 'default' },
|
|
809
|
+
spec: { rules: [{ http: { paths: [{ backend: { service: { name: 'svc1' } } }] } }] },
|
|
810
|
+
stateDisplay: 'Active',
|
|
811
|
+
stateSimpleColor: 'success'
|
|
812
|
+
};
|
|
813
|
+
|
|
814
|
+
const workload = new Workload({
|
|
815
|
+
type: WORKLOAD_TYPES.DEPLOYMENT,
|
|
816
|
+
metadata: { name: 'test', namespace: 'default' },
|
|
817
|
+
spec: { template: { metadata: { labels: { app: 'my-app' } } } }
|
|
818
|
+
}, {
|
|
819
|
+
getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
|
|
820
|
+
dispatch: jest.fn(),
|
|
821
|
+
rootGetters: {
|
|
822
|
+
'i18n/t': (key: string) => key,
|
|
823
|
+
'cluster/all': (type: string) => {
|
|
824
|
+
if (type === SERVICE) {
|
|
825
|
+
return [mockService];
|
|
826
|
+
}
|
|
827
|
+
if (type === INGRESS) {
|
|
828
|
+
return [mockIngress];
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
return [];
|
|
832
|
+
}
|
|
833
|
+
},
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
Object.defineProperty(workload, 'pods', { get: () => [mockPod] });
|
|
837
|
+
|
|
838
|
+
const rows = workload.resourcesCardRows;
|
|
839
|
+
|
|
840
|
+
expect(rows[0].label).toBe('component.resource.detail.card.resourcesCard.rows.services');
|
|
841
|
+
expect(rows[1].label).toBe('component.resource.detail.card.resourcesCard.rows.ingresses');
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
it('should not include ingresses row when no matching ingresses', () => {
|
|
845
|
+
const mockPod = {
|
|
846
|
+
metadata: {
|
|
847
|
+
name: 'pod-1', namespace: 'default', labels: { app: 'my-app' }
|
|
848
|
+
}
|
|
849
|
+
};
|
|
850
|
+
const mockService = {
|
|
851
|
+
metadata: { name: 'svc1', namespace: 'default' },
|
|
852
|
+
spec: { selector: { app: 'my-app' } },
|
|
853
|
+
stateDisplay: 'Active',
|
|
854
|
+
stateSimpleColor: 'success'
|
|
855
|
+
};
|
|
856
|
+
|
|
857
|
+
const workload = new Workload({
|
|
858
|
+
type: WORKLOAD_TYPES.DEPLOYMENT,
|
|
859
|
+
metadata: { name: 'test', namespace: 'default' },
|
|
860
|
+
spec: { template: { metadata: { labels: { app: 'my-app' } } } }
|
|
861
|
+
}, {
|
|
862
|
+
getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
|
|
863
|
+
dispatch: jest.fn(),
|
|
864
|
+
rootGetters: {
|
|
865
|
+
'i18n/t': (key: string) => key,
|
|
866
|
+
'cluster/all': (type: string) => {
|
|
867
|
+
if (type === SERVICE) {
|
|
868
|
+
return [mockService];
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
return [];
|
|
872
|
+
}
|
|
873
|
+
},
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
Object.defineProperty(workload, 'pods', { get: () => [mockPod] });
|
|
877
|
+
|
|
878
|
+
const rows = workload.resourcesCardRows;
|
|
879
|
+
const ingressesRow = rows.find((r: any) => r.label === 'component.resource.detail.card.resourcesCard.rows.ingresses');
|
|
880
|
+
|
|
881
|
+
expect(ingressesRow).toBeUndefined();
|
|
882
|
+
});
|
|
883
|
+
});
|
|
509
884
|
});
|
|
@@ -4,8 +4,10 @@ import { insertAt } from '@shell/utils/array';
|
|
|
4
4
|
import { CLUSTER_REPO_APPCO_AUTH_GENERATE_NAME, CATALOG as CATALOG_TYPE } from '@shell/config/types';
|
|
5
5
|
import { colorForState, stateDisplay } from '@shell/plugins/dashboard-store/resource-class';
|
|
6
6
|
import { _CREATE } from '@shell/config/query-params';
|
|
7
|
+
import { formatDuration } from '@shell/utils/duration';
|
|
7
8
|
|
|
8
9
|
import SteveModel from '@shell/plugins/steve/steve-class';
|
|
10
|
+
import { SUSE_APPCO_DISPLAY_NAME } from '@shell/utils/fleet-appco';
|
|
9
11
|
|
|
10
12
|
export default class ClusterRepo extends SteveModel {
|
|
11
13
|
applyDefaults() {
|
|
@@ -183,7 +185,7 @@ export default class ClusterRepo extends SteveModel {
|
|
|
183
185
|
|
|
184
186
|
get typeDisplay() {
|
|
185
187
|
if (this.isSuseAppCollectionFromUI) {
|
|
186
|
-
return
|
|
188
|
+
return SUSE_APPCO_DISPLAY_NAME;
|
|
187
189
|
}
|
|
188
190
|
if ( this.spec.gitRepo ) {
|
|
189
191
|
return 'git';
|
|
@@ -218,15 +220,37 @@ export default class ClusterRepo extends SteveModel {
|
|
|
218
220
|
return this.spec?.gitBranch || '(default)';
|
|
219
221
|
}
|
|
220
222
|
|
|
223
|
+
get defaultRefreshIntervalHours() {
|
|
224
|
+
return this.isOciType ? 24 : 1;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
get defaultRefreshInterval() {
|
|
228
|
+
return 60 * 60 * this.defaultRefreshIntervalHours;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
get refreshIntervalDisplay() {
|
|
232
|
+
const val = this.spec?.refreshInterval;
|
|
233
|
+
|
|
234
|
+
if (val < 0) {
|
|
235
|
+
return this.t('generic.disabled');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return formatDuration(val ?? this.defaultRefreshInterval);
|
|
239
|
+
}
|
|
240
|
+
|
|
221
241
|
get details() {
|
|
222
242
|
return [
|
|
223
243
|
{
|
|
224
|
-
label: '
|
|
244
|
+
label: this.t('generic.type'),
|
|
225
245
|
content: this.typeDisplay,
|
|
226
246
|
},
|
|
227
247
|
{
|
|
228
|
-
label:
|
|
229
|
-
content:
|
|
248
|
+
label: this.t('catalog.repo.refreshInterval.label'),
|
|
249
|
+
content: this.refreshIntervalDisplay,
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
label: this.t('catalog.repo.downloaded.label'),
|
|
253
|
+
content: this.status?.downloadTime,
|
|
230
254
|
formatter: 'LiveDate',
|
|
231
255
|
formatterOpts: { addSuffix: true },
|
|
232
256
|
},
|
|
@@ -190,20 +190,54 @@ export default class ClusterScan extends SteveModel {
|
|
|
190
190
|
return this.$dispatch('find', { type: COMPLIANCE.BENCHMARK, id: benchmarkId });
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
// Fetches the kube-bench profile ConfigMap referenced by the benchmark and
|
|
194
|
+
// parses its metadata.yaml data key. Top-level fields become the XCCDF
|
|
195
|
+
// benchmark metadata; the checks: map becomes per-check decorations
|
|
196
|
+
// (framework-agnostic; STIG, CIS, BSI, etc. all populate the same shape).
|
|
197
|
+
async _resolveExportMetadata(benchmark) {
|
|
198
|
+
const name = benchmark?.spec?.customBenchmarkConfigMapName;
|
|
199
|
+
const namespace = benchmark?.spec?.customBenchmarkConfigMapNamespace;
|
|
200
|
+
const empty = { metadata: {}, decorations: {} };
|
|
201
|
+
|
|
202
|
+
if (!name || !namespace) {
|
|
203
|
+
return empty;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
const cm = await this.$dispatch('find', { type: 'configmap', id: `${ namespace }/${ name }` });
|
|
208
|
+
const raw = cm?.data?.['metadata.yaml'];
|
|
209
|
+
|
|
210
|
+
if (!raw) {
|
|
211
|
+
return empty;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const jsyaml = await import(/* webpackChunkName: "js-yaml" */'js-yaml');
|
|
215
|
+
const parsed = jsyaml.load(raw) || {};
|
|
216
|
+
const { checks = {}, ...rest } = parsed;
|
|
217
|
+
|
|
218
|
+
return { metadata: rest, decorations: checks };
|
|
219
|
+
} catch (e) {
|
|
220
|
+
console.error('[_resolveExportMetadata] failed to fetch or parse benchmark ConfigMap:', e); // eslint-disable-line no-console
|
|
221
|
+
|
|
222
|
+
return empty;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
193
226
|
async downloadLatestReportXCCDF() {
|
|
194
227
|
const reports = await this.getReports() || [];
|
|
195
228
|
const report = sortBy(reports, 'metadata.creationTimestamp', true)[0];
|
|
196
229
|
|
|
197
230
|
try {
|
|
198
231
|
const benchmark = await this._resolveBenchmark();
|
|
232
|
+
const { metadata, decorations } = await this._resolveExportMetadata(benchmark);
|
|
199
233
|
const { generateXCCDFPerNode } = await import(/* webpackChunkName: "xccdf" */'@shell/utils/xccdf');
|
|
200
234
|
|
|
201
235
|
const parsed = report.parsedReport || {};
|
|
202
236
|
const common = {
|
|
203
237
|
report: parsed,
|
|
204
238
|
benchmarkVersion: parsed.version || benchmark?.spec?.benchmarkVersion || '',
|
|
205
|
-
metadata
|
|
206
|
-
|
|
239
|
+
metadata,
|
|
240
|
+
decorations,
|
|
207
241
|
};
|
|
208
242
|
|
|
209
243
|
const toZip = {};
|
|
@@ -238,6 +272,7 @@ export default class ClusterScan extends SteveModel {
|
|
|
238
272
|
|
|
239
273
|
try {
|
|
240
274
|
const benchmark = await this._resolveBenchmark();
|
|
275
|
+
const { metadata, decorations } = await this._resolveExportMetadata(benchmark);
|
|
241
276
|
const { generateXCCDFPerNode } = await import(/* webpackChunkName: "xccdf" */'@shell/utils/xccdf');
|
|
242
277
|
|
|
243
278
|
const hasParsedNodes = reports.some((report) => Object.entries(report.parsedReport?.nodes || {}).length);
|
|
@@ -251,8 +286,8 @@ export default class ClusterScan extends SteveModel {
|
|
|
251
286
|
const common = {
|
|
252
287
|
report: parsed,
|
|
253
288
|
benchmarkVersion: parsed.version || benchmark?.spec?.benchmarkVersion || '',
|
|
254
|
-
metadata
|
|
255
|
-
|
|
289
|
+
metadata,
|
|
290
|
+
decorations,
|
|
256
291
|
};
|
|
257
292
|
const folder = labelFor(report);
|
|
258
293
|
|
|
@@ -4,10 +4,29 @@ import { set } from '@shell/utils/object';
|
|
|
4
4
|
import { SOURCE_TYPE } from '@shell/config/product/fleet';
|
|
5
5
|
import FleetUtils from '@shell/utils/fleet';
|
|
6
6
|
import { FLEET } from '@shell/config/types';
|
|
7
|
-
import { FLEET as FLEET_ANNOTATIONS } from '@shell/config/labels-annotations';
|
|
7
|
+
import { CATALOG, FLEET as FLEET_ANNOTATIONS } from '@shell/config/labels-annotations';
|
|
8
8
|
import FleetApplication from '@shell/models/fleet-application';
|
|
9
|
+
import { SUSE_APP_COLLECTION_REPO_URL, SUSE_APPCO_DISPLAY_NAME } from '@shell/utils/fleet-appco';
|
|
9
10
|
|
|
10
11
|
export default class HelmOp extends FleetApplication {
|
|
12
|
+
get isSuseAppCollectionFromUI() {
|
|
13
|
+
return !!this.metadata?.annotations?.[CATALOG.SUSE_APP_COLLECTION];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get isSuseAppCollection() {
|
|
17
|
+
// Annotation set by the UI on create, or fallback to URL check for older resources
|
|
18
|
+
return this.isSuseAppCollectionFromUI ||
|
|
19
|
+
(this.spec?.helm?.repo || '').startsWith(SUSE_APP_COLLECTION_REPO_URL);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get applicationType() {
|
|
23
|
+
if (this.isSuseAppCollectionFromUI) {
|
|
24
|
+
return SUSE_APPCO_DISPLAY_NAME;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return this.kind;
|
|
28
|
+
}
|
|
29
|
+
|
|
11
30
|
applyDefaults() {
|
|
12
31
|
const spec = this.spec || {};
|
|
13
32
|
const meta = this.metadata || {};
|