@rancher/shell 3.0.5-rc.5 → 3.0.5-rc.6
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/data/aws-regions.json +1 -0
- package/assets/images/key.svg +17 -0
- package/assets/styles/base/_spacing.scss +2 -2
- package/assets/styles/global/_form.scss +1 -1
- package/assets/styles/global/_labeled-input.scss +1 -1
- package/assets/styles/themes/_dark.scss +3 -0
- package/assets/styles/themes/_light.scss +3 -0
- package/assets/styles/vendor/vue-select.scss +1 -1
- package/assets/translations/en-us.yaml +404 -64
- package/assets/translations/zh-hans.yaml +3 -4
- package/cloud-credential/gcp.vue +9 -1
- package/components/AppModal.vue +2 -0
- package/components/CodeMirror.vue +1 -1
- package/components/ConfigMapSettings/Settings.vue +377 -0
- package/components/ConfigMapSettings/index.vue +354 -0
- package/components/CruResource.vue +1 -2
- package/components/DetailText.vue +61 -11
- package/components/Drawer/Chrome.vue +116 -0
- package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +61 -0
- package/components/Drawer/ResourceDetailDrawer/YamlTab.vue +48 -0
- package/components/Drawer/ResourceDetailDrawer/__tests__/ConfigTab.test.ts +54 -0
- package/components/Drawer/ResourceDetailDrawer/__tests__/YamlTab.test.ts +80 -0
- package/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts +82 -0
- package/components/Drawer/ResourceDetailDrawer/__tests__/helpers.test.ts +42 -0
- package/components/Drawer/ResourceDetailDrawer/composables.ts +50 -0
- package/components/Drawer/ResourceDetailDrawer/helpers.ts +10 -0
- package/components/Drawer/ResourceDetailDrawer/index.vue +110 -0
- package/components/GrowlManager.vue +16 -15
- package/components/IconOrSvg.vue +5 -0
- package/components/KeyValueView.vue +1 -1
- package/components/LocaleSelector.vue +9 -1
- package/components/ProgressBarMulti.vue +1 -0
- package/components/PromptModal.vue +6 -1
- package/components/RelatedResources.vue +4 -12
- package/components/Resource/Detail/Additional.vue +46 -0
- package/components/Resource/Detail/Metadata/Annotations/__tests__/index.test.ts +1 -1
- package/components/Resource/Detail/Metadata/Annotations/index.vue +5 -0
- package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +223 -0
- package/components/Resource/Detail/Metadata/IdentifyingInformation/composable.ts +37 -254
- package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +298 -0
- package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +27 -5
- package/components/Resource/Detail/Metadata/KeyValue.vue +25 -17
- package/components/Resource/Detail/Metadata/Labels/__tests__/index.test.ts +1 -1
- package/components/Resource/Detail/Metadata/Labels/index.vue +4 -0
- package/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts +1 -1
- package/components/Resource/Detail/Metadata/__tests__/Rectangle.test.ts +1 -1
- package/components/Resource/Detail/Metadata/__tests__/composables.test.ts +75 -0
- package/components/Resource/Detail/Metadata/composables.ts +60 -11
- package/components/Resource/Detail/Metadata/index.vue +12 -5
- package/components/Resource/Detail/Page.vue +15 -0
- package/components/Resource/Detail/ResourceRow.vue +37 -18
- package/components/Resource/Detail/ResourceTabs/ConfigMapDataTab/__tests__/composables.test.ts +29 -0
- package/components/Resource/Detail/ResourceTabs/ConfigMapDataTab/__tests__/index.test.ts +48 -0
- package/components/Resource/Detail/ResourceTabs/ConfigMapDataTab/composables.ts +31 -0
- package/components/Resource/Detail/ResourceTabs/ConfigMapDataTab/index.vue +50 -0
- package/components/Resource/Detail/ResourceTabs/KnownHostsTab/__tests__/composables.test.ts +66 -0
- package/components/Resource/Detail/ResourceTabs/KnownHostsTab/composables.ts +21 -0
- package/components/Resource/Detail/ResourceTabs/KnownHostsTab/index.vue +31 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/Basic.vue +45 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/BasicAuth.vue +31 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/Certificate.vue +31 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/Registry.vue +22 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/ServiceAccountToken.vue +31 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/Ssh.vue +32 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/Basic.test.ts +40 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/BasicAuth.test.ts +33 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/Certificate.test.ts +33 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/Registry.test.ts +27 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/ServiceAccountToken.test.ts +33 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/Ssh.test.ts +33 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/auth-types.test.ts +186 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/composables.test.ts +102 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/auth-types.ts +109 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/composeables.ts +52 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/index.vue +71 -0
- package/components/Resource/Detail/TitleBar/Title.vue +2 -1
- package/components/Resource/Detail/TitleBar/__tests__/Title.test.ts +1 -1
- package/components/Resource/Detail/TitleBar/__tests__/Top.test.ts +1 -1
- package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +63 -0
- package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +1 -1
- package/components/Resource/Detail/TitleBar/composables.ts +44 -0
- package/components/Resource/Detail/TitleBar/index.vue +83 -11
- package/components/Resource/Detail/composables.ts +45 -0
- package/components/ResourceDetail/Masthead/__tests__/index.test.ts +70 -0
- package/components/ResourceDetail/{__tests__/Masthead.test.ts → Masthead/__tests__/legacy.test.ts} +3 -3
- package/components/ResourceDetail/Masthead/index.vue +65 -0
- package/components/ResourceDetail/Masthead/latest.vue +44 -0
- package/components/ResourceDetail/__tests__/index.test.ts +26 -5
- package/components/ResourceDetail/index.vue +30 -16
- package/components/ResourceDetail/legacy.vue +18 -1
- package/components/ResourceList/Masthead.vue +6 -0
- package/components/ResourceYaml.vue +14 -1
- package/components/SlideInPanelManager.vue +46 -7
- package/components/StateDot/index.vue +28 -0
- package/components/Tabbed/index.vue +11 -15
- package/components/Wizard.vue +4 -2
- package/components/__tests__/ConfigMapSettings.test.ts +376 -0
- package/components/__tests__/GrowlManager.test.ts +0 -25
- package/components/auth/login/ldap.vue +1 -1
- package/components/fleet/FleetApplications.vue +0 -7
- package/components/fleet/FleetClusterTargets/TargetsList.vue +66 -0
- package/components/fleet/FleetClusterTargets/index.vue +455 -0
- package/components/fleet/FleetClusters.vue +25 -6
- package/components/fleet/FleetGitRepoPaths.vue +476 -0
- package/components/fleet/FleetHelmOps.vue +8 -0
- package/components/fleet/FleetRepos.vue +1 -6
- package/components/fleet/FleetResources.vue +4 -5
- package/components/fleet/FleetValuesFrom.vue +295 -0
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +1224 -0
- package/components/fleet/__tests__/FleetGitRepoPaths.test.ts +265 -0
- package/components/fleet/__tests__/FleetOCIStorageSecret.test.ts +13 -13
- package/components/fleet/__tests__/FleetValuesFrom.test.ts +300 -0
- package/components/fleet/dashboard/ResourceCard.vue +1 -0
- package/components/fleet/dashboard/ResourceCardSummary.vue +1 -5
- package/components/fleet/dashboard/ResourceDetails.vue +8 -10
- package/components/fleet/dashboard/ResourcePanel.vue +15 -8
- package/components/form/ArrayList.vue +13 -2
- package/components/form/ChangePassword.vue +3 -1
- package/components/form/Footer.vue +10 -4
- package/components/form/KeyValue.vue +81 -43
- package/components/form/LabeledSelect.vue +56 -16
- package/components/form/Labels.vue +90 -17
- package/components/form/MatchExpressions.vue +46 -5
- package/components/form/NameNsDescription.vue +1 -1
- package/components/form/ResourceSelector.vue +1 -0
- package/components/form/ResourceTabs/index.vue +5 -0
- package/components/form/SecretSelector.vue +9 -2
- package/components/form/Select.vue +57 -19
- package/components/form/SimpleSecretSelector.vue +9 -2
- package/components/form/Taints.vue +21 -2
- package/components/form/UnitInput.vue +8 -0
- package/components/form/ValueFromResource.vue +1 -1
- package/components/form/__tests__/LabeledSelect.test.ts +8 -4
- package/components/form/__tests__/Labels.test.ts +360 -0
- package/components/form/__tests__/MatchExpressions.test.ts +16 -13
- package/components/form/__tests__/Select.test.ts +5 -2
- package/components/formatter/FleetApplicationSource.vue +1 -1
- package/components/formatter/WorkloadHealthScale.vue +1 -1
- package/components/google/AccountAccess.vue +211 -0
- package/components/google/types/gcp.d.ts +136 -0
- package/components/google/types/index.d.ts +101 -0
- package/components/google/util/__mocks__/gcp.ts +465 -0
- package/components/google/util/formatter.ts +82 -0
- package/components/google/util/gcp.ts +134 -0
- package/components/google/util/index.d.ts +11 -0
- package/components/nav/Favorite.vue +1 -1
- package/components/nav/Group.vue +70 -47
- package/components/nav/Header.vue +5 -1
- package/components/nav/NamespaceFilter.vue +13 -1
- package/components/nav/NotificationCenter/Notification.vue +510 -0
- package/components/nav/NotificationCenter/NotificationHeader.vue +112 -0
- package/components/nav/NotificationCenter/index.vue +148 -0
- package/composables/drawer.ts +26 -0
- package/composables/resources.test.ts +63 -0
- package/composables/resources.ts +38 -0
- package/composables/useIsNewDetailPageEnabled.ts +17 -0
- package/config/labels-annotations.js +6 -0
- package/config/product/auth.js +16 -1
- package/config/product/{cis.js → compliance.js} +23 -26
- package/config/product/explorer.js +5 -1
- package/config/product/fleet.js +7 -0
- package/config/product/settings.js +22 -11
- package/config/query-params.js +3 -0
- package/config/roles.ts +1 -1
- package/config/router/navigation-guards/authentication.js +51 -2
- package/config/router/routes.js +27 -31
- package/config/settings.ts +21 -3
- package/config/store.js +2 -0
- package/config/system-namespaces.js +1 -1
- package/config/table-headers.js +2 -2
- package/config/types.js +15 -6
- package/core/plugin.ts +32 -7
- package/core/types.ts +18 -1
- package/detail/{cis.cattle.io.clusterscan.vue → compliance.cattle.io.clusterscan.vue} +22 -18
- package/detail/management.cattle.io.fleetworkspace.vue +18 -27
- package/detail/management.cattle.io.oidcclient.vue +369 -0
- package/detail/node.vue +2 -2
- package/detail/pod.vue +2 -2
- package/detail/service.vue +10 -1
- package/detail/workload/index.vue +8 -2
- package/dialog/ExtensionCatalogUninstallDialog.vue +7 -4
- package/dialog/GenericPrompt.vue +1 -1
- package/dialog/ImportDialog.vue +8 -8
- package/dialog/OidcClientSecretDialog.vue +117 -0
- package/edit/__tests__/cis.cattle.io.clusterscan.test.ts +3 -3
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +5 -2
- package/edit/autoscaling.horizontalpodautoscaler/index.vue +4 -1
- package/edit/{cis.cattle.io.clusterscan.vue → compliance.cattle.io.clusterscan.vue} +30 -31
- package/edit/{cis.cattle.io.clusterscanbenchmark.vue → compliance.cattle.io.clusterscanbenchmark.vue} +4 -4
- package/edit/{cis.cattle.io.clusterscanprofile.vue → compliance.cattle.io.clusterscanprofile.vue} +5 -5
- package/edit/configmap.vue +4 -1
- package/edit/constraints.gatekeeper.sh.constraint/index.vue +1 -0
- package/edit/fleet.cattle.io.gitrepo.vue +44 -222
- package/edit/fleet.cattle.io.helmop.vue +44 -269
- package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
- package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +1 -0
- package/edit/logging-flow/index.vue +1 -0
- package/edit/logging.banzaicloud.io.output/index.vue +1 -0
- package/edit/management.cattle.io.fleetworkspace.vue +1 -0
- package/edit/management.cattle.io.oidcclient.vue +162 -0
- package/edit/management.cattle.io.project.vue +4 -1
- package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +1 -1
- package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +5 -0
- package/edit/monitoring.coreos.com.prometheusrule/index.vue +1 -0
- package/edit/monitoring.coreos.com.receiver/auth.vue +30 -30
- package/edit/monitoring.coreos.com.receiver/index.vue +1 -0
- package/edit/monitoring.coreos.com.receiver/types/email.vue +1 -1
- package/edit/monitoring.coreos.com.route.vue +1 -0
- package/edit/namespace.vue +1 -0
- package/edit/networking.istio.io.destinationrule/index.vue +4 -1
- package/edit/networking.k8s.io.ingress/index.vue +4 -1
- package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +7 -2
- package/edit/networking.k8s.io.networkpolicy/index.vue +6 -2
- package/edit/node.vue +1 -0
- package/edit/persistentvolume/index.vue +4 -1
- package/edit/provisioning.cattle.io.cluster/rke2.vue +418 -382
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +27 -27
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +5 -0
- package/edit/resources.cattle.io.restore.vue +1 -1
- package/edit/secret/index.vue +1 -0
- package/edit/service.vue +4 -1
- package/edit/serviceaccount.vue +4 -1
- package/edit/storage.k8s.io.storageclass/index.vue +4 -1
- package/edit/workload/index.vue +5 -0
- package/list/{cis.cattle.io.clusterscan.vue → compliance.cattle.io.clusterscan.vue} +2 -2
- package/list/management.cattle.io.oidcclient.vue +108 -0
- package/list/node.vue +2 -0
- package/machine-config/amazonec2.vue +3 -24
- package/machine-config/components/GCEImage.vue +374 -0
- package/machine-config/google.vue +617 -0
- package/mixins/__tests__/brand.spec.ts +170 -0
- package/mixins/brand.js +16 -17
- package/mixins/create-edit-view/index.js +5 -0
- package/mixins/resource-fetch-api-pagination.js +16 -0
- package/mixins/vue-select-overrides.js +1 -0
- package/models/{cis.cattle.io.clusterscan.js → compliance.cattle.io.clusterscan.js} +8 -8
- package/models/{cis.cattle.io.clusterscanbenchmark.js → compliance.cattle.io.clusterscanbenchmark.js} +1 -1
- package/models/{cis.cattle.io.clusterscanprofile.js → compliance.cattle.io.clusterscanprofile.js} +5 -5
- package/models/{cis.cattle.io.clusterscanreport.js → compliance.cattle.io.clusterscanreport.js} +1 -1
- package/models/fleet-application.js +8 -79
- package/models/fleet.cattle.io.cluster.js +11 -0
- package/models/fleet.cattle.io.gitrepo.js +2 -2
- package/models/fleet.cattle.io.helmop.js +9 -39
- package/models/management.cattle.io.fleetworkspace.js +2 -1
- package/models/management.cattle.io.oidcclient.js +18 -0
- package/models/management.cattle.io.registration.js +3 -0
- package/models/provisioning.cattle.io.cluster.js +5 -5
- package/models/service.js +4 -0
- package/models/workload.js +5 -0
- package/package.json +1 -1
- package/pages/about.vue +4 -58
- package/pages/auth/login.vue +1 -1
- package/pages/c/_cluster/apps/charts/AddRepoLink.vue +0 -1
- package/pages/c/_cluster/apps/charts/index.vue +285 -81
- package/pages/c/_cluster/auth/user.retention/index.vue +87 -78
- package/pages/c/_cluster/explorer/index.vue +3 -3
- package/pages/c/_cluster/explorer/tools/pages/_page.vue +0 -1
- package/pages/c/_cluster/fleet/application/create.vue +3 -2
- package/pages/c/_cluster/fleet/index.vue +94 -56
- package/pages/c/_cluster/fleet/settings/index.vue +229 -0
- package/pages/c/_cluster/longhorn/index.vue +5 -2
- package/pages/c/_cluster/uiplugins/CatalogList/index.vue +16 -1
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +2 -2
- package/pages/explorer/resource/detail/configmap.vue +30 -7
- package/pages/explorer/resource/detail/secret.vue +50 -0
- package/pages/home.vue +9 -55
- package/pages/support/index.vue +4 -6
- package/plugins/dashboard-store/actions.js +19 -5
- package/plugins/dashboard-store/getters.js +4 -0
- package/plugins/dashboard-store/resource-class.js +16 -2
- package/plugins/steve/steve-pagination-utils.ts +26 -18
- package/plugins/steve/subscribe.js +6 -1
- package/rancher-components/Banner/Banner.vue +13 -0
- package/rancher-components/Form/Checkbox/Checkbox.vue +9 -4
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +1 -1
- package/rancher-components/LabeledTooltip/LabeledTooltip.vue +1 -0
- package/rancher-components/RcItemCard/RcItemCard.vue +8 -3
- package/store/auth.js +2 -0
- package/store/catalog.js +23 -1
- package/store/growl.js +97 -8
- package/store/index.js +6 -0
- package/store/notifications.ts +426 -0
- package/store/prefs.js +0 -1
- package/store/type-map.js +19 -16
- package/store/uiplugins.ts +15 -1
- package/types/fleet.d.ts +24 -0
- package/types/notifications/index.ts +74 -0
- package/types/shell/index.d.ts +46 -32
- package/types/store/dashboard-store.types.ts +16 -0
- package/utils/__tests__/fleet.test.ts +148 -0
- package/utils/__tests__/object.test.ts +54 -1
- package/utils/__tests__/string.test.ts +273 -1
- package/utils/__tests__/time.test.ts +31 -0
- package/utils/auth.js +9 -2
- package/utils/crypto/encryption.ts +103 -0
- package/utils/cspAdaptor.ts +51 -0
- package/utils/fleet.ts +54 -65
- package/utils/object.js +36 -0
- package/utils/pagination-utils.ts +1 -1
- package/utils/release-notes.ts +48 -0
- package/utils/selector-typed.ts +7 -2
- package/utils/string.js +24 -0
- package/utils/{time.js → time.ts} +25 -6
- package/utils/uiplugins.ts +22 -0
- package/utils/validators/formRules/index.ts +3 -0
- package/components/Resource/Detail/TitleBar/composable.ts +0 -31
- package/config/product/legacy.js +0 -62
- package/pages/c/_cluster/legacy/pages/_page.vue +0 -29
- package/pages/c/_cluster/legacy/project/_page.vue +0 -57
- package/pages/c/_cluster/legacy/project/index.vue +0 -32
- package/pages/c/_cluster/legacy/project/pipelines.vue +0 -96
- /package/components/ResourceDetail/{Masthead.vue → Masthead/legacy.vue} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { decodeHtml, resourceNames } from '@shell/utils/string';
|
|
1
|
+
import { decodeHtml, resourceNames, pathArrayToTree } from '@shell/utils/string';
|
|
2
2
|
|
|
3
3
|
describe('fx: decodeHtml', () => {
|
|
4
4
|
it('should decode HTML values from escaped string into valid markup', () => {
|
|
@@ -89,3 +89,275 @@ describe('fx: resourceNames', () => {
|
|
|
89
89
|
});
|
|
90
90
|
});
|
|
91
91
|
});
|
|
92
|
+
|
|
93
|
+
describe('fx: pathArrayToTree', () => {
|
|
94
|
+
interface TreeNode {
|
|
95
|
+
name: string;
|
|
96
|
+
children: TreeNode[];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function sortTree(nodes: TreeNode[]): void {
|
|
100
|
+
if (!nodes) return;
|
|
101
|
+
nodes.sort((a, b) => a.name.localeCompare(b.name));
|
|
102
|
+
nodes.forEach((node) => {
|
|
103
|
+
if (node.children && node.children.length > 0) {
|
|
104
|
+
sortTree(node.children);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
it('should return an empty array for an empty input array', () => {
|
|
110
|
+
const paths: string[] = [];
|
|
111
|
+
|
|
112
|
+
expect(pathArrayToTree(paths)).toStrictEqual([]);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should convert simple single-level paths correctly', () => {
|
|
116
|
+
const paths: string[] = [
|
|
117
|
+
'file1.txt',
|
|
118
|
+
'folderA',
|
|
119
|
+
'itemB'
|
|
120
|
+
];
|
|
121
|
+
const expected: TreeNode[] = [
|
|
122
|
+
{ name: 'file1.txt', children: [] },
|
|
123
|
+
{ name: 'folderA', children: [] },
|
|
124
|
+
{ name: 'itemB', children: [] },
|
|
125
|
+
];
|
|
126
|
+
const actual = pathArrayToTree(paths);
|
|
127
|
+
|
|
128
|
+
sortTree(actual); // Sort the actual output for deterministic comparison
|
|
129
|
+
expect(actual).toStrictEqual(expected);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should convert two-level nested paths correctly', () => {
|
|
133
|
+
const paths: string[] = [
|
|
134
|
+
'folderA/file1.txt',
|
|
135
|
+
'folderC/file3.txt'
|
|
136
|
+
];
|
|
137
|
+
const expected: TreeNode[] = [
|
|
138
|
+
{
|
|
139
|
+
name: 'folderA',
|
|
140
|
+
children: [
|
|
141
|
+
{ name: 'file1.txt', children: [] },
|
|
142
|
+
]
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: 'folderC',
|
|
146
|
+
children: [
|
|
147
|
+
{ name: 'file3.txt', children: [] }
|
|
148
|
+
]
|
|
149
|
+
},
|
|
150
|
+
];
|
|
151
|
+
const actual = pathArrayToTree(paths);
|
|
152
|
+
|
|
153
|
+
sortTree(actual);
|
|
154
|
+
expect(actual).toStrictEqual(expected);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('should handle deep nesting and common prefixes correctly', () => {
|
|
158
|
+
const paths: string[] = [
|
|
159
|
+
'root/level1/level2/file.doc',
|
|
160
|
+
'root/level1/another_file.txt',
|
|
161
|
+
'root/diff_level1/config.json'
|
|
162
|
+
];
|
|
163
|
+
const expected: TreeNode[] = [
|
|
164
|
+
{
|
|
165
|
+
name: 'root',
|
|
166
|
+
children: [
|
|
167
|
+
{
|
|
168
|
+
name: 'diff_level1',
|
|
169
|
+
children: [
|
|
170
|
+
{ name: 'config.json', children: [] }
|
|
171
|
+
]
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: 'level1',
|
|
175
|
+
children: [
|
|
176
|
+
{ name: 'another_file.txt', children: [] },
|
|
177
|
+
{
|
|
178
|
+
name: 'level2',
|
|
179
|
+
children: [
|
|
180
|
+
{ name: 'file.doc', children: [] }
|
|
181
|
+
]
|
|
182
|
+
}
|
|
183
|
+
]
|
|
184
|
+
}
|
|
185
|
+
]
|
|
186
|
+
}
|
|
187
|
+
];
|
|
188
|
+
const actual = pathArrayToTree(paths);
|
|
189
|
+
|
|
190
|
+
sortTree(actual);
|
|
191
|
+
expect(actual).toStrictEqual(expected);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('should handle leading/trailing/multiple slashes by treating them as path segments', () => {
|
|
195
|
+
const paths: string[] = [
|
|
196
|
+
'/path/to/file.txt', // Starts with a slash
|
|
197
|
+
'path/to/another.txt/', // Ends with a slash (adds an empty segment)
|
|
198
|
+
'//root/item//subitem//', // Multiple slashes (adds empty segments)
|
|
199
|
+
'root/item/another_subitem'
|
|
200
|
+
];
|
|
201
|
+
const expected: TreeNode[] = [
|
|
202
|
+
{
|
|
203
|
+
name: '',
|
|
204
|
+
children: [
|
|
205
|
+
{
|
|
206
|
+
name: '',
|
|
207
|
+
children: [
|
|
208
|
+
{
|
|
209
|
+
name: 'root',
|
|
210
|
+
children: [
|
|
211
|
+
{
|
|
212
|
+
name: 'item',
|
|
213
|
+
children: [
|
|
214
|
+
{
|
|
215
|
+
name: '',
|
|
216
|
+
children: [
|
|
217
|
+
{
|
|
218
|
+
name: 'subitem',
|
|
219
|
+
children: [
|
|
220
|
+
{
|
|
221
|
+
name: '',
|
|
222
|
+
children: [
|
|
223
|
+
{
|
|
224
|
+
name: '',
|
|
225
|
+
children: [
|
|
226
|
+
],
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
},
|
|
234
|
+
],
|
|
235
|
+
},
|
|
236
|
+
],
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
name: 'path',
|
|
242
|
+
children: [
|
|
243
|
+
{
|
|
244
|
+
name: 'to',
|
|
245
|
+
children: [
|
|
246
|
+
{
|
|
247
|
+
name: 'file.txt',
|
|
248
|
+
children: [
|
|
249
|
+
],
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
},
|
|
253
|
+
],
|
|
254
|
+
},
|
|
255
|
+
],
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
name: 'path',
|
|
259
|
+
children: [
|
|
260
|
+
{
|
|
261
|
+
name: 'to',
|
|
262
|
+
children: [
|
|
263
|
+
{
|
|
264
|
+
name: 'another.txt',
|
|
265
|
+
children: [
|
|
266
|
+
{
|
|
267
|
+
name: '',
|
|
268
|
+
children: [
|
|
269
|
+
],
|
|
270
|
+
},
|
|
271
|
+
],
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
},
|
|
275
|
+
],
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
name: 'root',
|
|
279
|
+
children: [
|
|
280
|
+
{
|
|
281
|
+
name: 'item',
|
|
282
|
+
children: [
|
|
283
|
+
{
|
|
284
|
+
name: 'another_subitem',
|
|
285
|
+
children: [
|
|
286
|
+
],
|
|
287
|
+
},
|
|
288
|
+
],
|
|
289
|
+
},
|
|
290
|
+
],
|
|
291
|
+
},
|
|
292
|
+
];
|
|
293
|
+
const actual = pathArrayToTree(paths);
|
|
294
|
+
|
|
295
|
+
sortTree(actual);
|
|
296
|
+
expect(actual).toStrictEqual(expected);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('should produce the same structure regardless of input order', () => {
|
|
300
|
+
const paths1: string[] = ['a/b', 'a/c'];
|
|
301
|
+
const paths2: string[] = ['a/c', 'a/b'];
|
|
302
|
+
|
|
303
|
+
const actual1 = pathArrayToTree(paths1);
|
|
304
|
+
|
|
305
|
+
sortTree(actual1);
|
|
306
|
+
const actual2 = pathArrayToTree(paths2);
|
|
307
|
+
|
|
308
|
+
sortTree(actual2);
|
|
309
|
+
|
|
310
|
+
expect(actual1).toStrictEqual(actual2);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it('should handle names with special characters (not slashes) correctly', () => {
|
|
314
|
+
const paths: string[] = [
|
|
315
|
+
'my-folder/file_name.1.txt',
|
|
316
|
+
'another folder/item with spaces'
|
|
317
|
+
];
|
|
318
|
+
const expected: TreeNode[] = [
|
|
319
|
+
{
|
|
320
|
+
name: 'another folder',
|
|
321
|
+
children: [
|
|
322
|
+
{ name: 'item with spaces', children: [] }
|
|
323
|
+
]
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
name: 'my-folder',
|
|
327
|
+
children: [
|
|
328
|
+
{ name: 'file_name.1.txt', children: [] }
|
|
329
|
+
]
|
|
330
|
+
}
|
|
331
|
+
];
|
|
332
|
+
const actual = pathArrayToTree(paths);
|
|
333
|
+
|
|
334
|
+
sortTree(actual);
|
|
335
|
+
expect(actual).toStrictEqual(expected);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it('should handle duplicate paths correctly, only adding unique structure', () => {
|
|
339
|
+
const paths: string[] = [
|
|
340
|
+
'folder/file.txt',
|
|
341
|
+
'folder/file.txt', // Duplicate
|
|
342
|
+
'other_folder/another_file.txt'
|
|
343
|
+
];
|
|
344
|
+
const expected: TreeNode[] = [
|
|
345
|
+
{
|
|
346
|
+
name: 'folder',
|
|
347
|
+
children: [
|
|
348
|
+
{ name: 'file.txt', children: [] }
|
|
349
|
+
]
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
name: 'other_folder',
|
|
353
|
+
children: [
|
|
354
|
+
{ name: 'another_file.txt', children: [] }
|
|
355
|
+
]
|
|
356
|
+
}
|
|
357
|
+
];
|
|
358
|
+
const actual = pathArrayToTree(paths);
|
|
359
|
+
|
|
360
|
+
sortTree(actual);
|
|
361
|
+
expect(actual).toStrictEqual(expected);
|
|
362
|
+
});
|
|
363
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { DATE_FORMAT, TIME_FORMAT } from '@shell/store/prefs';
|
|
2
|
+
import { dateTimeFormat } from '@shell/utils/time';
|
|
3
|
+
import { type Store } from 'vuex';
|
|
4
|
+
|
|
5
|
+
describe('function: dateTimeFormat', () => {
|
|
6
|
+
jest.useFakeTimers()
|
|
7
|
+
.setSystemTime(new Date('21-10-2010 4:29:00'));
|
|
8
|
+
const store = {
|
|
9
|
+
getters: {
|
|
10
|
+
'prefs/get': (key: string) => {
|
|
11
|
+
return {
|
|
12
|
+
[DATE_FORMAT]: 'ddd, MMM D YYYY',
|
|
13
|
+
[TIME_FORMAT]: 'h:mm:ss a',
|
|
14
|
+
}[key];
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
} as Store<any>;
|
|
18
|
+
|
|
19
|
+
it('should format date and time correctly', () => {
|
|
20
|
+
const date = new Date('2010-10-21T04:29:00Z');
|
|
21
|
+
const formattedDate = dateTimeFormat(date.toISOString(), store);
|
|
22
|
+
|
|
23
|
+
expect(formattedDate).toBe('Thu, Oct 21 2010 4:29:00 am');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should return empty string for undefined value', () => {
|
|
27
|
+
const formattedDate = dateTimeFormat(undefined, store);
|
|
28
|
+
|
|
29
|
+
expect(formattedDate).toBe('');
|
|
30
|
+
});
|
|
31
|
+
});
|
package/utils/auth.js
CHANGED
|
@@ -8,6 +8,7 @@ import { allHash } from '@shell/utils/promise';
|
|
|
8
8
|
import { getProductFromRoute, getResourceFromRoute } from '@shell/utils/router';
|
|
9
9
|
import { NAME as EXPLORER } from '@shell/config/product/explorer';
|
|
10
10
|
import { findBy } from '@shell/utils/array';
|
|
11
|
+
import { onExtensionsReady } from '@shell/utils/uiplugins';
|
|
11
12
|
|
|
12
13
|
export const AUTH_BROADCAST_CHANNEL_NAME = 'rancher-auth-test-callback';
|
|
13
14
|
|
|
@@ -333,9 +334,15 @@ export async function tryInitialSetup(store, password = 'admin') {
|
|
|
333
334
|
/**
|
|
334
335
|
* Record in our state management that we're indeed logged in
|
|
335
336
|
*/
|
|
336
|
-
export function isLoggedIn(store,
|
|
337
|
+
export async function isLoggedIn(store, userData) {
|
|
337
338
|
store.commit('auth/hasAuth', true);
|
|
338
|
-
store.commit('auth/loggedInAs',
|
|
339
|
+
store.commit('auth/loggedInAs', userData.id);
|
|
340
|
+
|
|
341
|
+
// Init the notification center now that we know who the user is
|
|
342
|
+
await store.dispatch('notifications/init', userData);
|
|
343
|
+
|
|
344
|
+
// Let the extensions know that the user is logged in
|
|
345
|
+
await onExtensionsReady(store);
|
|
339
346
|
}
|
|
340
347
|
|
|
341
348
|
/**
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for string encryption and decryption
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// We just use a static salt
|
|
6
|
+
const SALT = new TextEncoder().encode('rancher-dashboard');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Type for the result of encryption
|
|
10
|
+
*/
|
|
11
|
+
export type EncryptedString = {
|
|
12
|
+
cipher: string;
|
|
13
|
+
iv: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Generate an encryption key from a password
|
|
18
|
+
*
|
|
19
|
+
* @param password Password to use for the key
|
|
20
|
+
* @returns Encryption key
|
|
21
|
+
*/
|
|
22
|
+
export async function deriveKey(password: string): Promise<CryptoKey> {
|
|
23
|
+
const passwordBytes = stringToBytes(password);
|
|
24
|
+
|
|
25
|
+
const initialKey = await crypto.subtle.importKey(
|
|
26
|
+
'raw',
|
|
27
|
+
passwordBytes,
|
|
28
|
+
{ name: 'PBKDF2' },
|
|
29
|
+
false,
|
|
30
|
+
['deriveKey']
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
return await crypto.subtle.deriveKey(
|
|
34
|
+
{
|
|
35
|
+
name: 'PBKDF2',
|
|
36
|
+
salt: SALT,
|
|
37
|
+
iterations: 100000,
|
|
38
|
+
hash: 'SHA-256'
|
|
39
|
+
},
|
|
40
|
+
initialKey,
|
|
41
|
+
{
|
|
42
|
+
name: 'AES-GCM',
|
|
43
|
+
length: 256
|
|
44
|
+
},
|
|
45
|
+
false,
|
|
46
|
+
[
|
|
47
|
+
'encrypt',
|
|
48
|
+
'decrypt'
|
|
49
|
+
]
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Encrypt data
|
|
55
|
+
*
|
|
56
|
+
* @param content String to be encrypted
|
|
57
|
+
* @param key Encryption key
|
|
58
|
+
* @returns Encrypted data
|
|
59
|
+
*/
|
|
60
|
+
export async function encrypt(content: string, key: CryptoKey): Promise<EncryptedString> {
|
|
61
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
62
|
+
const contentBytes = stringToBytes(content);
|
|
63
|
+
const cipher = new Uint8Array(
|
|
64
|
+
await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, contentBytes)
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
iv: bytesToBase64(iv),
|
|
69
|
+
cipher: bytesToBase64(cipher),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Decrypt encrypted data
|
|
75
|
+
* @param encryptedData Encrypted data with `cipher` and `iv` field
|
|
76
|
+
* @param key Key to use for decryption
|
|
77
|
+
* @returns Decrypted value as a string
|
|
78
|
+
*/
|
|
79
|
+
export async function decrypt(encryptedData: EncryptedString, key: CryptoKey): Promise<string> {
|
|
80
|
+
const iv = base64ToBytes(encryptedData.iv);
|
|
81
|
+
const cipher = base64ToBytes(encryptedData.cipher);
|
|
82
|
+
const contentBytes = new Uint8Array(
|
|
83
|
+
await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, cipher)
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
return bytesToString(contentBytes);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function bytesToString(bytes: any): string {
|
|
90
|
+
return new TextDecoder().decode(bytes);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function stringToBytes(str: string): Uint8Array {
|
|
94
|
+
return new TextEncoder().encode(str);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function bytesToBase64(arr: Uint8Array) {
|
|
98
|
+
return btoa(Array.from(arr, (b: number) => String.fromCharCode(b)).join(''));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function base64ToBytes(base64: string): Uint8Array {
|
|
102
|
+
return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
|
|
103
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// For testing these could be changed to something like...
|
|
2
|
+
|
|
3
|
+
import { CATALOG } from '@shell/config/types';
|
|
4
|
+
import { FilterArgs, PaginationFilterField, PaginationParamFilter } from '@shell/types/store/pagination.types';
|
|
5
|
+
import { VuexStore } from '@shell/types/store/vuex';
|
|
6
|
+
|
|
7
|
+
const CSP_ADAPTER_APPS = ['rancher-csp-adapter', 'rancher-csp-billing-adapter'];
|
|
8
|
+
// For testing above line could be replaced with below line...
|
|
9
|
+
// const cspAdaptorApp = ['rancher-webhooka', 'rancher-webhook'];
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Helpers in order to
|
|
13
|
+
*/
|
|
14
|
+
class CspAdapterUtils {
|
|
15
|
+
static canPagination($store: VuexStore): boolean {
|
|
16
|
+
return $store.getters[`management/paginationEnabled`]({ id: CATALOG.APP, context: 'branding' });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static fetchCspAdaptorApp($store: VuexStore): Promise<any> {
|
|
20
|
+
// For the login page, the schemas won't be loaded - we don't need the apps in this case
|
|
21
|
+
if ($store.getters['management/canList'](CATALOG.APP)) {
|
|
22
|
+
if (CspAdapterUtils.canPagination($store)) {
|
|
23
|
+
// Restrict the amount of apps we need to fetch
|
|
24
|
+
return $store.dispatch('management/findPage', {
|
|
25
|
+
type: CATALOG.APP,
|
|
26
|
+
opt: { // Of type ActionFindPageArgs
|
|
27
|
+
pagination: new FilterArgs({
|
|
28
|
+
filters: PaginationParamFilter.createMultipleFields(CSP_ADAPTER_APPS.map(
|
|
29
|
+
(t) => new PaginationFilterField({
|
|
30
|
+
field: 'metadata.name',
|
|
31
|
+
value: t,
|
|
32
|
+
})
|
|
33
|
+
)),
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return $store.dispatch('management/findAll', { type: CATALOG.APP });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return Promise.resolve([]);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static hasCspAdapter({ $store, apps }: { $store: VuexStore, apps: any[]}): Object {
|
|
46
|
+
// In theory this should contain the filtered apps when pagination is on, and all apps when off. Keep filtering though in both cases just in case
|
|
47
|
+
return apps?.find((a) => CSP_ADAPTER_APPS.includes(a.metadata?.name));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default CspAdapterUtils;
|
package/utils/fleet.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isEmpty, isEqual } from 'lodash';
|
|
1
2
|
import {
|
|
2
3
|
BundleDeploymentResource,
|
|
3
4
|
BundleResourceKey,
|
|
@@ -6,10 +7,10 @@ import {
|
|
|
6
7
|
Condition,
|
|
7
8
|
} from '@shell/types/resources/fleet';
|
|
8
9
|
import { mapStateToEnum, STATES_ENUM, STATES } from '@shell/plugins/dashboard-store/resource-class';
|
|
9
|
-
import { FLEET as FLEET_LABELS } from '@shell/config/labels-annotations';
|
|
10
|
+
import { FLEET as FLEET_LABELS, CAPI } from '@shell/config/labels-annotations';
|
|
10
11
|
import { NAME as EXPLORER_NAME } from '@shell/config/product/explorer';
|
|
11
|
-
import { FleetDashboardState, FleetResourceState } from '@shell/types/fleet';
|
|
12
|
-
import { FLEET } from '@shell/config/types';
|
|
12
|
+
import { FleetDashboardState, FleetResourceState, Target, TargetMode } from '@shell/types/fleet';
|
|
13
|
+
import { FLEET, VIRTUAL_HARVESTER_PROVIDER } from '@shell/config/types';
|
|
13
14
|
|
|
14
15
|
interface Resource extends BundleDeploymentResource {
|
|
15
16
|
state: string,
|
|
@@ -19,17 +20,6 @@ type Labels = {
|
|
|
19
20
|
[key: string]: string,
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
interface KeyRef {
|
|
23
|
-
key: string;
|
|
24
|
-
name: string;
|
|
25
|
-
namespace?: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
interface ValueFrom {
|
|
29
|
-
configMapKeyRef?: KeyRef;
|
|
30
|
-
secretKeyRef?: KeyRef;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
23
|
function resourceKey(r: BundleResourceKey): string {
|
|
34
24
|
return `${ r.kind }/${ r.namespace }/${ r.name }`;
|
|
35
25
|
}
|
|
@@ -42,64 +32,63 @@ function conditionIsTrue(conditions: Condition[] | undefined, type: string): boo
|
|
|
42
32
|
return !!conditions.find((c) => c.type === type && c.status.toLowerCase() === 'true');
|
|
43
33
|
}
|
|
44
34
|
|
|
45
|
-
class
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
35
|
+
class Application {
|
|
36
|
+
excludeHarvesterRule = {
|
|
37
|
+
clusterSelector: {
|
|
38
|
+
matchExpressions: [{
|
|
39
|
+
key: CAPI.PROVIDER,
|
|
40
|
+
operator: 'NotIn',
|
|
41
|
+
values: [
|
|
42
|
+
VIRTUAL_HARVESTER_PROVIDER
|
|
43
|
+
],
|
|
44
|
+
}],
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
getTargetMode(targets: Target[], namespace: string): TargetMode {
|
|
49
|
+
if (namespace === 'fleet-local') {
|
|
50
|
+
return 'local';
|
|
51
|
+
}
|
|
49
52
|
|
|
50
|
-
|
|
53
|
+
if (!targets.length) {
|
|
54
|
+
return 'none';
|
|
55
|
+
}
|
|
51
56
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
let mode: TargetMode = 'all';
|
|
58
|
+
|
|
59
|
+
for (const target of targets) {
|
|
60
|
+
const {
|
|
61
|
+
clusterName,
|
|
62
|
+
clusterSelector,
|
|
63
|
+
clusterGroup,
|
|
64
|
+
clusterGroupSelector,
|
|
65
|
+
} = target;
|
|
66
|
+
|
|
67
|
+
if (clusterGroup || clusterGroupSelector) {
|
|
68
|
+
return 'advanced';
|
|
59
69
|
}
|
|
60
70
|
|
|
61
|
-
|
|
71
|
+
if (clusterName) {
|
|
72
|
+
mode = 'clusters';
|
|
73
|
+
}
|
|
62
74
|
|
|
63
|
-
if (
|
|
64
|
-
|
|
65
|
-
secretKeyRef: {
|
|
66
|
-
key: sc.key || '',
|
|
67
|
-
name: sc.name || '',
|
|
68
|
-
}
|
|
69
|
-
};
|
|
75
|
+
if (!isEmpty(clusterSelector)) {
|
|
76
|
+
mode = 'clusters';
|
|
70
77
|
}
|
|
78
|
+
}
|
|
71
79
|
|
|
72
|
-
|
|
80
|
+
const normalized = [...targets].map((target) => {
|
|
81
|
+
delete target.name;
|
|
82
|
+
|
|
83
|
+
return target;
|
|
73
84
|
});
|
|
74
|
-
}
|
|
75
85
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const out = {} as ValueFrom;
|
|
84
|
-
|
|
85
|
-
if (cm?.name) {
|
|
86
|
-
out.configMapKeyRef = {
|
|
87
|
-
key: cm.key,
|
|
88
|
-
name: cm.name,
|
|
89
|
-
namespace
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (sc?.name) {
|
|
94
|
-
out.secretKeyRef = {
|
|
95
|
-
key: sc.key,
|
|
96
|
-
name: sc.name,
|
|
97
|
-
namespace
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return out;
|
|
102
|
-
});
|
|
86
|
+
// Check if targets contains only harvester rule after name normalizing
|
|
87
|
+
if (isEqual(normalized, [this.excludeHarvesterRule])) {
|
|
88
|
+
mode = 'all';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return mode;
|
|
103
92
|
}
|
|
104
93
|
}
|
|
105
94
|
|
|
@@ -142,14 +131,14 @@ class Fleet {
|
|
|
142
131
|
{
|
|
143
132
|
index: 3,
|
|
144
133
|
id: 'info',
|
|
145
|
-
label: '
|
|
134
|
+
label: 'Pending',
|
|
146
135
|
color: '#3d98d3',
|
|
147
136
|
icon: 'icon icon-warning',
|
|
148
137
|
stateBackground: 'bg-info'
|
|
149
138
|
},
|
|
150
139
|
];
|
|
151
140
|
|
|
152
|
-
|
|
141
|
+
Application = new Application();
|
|
153
142
|
|
|
154
143
|
GIT_HTTPS_REGEX = /^https?:\/\/github\.com\/(.*?)(\.git)?\/*$/;
|
|
155
144
|
GIT_SSH_REGEX = /^git@github\.com:.*\.git$/;
|
package/utils/object.js
CHANGED
|
@@ -527,3 +527,39 @@ export function mergeWithReplace(
|
|
|
527
527
|
}
|
|
528
528
|
});
|
|
529
529
|
}
|
|
530
|
+
/**
|
|
531
|
+
* Converts Object into a string of a format "key1, val1, key2, val2"
|
|
532
|
+
* @param {Object} input - KV object to convert
|
|
533
|
+
* @returns string
|
|
534
|
+
*/
|
|
535
|
+
export const convertKVToString = (input) => {
|
|
536
|
+
if (!input || typeof input !== 'object') return '';
|
|
537
|
+
|
|
538
|
+
return Object.entries(input)
|
|
539
|
+
.flatMap(([key, value]) => [key, String(value)])
|
|
540
|
+
.join(',');
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Converts kv string into object
|
|
545
|
+
* @param {string} input - a string of a format "key1, val1, key2, val2"
|
|
546
|
+
* @returns
|
|
547
|
+
*/
|
|
548
|
+
export const convertStringToKV = ( input ) => {
|
|
549
|
+
if (!input?.trim()) return {};
|
|
550
|
+
|
|
551
|
+
const parts = input.split(',').map((part) => part.trim());
|
|
552
|
+
const result = {};
|
|
553
|
+
|
|
554
|
+
for (let i = 0; i < parts.length - 1; i += 2) {
|
|
555
|
+
const key = parts[i];
|
|
556
|
+
const value = parts[i + 1];
|
|
557
|
+
|
|
558
|
+
// Accept empty keys but ignore undefined values
|
|
559
|
+
if (typeof key === 'string') {
|
|
560
|
+
result[key] = value ?? '';
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
return result;
|
|
565
|
+
};
|