@rancher/shell 3.0.8 → 3.0.9-rc.2
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/intf/modal.ts +38 -0
- package/apis/intf/slide-in.ts +3 -1
- package/apis/shell/__tests__/slide-in.test.ts +36 -0
- package/apis/shell/slide-in.ts +5 -1
- package/assets/styles/base/_color.scss +1 -0
- package/assets/styles/base/_typography.scss +14 -5
- package/assets/styles/themes/_light.scss +1 -1
- package/assets/styles/themes/_modern.scss +1 -1
- package/assets/translations/en-us.yaml +94 -33
- package/assets/translations/zh-hans.yaml +0 -2
- package/components/ActionMenuShell.vue +4 -4
- package/components/CodeMirror.vue +4 -3
- package/components/DetailText.vue +54 -7
- package/components/Drawer/Chrome.vue +11 -4
- package/components/Drawer/DrawerCard.vue +19 -0
- package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +3 -11
- package/components/Drawer/ResourceDetailDrawer/__tests__/ConfigTab.test.ts +2 -2
- package/components/Drawer/ResourceDetailDrawer/index.vue +3 -20
- package/components/Drawer/types.ts +1 -0
- package/components/DynamicContent/DynamicContentCloseButton.vue +2 -2
- package/components/LocaleSelector.vue +1 -1
- package/components/Markdown.vue +1 -1
- package/components/PopoverCard.vue +3 -3
- package/components/Resource/Detail/Card/ExtrasCard.vue +39 -0
- package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +142 -0
- package/components/Resource/Detail/Card/StateCard/composables.ts +41 -11
- package/components/Resource/Detail/Card/StateCard/index.vue +3 -9
- package/components/Resource/Detail/Card/StateCard/types.ts +6 -0
- package/components/Resource/Detail/Card/{PodsCard → StatusCard}/index.vue +11 -10
- package/components/Resource/Detail/Card/__tests__/PodsCard.test.ts +24 -25
- package/components/Resource/Detail/Cards.vue +27 -0
- package/components/Resource/Detail/Masthead/__tests__/index.test.ts +70 -0
- package/components/Resource/Detail/Masthead/index.vue +5 -0
- package/components/Resource/Detail/Metadata/KeyValueRow.vue +4 -2
- package/components/Resource/Detail/ResourcePopover/ResourcePopoverCard.vue +2 -2
- package/components/Resource/Detail/ResourceRow.types.ts +14 -0
- package/components/Resource/Detail/ResourceRow.vue +23 -35
- package/components/Resource/Detail/StatusRow.vue +5 -2
- package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +38 -7
- package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +106 -2
- package/components/Resource/Detail/TitleBar/composables.ts +2 -1
- package/components/Resource/Detail/TitleBar/index.vue +41 -6
- package/components/ResourceDetail/Masthead/__tests__/index.test.ts +49 -1
- package/components/ResourceDetail/Masthead/__tests__/latest.test.ts +85 -0
- package/components/ResourceDetail/Masthead/index.vue +1 -0
- package/components/ResourceDetail/Masthead/latest.vue +8 -1
- package/components/ResourceDetail/Masthead/legacy.vue +1 -1
- package/components/Setting.vue +1 -1
- package/components/SortableTable/index.vue +25 -0
- package/components/SortableTable/selection.js +25 -12
- package/components/SortableTable/sorting.js +1 -1
- package/components/Tabbed/Tab.vue +1 -0
- package/components/Tabbed/index.vue +29 -6
- package/components/Window/ContainerShell.vue +10 -13
- package/components/fleet/FleetClusterTargets/TargetsList.vue +47 -29
- package/components/fleet/FleetClusterTargets/index.vue +82 -29
- package/components/fleet/FleetClusters.vue +26 -12
- package/components/fleet/FleetGitRepoPaths.vue +2 -2
- package/components/fleet/FleetResources.vue +14 -0
- package/components/fleet/FleetValuesFrom.vue +2 -2
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +531 -0
- package/components/fleet/__tests__/FleetClusters.test.ts +576 -0
- package/components/fleet/dashboard/ResourceDetails.vue +96 -123
- package/components/form/Conditions.vue +1 -15
- package/components/form/HookOption.vue +5 -0
- package/components/form/LabeledSelect.vue +1 -1
- package/components/form/LifecycleHooks.vue +2 -6
- package/components/form/ResourceLabeledSelect.vue +12 -1
- package/components/form/SeccompProfile.vue +113 -0
- package/components/form/Security.vue +244 -133
- package/components/form/__tests__/LabeledSelect.test.ts +1 -1
- package/components/form/__tests__/SeccompProfile.test.js +124 -0
- package/components/form/__tests__/Security.test.ts +125 -37
- package/components/formatter/Autoscaler.vue +2 -2
- package/components/formatter/FleetSummaryGraph.vue +4 -1
- package/components/nav/Group.vue +5 -0
- package/components/nav/Header.vue +3 -3
- package/components/nav/HeaderPageActionMenu.vue +1 -1
- package/components/nav/NamespaceFilter.vue +6 -6
- package/components/nav/NotificationCenter/index.vue +1 -1
- package/components/nav/TopLevelMenu.helper.ts +41 -16
- package/components/nav/TopLevelMenu.vue +45 -25
- package/components/nav/WorkspaceSwitcher.vue +1 -1
- package/components/nav/__tests__/TopLevelMenu.helper.test.ts +277 -0
- package/components/nav/__tests__/TopLevelMenu.test.ts +160 -4
- package/components/templates/default.vue +0 -3
- package/components/templates/home.vue +0 -3
- package/components/templates/plain.vue +0 -3
- package/composables/useClickOutside.ts +1 -1
- package/config/product/explorer.js +1 -2
- package/config/types.js +41 -8
- package/detail/__tests__/workload.test.ts +8 -16
- package/detail/catalog.cattle.io.app.vue +6 -0
- package/detail/fleet.cattle.io.cluster.vue +6 -0
- package/detail/workload/index.vue +7 -109
- package/edit/__tests__/projectsecret.test.ts +42 -0
- package/edit/auth/__tests__/oidc.test.ts +50 -0
- package/edit/auth/oidc.vue +68 -44
- package/edit/autoscaling.horizontalpodautoscaler/index.vue +140 -59
- package/edit/autoscaling.horizontalpodautoscaler/metrics-row.vue +41 -5
- package/edit/projectsecret.vue +29 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +89 -200
- package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +58 -17
- package/edit/provisioning.cattle.io.cluster/rke2.vue +11 -0
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +3 -63
- package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +82 -14
- package/edit/workload/__tests__/index.test.ts +122 -85
- package/edit/workload/index.vue +48 -29
- package/edit/workload/mixins/workload.js +85 -32
- package/list/catalog.cattle.io.clusterrepo.vue +1 -1
- package/list/projectsecret.vue +2 -2
- package/machine-config/__tests__/vmwarevsphere.test.ts +64 -0
- package/machine-config/amazonec2.vue +2 -2
- package/machine-config/vmwarevsphere.vue +58 -4
- package/mixins/__tests__/brand.spec.ts +18 -13
- package/mixins/__tests__/chart.test.ts +63 -0
- package/mixins/chart.js +56 -51
- package/models/__tests__/catalog.cattle.io.app.test.ts +33 -0
- package/models/__tests__/workload.test.ts +333 -0
- package/models/catalog.cattle.io.app.js +8 -0
- package/models/pod.js +14 -0
- package/models/secret.js +1 -1
- package/models/workload.js +93 -27
- package/package.json +4 -4
- package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +91 -0
- package/pages/c/_cluster/apps/charts/install.vue +4 -4
- package/pages/c/_cluster/explorer/EventsTable.vue +2 -2
- package/pages/c/_cluster/fleet/index.vue +18 -12
- package/pages/c/_cluster/manager/hostedprovider/index.vue +1 -19
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -1
- package/pages/c/_cluster/uiplugins/index.vue +1 -1
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +234 -0
- package/plugins/dashboard-store/actions.js +9 -8
- package/plugins/dashboard-store/resource-class.js +97 -1
- package/plugins/steve/__tests__/revision.test.ts +84 -0
- package/plugins/steve/__tests__/steve-pagination-utils.test.ts +30 -0
- package/plugins/steve/__tests__/subscribe.spec.ts +134 -0
- package/plugins/steve/mutations.js +9 -0
- package/plugins/steve/revision.ts +26 -0
- package/plugins/steve/steve-pagination-utils.ts +6 -5
- package/plugins/steve/subscribe.js +211 -51
- package/plugins/subscribe-events.ts +2 -2
- package/rancher-components/Form/Checkbox/Checkbox.vue +13 -0
- package/rancher-components/LabeledTooltip/LabeledTooltip.vue +1 -1
- package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.vue +1 -1
- package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +3 -1
- package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +3 -1
- package/rancher-components/Pill/RcTag/RcTag.vue +1 -1
- package/rancher-components/Pill/index.ts +4 -0
- package/rancher-components/RcButton/RcButton.test.ts +53 -9
- package/rancher-components/RcButton/RcButton.vue +217 -25
- package/rancher-components/RcButton/types.ts +27 -1
- package/rancher-components/RcDropdown/RcDropdownMenu.vue +4 -4
- package/rancher-components/RcDropdown/types.ts +3 -3
- package/rancher-components/RcIcon/RcIcon.test.ts +42 -0
- package/rancher-components/RcIcon/RcIcon.vue +9 -6
- package/rancher-components/RcIcon/types.ts +13 -9
- package/rancher-components/utils/status.test.ts +10 -15
- package/rancher-components/utils/status.ts +5 -6
- package/store/aws.js +18 -12
- package/store/index.js +4 -8
- package/store/type-map.utils.ts +1 -1
- package/types/kube/kube-api.ts +29 -3
- package/types/rancher/steve.api.ts +40 -0
- package/types/shell/index.d.ts +99 -0
- package/types/store/dashboard-store.types.ts +29 -7
- package/types/store/pagination.types.ts +1 -0
- package/types/store/subscribe-events.types.ts +1 -0
- package/utils/__tests__/azure.test.ts +56 -0
- package/utils/__tests__/back-off.test.ts +364 -245
- package/utils/__tests__/error.test.ts +44 -0
- package/utils/__tests__/fleet.test.ts +8 -1
- package/utils/__tests__/pagination-wrapper.test.ts +167 -0
- package/utils/__tests__/version.test.ts +55 -1
- package/utils/azure.js +12 -0
- package/utils/back-off.ts +302 -69
- package/utils/cspAdaptor.ts +32 -14
- package/utils/dynamic-content/__tests__/index.test.ts +1 -1
- package/utils/dynamic-content/__tests__/new-release.test.ts +48 -7
- package/utils/dynamic-content/__tests__/support-notice.test.ts +1 -4
- package/utils/dynamic-content/index.ts +1 -6
- package/utils/dynamic-content/new-release.ts +5 -3
- package/utils/dynamic-content/types.d.ts +0 -1
- package/utils/error.js +9 -0
- package/utils/fleet.ts +2 -2
- package/utils/inactivity.ts +2 -3
- package/utils/pagination-wrapper.ts +101 -17
- package/utils/validators/formRules/index.ts +3 -0
- package/utils/version.js +38 -0
- package/components/auth/AzureWarning.vue +0 -77
- /package/components/Resource/Detail/{Card/PodsCard/Bubble.vue → Bubble.vue} +0 -0
- /package/components/Resource/Detail/Card/{PodsCard → StatusCard}/composable.ts +0 -0
|
@@ -204,6 +204,12 @@ export default {
|
|
|
204
204
|
const spec = this.value.spec;
|
|
205
205
|
let podTemplateSpec = type === WORKLOAD_TYPES.CRON_JOB ? spec.jobTemplate.spec.template.spec : spec?.template?.spec;
|
|
206
206
|
|
|
207
|
+
// Area to add default value to securityContext on POD CREATE
|
|
208
|
+
// Check if the securityContext has been setup, for None should be {} when cloning, for new should be empty.
|
|
209
|
+
if (this.mode === _CREATE && !podTemplateSpec.securityContext) {
|
|
210
|
+
podTemplateSpec.securityContext = { seccompProfile: { type: 'RuntimeDefault' } };
|
|
211
|
+
}
|
|
212
|
+
|
|
207
213
|
let containers = podTemplateSpec.containers || [];
|
|
208
214
|
let container;
|
|
209
215
|
|
|
@@ -258,6 +264,7 @@ export default {
|
|
|
258
264
|
secondaryResourceData: this.secondaryResourceDataConfig(),
|
|
259
265
|
namespacedConfigMaps: [],
|
|
260
266
|
allNodes: null,
|
|
267
|
+
workerNodes: null,
|
|
261
268
|
allNodeObjects: [],
|
|
262
269
|
namespacedSecrets: [],
|
|
263
270
|
imagePullNamespacedSecrets: [],
|
|
@@ -276,20 +283,46 @@ export default {
|
|
|
276
283
|
container,
|
|
277
284
|
containerChange: 0,
|
|
278
285
|
tabChange: 0,
|
|
279
|
-
podFsGroup: podTemplateSpec.securityContext?.fsGroup,
|
|
280
286
|
savePvcHookName: 'savePvcHook',
|
|
281
287
|
tabWeightMap: TAB_WEIGHT_MAP,
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
288
|
+
// [fvFormRulesets][localhostProfile] added as default not required, needs to be on the INDEX 0
|
|
289
|
+
fvFormRuleSets: [{
|
|
290
|
+
path: 'podTemplateSpec.securityContext.seccompProfile.localhostProfile',
|
|
291
|
+
rules: [''],
|
|
292
|
+
rootObject: this,
|
|
293
|
+
translationKey: 'workload.container.security.localhostProfile.label'
|
|
294
|
+
}],
|
|
295
|
+
fvReportedValidationPaths: ['spec'],
|
|
296
|
+
isNamespaceNew: false,
|
|
297
|
+
idKey: ID_KEY
|
|
286
298
|
};
|
|
287
299
|
},
|
|
288
300
|
|
|
289
301
|
computed: {
|
|
290
302
|
...mapGetters(['currentCluster']),
|
|
303
|
+
seccompProfileTypes() {
|
|
304
|
+
return [
|
|
305
|
+
{
|
|
306
|
+
value: 'None',
|
|
307
|
+
label: this.t('workload.container.security.seccompProfile.types.none.label')
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
value: 'RuntimeDefault',
|
|
311
|
+
label: this.t('workload.container.security.seccompProfile.types.runtimeDefault.label')
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
value: 'Localhost',
|
|
315
|
+
label: this.t('workload.container.security.seccompProfile.types.localhost.label')
|
|
316
|
+
}, {
|
|
317
|
+
value: 'Unconfined',
|
|
318
|
+
label: this.t('workload.container.security.seccompProfile.types.unconfined.label')
|
|
319
|
+
}];
|
|
320
|
+
},
|
|
321
|
+
|
|
291
322
|
tabErrors() {
|
|
292
|
-
|
|
323
|
+
const tabErrors = { podSecurityContext: this.fvGetPathErrors(['podTemplateSpec.securityContext.seccompProfile.localhostProfile'])?.length > 0 };
|
|
324
|
+
|
|
325
|
+
return tabErrors;
|
|
293
326
|
},
|
|
294
327
|
|
|
295
328
|
defaultWorkloadTab() {
|
|
@@ -422,8 +455,15 @@ export default {
|
|
|
422
455
|
}),
|
|
423
456
|
].map((container) => {
|
|
424
457
|
const containerImageRule = formRulesGenerator(this.$store.getters['i18n/t'], { name: container.name }).containerImage;
|
|
458
|
+
const localhostProfileRule = formRulesGenerator(this.$store.getters['i18n/t'], { name: container.name }).localhostProfile;
|
|
425
459
|
|
|
426
|
-
|
|
460
|
+
const imageError = containerImageRule(container);
|
|
461
|
+
const localhostProfileError = localhostProfileRule(container);
|
|
462
|
+
|
|
463
|
+
container.error = {
|
|
464
|
+
general: imageError,
|
|
465
|
+
localhostProfile: localhostProfileError
|
|
466
|
+
};
|
|
427
467
|
|
|
428
468
|
return container;
|
|
429
469
|
});
|
|
@@ -560,6 +600,26 @@ export default {
|
|
|
560
600
|
this.servicesOwned = await this.value.getServicesOwned();
|
|
561
601
|
},
|
|
562
602
|
|
|
603
|
+
'podTemplateSpec.securityContext.seccompProfile.type'(neu) {
|
|
604
|
+
if (neu === 'Localhost') {
|
|
605
|
+
// [fvFormRulesets][localhostProfile] added here, should be required if Localhost selected
|
|
606
|
+
this.fvFormRuleSets[0] = {
|
|
607
|
+
path: 'podTemplateSpec.securityContext.seccompProfile.localhostProfile',
|
|
608
|
+
rules: ['required'],
|
|
609
|
+
rootObject: this,
|
|
610
|
+
translationKey: 'workload.container.security.localhostProfile.label'
|
|
611
|
+
};
|
|
612
|
+
} else {
|
|
613
|
+
// [fvFormRulesets][localhostProfile] added here, should not be required if Localhost selected
|
|
614
|
+
this.fvFormRuleSets[0] = {
|
|
615
|
+
path: 'podTemplateSpec.securityContext.seccompProfile.localhostProfile',
|
|
616
|
+
rules: [''],
|
|
617
|
+
rootObject: this,
|
|
618
|
+
translationKey: 'workload.container.security.localhostProfile.label'
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
},
|
|
622
|
+
|
|
563
623
|
isNamespaceNew(neu, old) {
|
|
564
624
|
if (!old && neu) {
|
|
565
625
|
// As the namespace is new any resource that's been fetched with a namespace is now invalid
|
|
@@ -602,15 +662,6 @@ export default {
|
|
|
602
662
|
this.value['type'] = neu;
|
|
603
663
|
delete this.value.apiVersion;
|
|
604
664
|
},
|
|
605
|
-
|
|
606
|
-
container: {
|
|
607
|
-
handler(c) {
|
|
608
|
-
this.fvFormRuleSets = [{
|
|
609
|
-
path: 'image', rootObject: c, rules: ['required'], translationKey: 'workload.container.image'
|
|
610
|
-
}];
|
|
611
|
-
},
|
|
612
|
-
immediate: true
|
|
613
|
-
}
|
|
614
665
|
},
|
|
615
666
|
|
|
616
667
|
created() {
|
|
@@ -647,7 +698,24 @@ export default {
|
|
|
647
698
|
parsingFunc: (data) => {
|
|
648
699
|
return data.map((node) => node.id);
|
|
649
700
|
}
|
|
650
|
-
}
|
|
701
|
+
},
|
|
702
|
+
{
|
|
703
|
+
var: 'workerNodes',
|
|
704
|
+
parsingFunc: (data) => {
|
|
705
|
+
const keys = [
|
|
706
|
+
`node-role.kubernetes.io/control-plane`,
|
|
707
|
+
`node-role.kubernetes.io/etcd`
|
|
708
|
+
];
|
|
709
|
+
|
|
710
|
+
return data
|
|
711
|
+
.filter((node) => {
|
|
712
|
+
const taints = node?.spec?.taints || [];
|
|
713
|
+
|
|
714
|
+
return taints.every((taint) => !keys.includes(taint.key));
|
|
715
|
+
})
|
|
716
|
+
.map((node) => node.id);
|
|
717
|
+
}
|
|
718
|
+
},
|
|
651
719
|
]
|
|
652
720
|
},
|
|
653
721
|
[SERVICE]: {
|
|
@@ -806,7 +874,6 @@ export default {
|
|
|
806
874
|
}
|
|
807
875
|
|
|
808
876
|
this.fixPodAffinity(podAntiAffinity);
|
|
809
|
-
this.fixPodSecurityContext(this.podTemplateSpec);
|
|
810
877
|
|
|
811
878
|
template.metadata.namespace = this.value.metadata.namespace;
|
|
812
879
|
|
|
@@ -903,20 +970,6 @@ export default {
|
|
|
903
970
|
return podAffinity;
|
|
904
971
|
},
|
|
905
972
|
|
|
906
|
-
fixPodSecurityContext(podTempSpec) {
|
|
907
|
-
if (this.podFsGroup) {
|
|
908
|
-
podTempSpec.securityContext = podTempSpec.securityContext || {};
|
|
909
|
-
podTempSpec.securityContext.fsGroup = this.podFsGroup;
|
|
910
|
-
} else {
|
|
911
|
-
if (podTempSpec.securityContext?.fsGroup) {
|
|
912
|
-
delete podTempSpec.securityContext.fsGroup;
|
|
913
|
-
}
|
|
914
|
-
if (Object.keys(podTempSpec.securityContext || {}).length === 0) {
|
|
915
|
-
delete podTempSpec.securityContext;
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
},
|
|
919
|
-
|
|
920
973
|
selectType(type) {
|
|
921
974
|
if (!this.type && type) {
|
|
922
975
|
this.$router.replace({ params: { resource: type } });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import PaginatedResourceTable from '@shell/components/PaginatedResourceTable.vue';
|
|
3
3
|
import { CATALOG as CATALOG_ANNOTATIONS } from '@shell/config/labels-annotations';
|
|
4
|
-
import { RancherKubeMetadata } from '@shell/types/
|
|
4
|
+
import { RancherKubeMetadata } from '@shell/types/rancher/steve.api';
|
|
5
5
|
import { PaginationArgs, PaginationParamFilter } from '@shell/types/store/pagination.types';
|
|
6
6
|
import { defineComponent } from 'vue';
|
|
7
7
|
|
package/list/projectsecret.vue
CHANGED
|
@@ -10,7 +10,7 @@ import { TableColumn } from '@shell/types/store/type-map';
|
|
|
10
10
|
import { mapGetters } from 'vuex';
|
|
11
11
|
import { GROUP_RESOURCES, mapPref } from '@shell/store/prefs';
|
|
12
12
|
import { DEFAULT_PROJECT, SYSTEM_PROJECT, UI_PROJECT_SECRET, UI_PROJECT_SECRET_COPY } from '@shell/config/labels-annotations';
|
|
13
|
-
import { RancherKubeMetadata } from '@shell/types/
|
|
13
|
+
import { RancherKubeMetadata } from '@shell/types/rancher/steve.api';
|
|
14
14
|
import {
|
|
15
15
|
AGE, SECRET_DATA, STATE, SUB_TYPE, NAME as NAME_COL,
|
|
16
16
|
} from '@shell/config/table-headers';
|
|
@@ -170,7 +170,7 @@ export default {
|
|
|
170
170
|
createLocation() {
|
|
171
171
|
return {
|
|
172
172
|
name: 'c-cluster-product-resource-create',
|
|
173
|
-
params: { resource:
|
|
173
|
+
params: { resource: VIRTUAL_TYPES.PROJECT_SECRETS },
|
|
174
174
|
query: { [SECRET_SCOPE]: SECRET_QUERY_PARAMS.PROJECT_SCOPED }
|
|
175
175
|
};
|
|
176
176
|
},
|
|
@@ -57,6 +57,7 @@ describe('component: vmwarevsphere', () => {
|
|
|
57
57
|
|
|
58
58
|
const dataCenterElement = wrapper.find(`[data-testid="datacenter"]`).element;
|
|
59
59
|
const resourcePoolElement = wrapper.find(`[data-testid="resourcePool"]`).element;
|
|
60
|
+
const storageTypeElement = wrapper.find(`[data-testid="storageType"]`).element;
|
|
60
61
|
const dataStoreElement = wrapper.find(`[data-testid="dataStore"]`).element;
|
|
61
62
|
const folderElement = wrapper.find(`[data-testid="folder"]`).element;
|
|
62
63
|
const hostElement = wrapper.find(`[data-testid="host"]`).element;
|
|
@@ -64,6 +65,7 @@ describe('component: vmwarevsphere', () => {
|
|
|
64
65
|
|
|
65
66
|
expect(dataCenterElement).toBeDefined();
|
|
66
67
|
expect(resourcePoolElement).toBeDefined();
|
|
68
|
+
expect(storageTypeElement).toBeDefined();
|
|
67
69
|
expect(dataStoreElement).toBeDefined();
|
|
68
70
|
expect(folderElement).toBeDefined();
|
|
69
71
|
expect(hostElement).toBeDefined();
|
|
@@ -297,4 +299,66 @@ describe('component: vmwarevsphere', () => {
|
|
|
297
299
|
expect(wrapper.vm.value.network).toStrictEqual([legacyName]);
|
|
298
300
|
});
|
|
299
301
|
});
|
|
302
|
+
|
|
303
|
+
describe('storage type toggling', () => {
|
|
304
|
+
it('should toggle storage type to datastore-cluster and clear datastore value', async() => {
|
|
305
|
+
const wrapper = mount(vmwarevsphere, defaultCreateSetup);
|
|
306
|
+
|
|
307
|
+
wrapper.vm.value.datastore = 'some-datastore';
|
|
308
|
+
await wrapper.setData({ storageType: 'datastore-cluster' });
|
|
309
|
+
|
|
310
|
+
expect(wrapper.vm.storageType).toBe('datastore-cluster');
|
|
311
|
+
expect(wrapper.vm.value.datastore).toBe('');
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('should toggle storage type to datastore and clear datastoreCluster value', async() => {
|
|
315
|
+
const wrapper = mount(vmwarevsphere, defaultCreateSetup);
|
|
316
|
+
|
|
317
|
+
// Initialize with datastore-cluster selected
|
|
318
|
+
await wrapper.setData({ storageType: 'datastore-cluster' });
|
|
319
|
+
wrapper.vm.value.datastoreCluster = 'some-cluster';
|
|
320
|
+
|
|
321
|
+
// Switch back to datastore
|
|
322
|
+
await wrapper.setData({ storageType: 'datastore' });
|
|
323
|
+
|
|
324
|
+
expect(wrapper.vm.storageType).toBe('datastore');
|
|
325
|
+
expect(wrapper.vm.value.datastoreCluster).toBe('');
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('should initialize storage type to datastore-cluster if value has datastoreCluster set', () => {
|
|
329
|
+
const setup = {
|
|
330
|
+
...defaultEditSetup,
|
|
331
|
+
propsData: {
|
|
332
|
+
...defaultEditSetup.propsData,
|
|
333
|
+
value: {
|
|
334
|
+
...defaultEditSetup.propsData.value,
|
|
335
|
+
datastoreCluster: 'existing-cluster',
|
|
336
|
+
datastore: ''
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
const wrapper = mount(vmwarevsphere, setup);
|
|
341
|
+
|
|
342
|
+
expect(wrapper.vm.storageType).toBe('datastore-cluster');
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it('should correctly toggle the visibility of datastore and datastore cluster selects', async() => {
|
|
346
|
+
const wrapper = mount(vmwarevsphere, defaultCreateSetup);
|
|
347
|
+
const dataStoreContainer = wrapper.find('[data-testid="dataStore"]');
|
|
348
|
+
|
|
349
|
+
// Default state: datastore
|
|
350
|
+
let select = dataStoreContainer.findComponent({ name: 'LabeledSelect' });
|
|
351
|
+
|
|
352
|
+
expect(select.exists()).toBe(true);
|
|
353
|
+
expect(select.props('label')).toBe('%cluster.machineConfig.vsphere.scheduling.dataStore%');
|
|
354
|
+
|
|
355
|
+
// Toggle to datastore-cluster
|
|
356
|
+
await wrapper.setData({ storageType: 'datastore-cluster' });
|
|
357
|
+
|
|
358
|
+
select = dataStoreContainer.findComponent({ name: 'LabeledSelect' });
|
|
359
|
+
|
|
360
|
+
expect(select.exists()).toBe(true);
|
|
361
|
+
expect(select.props('label')).toBe('%cluster.machineConfig.vsphere.scheduling.dataStoreCluster%');
|
|
362
|
+
});
|
|
363
|
+
});
|
|
300
364
|
});
|
|
@@ -12,7 +12,7 @@ import { NORMAN } from '@shell/config/types';
|
|
|
12
12
|
import { allHash } from '@shell/utils/promise';
|
|
13
13
|
import { convertStringToKV, convertKVToString } from '@shell/utils/object';
|
|
14
14
|
import { sortBy } from '@shell/utils/sort';
|
|
15
|
-
import { stringify, exceptionToErrorsArray } from '@shell/utils/error';
|
|
15
|
+
import { stringify, exceptionToErrorsArray, formatAWSError } from '@shell/utils/error';
|
|
16
16
|
import EC2Networking from './components/EC2Networking.vue';
|
|
17
17
|
|
|
18
18
|
const DEFAULT_GROUP = 'rancher-nodes';
|
|
@@ -139,7 +139,7 @@ export default {
|
|
|
139
139
|
|
|
140
140
|
this.loadedRegionalFor = region;
|
|
141
141
|
} catch (e) {
|
|
142
|
-
this.errors = exceptionToErrorsArray(e);
|
|
142
|
+
this.errors = exceptionToErrorsArray(formatAWSError(e));
|
|
143
143
|
}
|
|
144
144
|
},
|
|
145
145
|
|
|
@@ -244,7 +244,17 @@ export default {
|
|
|
244
244
|
haveTemplates: null,
|
|
245
245
|
vAppOptions,
|
|
246
246
|
vappMode: VAPP_MODE.DISABLED,
|
|
247
|
-
|
|
247
|
+
storageType: this.value.datastoreCluster ? 'datastore-cluster' : 'datastore',
|
|
248
|
+
storageOptions: [
|
|
249
|
+
{
|
|
250
|
+
label: this.t('cluster.machineConfig.vsphere.scheduling.dataStore'),
|
|
251
|
+
value: 'datastore'
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
label: this.t('cluster.machineConfig.vsphere.scheduling.dataStoreCluster'),
|
|
255
|
+
value: 'datastore-cluster'
|
|
256
|
+
}
|
|
257
|
+
],
|
|
248
258
|
osOptions: OS_OPTIONS,
|
|
249
259
|
validationErrors: {},
|
|
250
260
|
};
|
|
@@ -381,6 +391,19 @@ export default {
|
|
|
381
391
|
'value.contentLibrary'() {
|
|
382
392
|
this.loadLibraryTemplates();
|
|
383
393
|
},
|
|
394
|
+
storageType(val) {
|
|
395
|
+
if (val === 'datastore-cluster') {
|
|
396
|
+
set(this.value, 'datastore', '');
|
|
397
|
+
if (!this.dataStoreClustersResults) {
|
|
398
|
+
this.loadDataStoreClusters();
|
|
399
|
+
}
|
|
400
|
+
} else {
|
|
401
|
+
set(this.value, 'datastoreCluster', '');
|
|
402
|
+
if (!this.dataStoresResults) {
|
|
403
|
+
this.loadDataStores();
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
},
|
|
384
407
|
'value.creationType'(value) {
|
|
385
408
|
set(this.value, 'cloneFrom', '');
|
|
386
409
|
const boot2dockerUrl = value === CREATION_METHOD.LEGACY ? BOOT_2_DOCKER_URL : '';
|
|
@@ -447,7 +470,7 @@ export default {
|
|
|
447
470
|
if (this.mode === _CREATE || this.poolCreateMode) {
|
|
448
471
|
set(this.value, 'datacenter', options[0]);
|
|
449
472
|
set(this.value, 'cloneFrom', undefined);
|
|
450
|
-
|
|
473
|
+
this.storageType = 'datastore';
|
|
451
474
|
}
|
|
452
475
|
|
|
453
476
|
if ([_EDIT, _VIEW].includes(this.mode) && !this.poolCreateMode) {
|
|
@@ -530,7 +553,7 @@ export default {
|
|
|
530
553
|
},
|
|
531
554
|
|
|
532
555
|
async loadDataStoreClusters() {
|
|
533
|
-
set(this, '
|
|
556
|
+
set(this, 'dataStoreClustersResults', null);
|
|
534
557
|
|
|
535
558
|
const options = await this.requestOptions('data-store-clusters', this.value.datacenter);
|
|
536
559
|
const content = this.mapPathOptionsToContent(options);
|
|
@@ -633,7 +656,13 @@ export default {
|
|
|
633
656
|
|
|
634
657
|
loadAllDatacenterResources() {
|
|
635
658
|
this.loadResourcePools();
|
|
636
|
-
this.
|
|
659
|
+
if (this.storageType === 'datastore') {
|
|
660
|
+
this.loadDataStores();
|
|
661
|
+
this.dataStoreClustersResults = null;
|
|
662
|
+
} else {
|
|
663
|
+
this.loadDataStoreClusters();
|
|
664
|
+
this.dataStoresResults = null;
|
|
665
|
+
}
|
|
637
666
|
this.loadFolders();
|
|
638
667
|
this.loadHosts();
|
|
639
668
|
this.loadTemplates();
|
|
@@ -831,12 +860,27 @@ export default {
|
|
|
831
860
|
/>
|
|
832
861
|
</div>
|
|
833
862
|
</div>
|
|
863
|
+
<div class="row mt-10">
|
|
864
|
+
<div
|
|
865
|
+
class="col span-12"
|
|
866
|
+
data-testid="storageType"
|
|
867
|
+
>
|
|
868
|
+
<RadioGroup
|
|
869
|
+
v-model:value="storageType"
|
|
870
|
+
name="storageType"
|
|
871
|
+
:options="storageOptions"
|
|
872
|
+
:disabled="isDisabled"
|
|
873
|
+
:row="true"
|
|
874
|
+
/>
|
|
875
|
+
</div>
|
|
876
|
+
</div>
|
|
834
877
|
<div class="row mt-10">
|
|
835
878
|
<div
|
|
836
879
|
class="col span-6"
|
|
837
880
|
data-testid="dataStore"
|
|
838
881
|
>
|
|
839
882
|
<LabeledSelect
|
|
883
|
+
v-if="storageType === 'datastore'"
|
|
840
884
|
v-model:value="value.datastore"
|
|
841
885
|
:loading="dataStoresLoading"
|
|
842
886
|
:mode="mode"
|
|
@@ -845,6 +889,16 @@ export default {
|
|
|
845
889
|
:disabled="isDisabled"
|
|
846
890
|
:tooltip="value.datastore"
|
|
847
891
|
/>
|
|
892
|
+
<LabeledSelect
|
|
893
|
+
v-else
|
|
894
|
+
v-model:value="value.datastoreCluster"
|
|
895
|
+
:loading="dataStoreClustersLoading"
|
|
896
|
+
:mode="mode"
|
|
897
|
+
:options="dataStoreClusters"
|
|
898
|
+
:label="t('cluster.machineConfig.vsphere.scheduling.dataStoreCluster')"
|
|
899
|
+
:disabled="isDisabled"
|
|
900
|
+
:tooltip="value.datastoreCluster"
|
|
901
|
+
/>
|
|
848
902
|
</div>
|
|
849
903
|
<div
|
|
850
904
|
class="col span-6"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils';
|
|
2
2
|
import { CATALOG, MANAGEMENT } from '@shell/config/types';
|
|
3
3
|
import Brand from '@shell/mixins/brand';
|
|
4
|
+
import CspAdapterUtils from '@shell/utils/cspAdaptor';
|
|
4
5
|
|
|
5
6
|
describe('brandMixin', () => {
|
|
6
7
|
const createWrapper = (vaiOn = false) => {
|
|
@@ -54,45 +55,47 @@ describe('brandMixin', () => {
|
|
|
54
55
|
data: () => data,
|
|
55
56
|
global: { mocks: { $store: store } }
|
|
56
57
|
});
|
|
57
|
-
const
|
|
58
|
+
const spyManagementDispatch = jest.spyOn(store, 'dispatch');
|
|
59
|
+
|
|
60
|
+
CspAdapterUtils.resetState();
|
|
58
61
|
|
|
59
62
|
return {
|
|
60
63
|
wrapper,
|
|
61
64
|
store,
|
|
62
|
-
|
|
65
|
+
spyManagementDispatch,
|
|
63
66
|
};
|
|
64
67
|
};
|
|
65
68
|
|
|
66
69
|
describe('should make correct requests', () => {
|
|
67
70
|
it('vai off', async() => {
|
|
68
|
-
const { wrapper,
|
|
71
|
+
const { wrapper, spyManagementDispatch } = createWrapper(false);
|
|
69
72
|
|
|
70
73
|
// NOTE - wrapper.vm.$options.fetch() doesn't work
|
|
71
74
|
await wrapper.vm.$options.fetch.apply(wrapper.vm);
|
|
72
75
|
|
|
73
76
|
// wrapper.vm.$nextTick();
|
|
74
|
-
expect(
|
|
77
|
+
expect(spyManagementDispatch).toHaveBeenNthCalledWith(1, 'management/findAll', {
|
|
75
78
|
type: MANAGEMENT.SETTING,
|
|
76
79
|
opt: {
|
|
77
80
|
load: 'multi', redirectUnauthorized: false, url: `/v1/${ MANAGEMENT.SETTING }s`
|
|
78
81
|
}
|
|
79
82
|
});
|
|
80
|
-
expect(
|
|
83
|
+
expect(spyManagementDispatch).toHaveBeenNthCalledWith(2, 'management/findAll', { type: CATALOG.APP });
|
|
81
84
|
});
|
|
82
85
|
|
|
83
86
|
it('vai on', async() => {
|
|
84
|
-
const { wrapper,
|
|
87
|
+
const { wrapper, spyManagementDispatch } = createWrapper(true);
|
|
85
88
|
|
|
86
89
|
// NOTE - wrapper.vm.$options.fetch() doesn't work
|
|
87
90
|
await wrapper.vm.$options.fetch.apply(wrapper.vm);
|
|
88
91
|
|
|
89
|
-
expect(
|
|
92
|
+
expect(spyManagementDispatch).toHaveBeenNthCalledWith(1, 'management/findAll', {
|
|
90
93
|
type: MANAGEMENT.SETTING,
|
|
91
94
|
opt: {
|
|
92
95
|
load: 'multi', url: `/v1/${ MANAGEMENT.SETTING }s`, redirectUnauthorized: false
|
|
93
96
|
}
|
|
94
97
|
});
|
|
95
|
-
expect(
|
|
98
|
+
expect(spyManagementDispatch).toHaveBeenNthCalledWith(2, 'management/findPage', {
|
|
96
99
|
type: CATALOG.APP,
|
|
97
100
|
opt: {
|
|
98
101
|
pagination: {
|
|
@@ -110,7 +113,9 @@ describe('brandMixin', () => {
|
|
|
110
113
|
pageSize: null,
|
|
111
114
|
projectsOrNamespaces: [],
|
|
112
115
|
sort: []
|
|
113
|
-
}
|
|
116
|
+
},
|
|
117
|
+
transient: true,
|
|
118
|
+
watch: false
|
|
114
119
|
}
|
|
115
120
|
});
|
|
116
121
|
});
|
|
@@ -120,7 +125,7 @@ describe('brandMixin', () => {
|
|
|
120
125
|
it('should have correct csp values (off)', async() => {
|
|
121
126
|
const { wrapper, store } = createWrapper();
|
|
122
127
|
|
|
123
|
-
const
|
|
128
|
+
const spyManagementDispatch = jest.spyOn(store, 'dispatch').mockImplementation((_, options) => {
|
|
124
129
|
const { type } = options as any;
|
|
125
130
|
|
|
126
131
|
if (type === MANAGEMENT.SETTING) {
|
|
@@ -136,7 +141,7 @@ describe('brandMixin', () => {
|
|
|
136
141
|
// NOTE - wrapper.vm.$options.fetch() doesn't work
|
|
137
142
|
await wrapper.vm.$options.fetch.apply(wrapper.vm, []);
|
|
138
143
|
|
|
139
|
-
expect(
|
|
144
|
+
expect(spyManagementDispatch).toHaveBeenCalledTimes(2);
|
|
140
145
|
|
|
141
146
|
expect(wrapper.vm.canCalcCspAdapter).toBeTruthy();
|
|
142
147
|
expect(wrapper.vm.cspAdapter).toBeFalsy();
|
|
@@ -145,7 +150,7 @@ describe('brandMixin', () => {
|
|
|
145
150
|
it.each(['rancher-csp-adapter', 'rancher-csp-billing-adapter'])('should have correct csp values (on - %p )', async(chartName) => {
|
|
146
151
|
const { wrapper, store } = createWrapper();
|
|
147
152
|
|
|
148
|
-
const
|
|
153
|
+
const spyManagementDispatch = jest.spyOn(store, 'dispatch').mockImplementation((_, options) => {
|
|
149
154
|
const { type } = options as any;
|
|
150
155
|
|
|
151
156
|
if (type === MANAGEMENT.SETTING) {
|
|
@@ -161,7 +166,7 @@ describe('brandMixin', () => {
|
|
|
161
166
|
// NOTE - wrapper.vm.$options.fetch() doesn't work
|
|
162
167
|
await wrapper.vm.$options.fetch.apply(wrapper.vm, []);
|
|
163
168
|
|
|
164
|
-
expect(
|
|
169
|
+
expect(spyManagementDispatch).toHaveBeenCalledTimes(2);
|
|
165
170
|
|
|
166
171
|
expect(wrapper.vm.canCalcCspAdapter).toBeTruthy();
|
|
167
172
|
expect(wrapper.vm.cspAdapter).toBeTruthy();
|
|
@@ -2,6 +2,8 @@ import ChartMixin from '@shell/mixins/chart';
|
|
|
2
2
|
import { OPA_GATE_KEEPER_ID } from '@shell/pages/c/_cluster/gatekeeper/index.vue';
|
|
3
3
|
import { mount } from '@vue/test-utils';
|
|
4
4
|
import { APP_UPGRADE_STATUS } from '@shell/store/catalog';
|
|
5
|
+
import { CATALOG as CATALOG_ANNOTATIONS } from '@shell/config/labels-annotations';
|
|
6
|
+
import { CATALOG } from '@shell/config/types';
|
|
5
7
|
|
|
6
8
|
describe('chartMixin', () => {
|
|
7
9
|
const testCases = {
|
|
@@ -151,6 +153,67 @@ describe('chartMixin', () => {
|
|
|
151
153
|
|
|
152
154
|
expect(mockStore.getters['catalog/version']).toHaveBeenCalledWith(expect.objectContaining({ showDeprecated: true }));
|
|
153
155
|
});
|
|
156
|
+
|
|
157
|
+
it('should find existing app using version annotations', async() => {
|
|
158
|
+
const mockStore = {
|
|
159
|
+
dispatch: jest.fn((action, payload) => {
|
|
160
|
+
if (action === 'cluster/find') {
|
|
161
|
+
return Promise.resolve({ id: payload.id, fetchValues: jest.fn() });
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return Promise.resolve();
|
|
165
|
+
}),
|
|
166
|
+
getters: {
|
|
167
|
+
currentCluster: () => {},
|
|
168
|
+
isRancher: () => true,
|
|
169
|
+
'catalog/repo': () => {
|
|
170
|
+
return () => 'repo';
|
|
171
|
+
},
|
|
172
|
+
'catalog/chart': () => {
|
|
173
|
+
return {
|
|
174
|
+
id: 'chart-id', versions: [{ version: '1.0.0' }], matchingInstalledApps: []
|
|
175
|
+
};
|
|
176
|
+
},
|
|
177
|
+
'catalog/version': () => ({
|
|
178
|
+
version: '1.0.0',
|
|
179
|
+
annotations: {
|
|
180
|
+
[CATALOG_ANNOTATIONS.NAMESPACE]: 'custom-ns',
|
|
181
|
+
[CATALOG_ANNOTATIONS.RELEASE_NAME]: 'custom-name',
|
|
182
|
+
}
|
|
183
|
+
}),
|
|
184
|
+
'prefs/get': () => {},
|
|
185
|
+
'i18n/t': () => jest.fn()
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const DummyComponent = {
|
|
190
|
+
mixins: [ChartMixin],
|
|
191
|
+
template: '<div></div>',
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const wrapper = mount(
|
|
195
|
+
DummyComponent,
|
|
196
|
+
{
|
|
197
|
+
global: {
|
|
198
|
+
mocks: {
|
|
199
|
+
$store: mockStore,
|
|
200
|
+
$route: {
|
|
201
|
+
query: {
|
|
202
|
+
chart: 'chart_name',
|
|
203
|
+
versionName: '1.0.0'
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
await wrapper.vm.fetchChart();
|
|
211
|
+
|
|
212
|
+
expect(mockStore.dispatch).toHaveBeenCalledWith('cluster/find', {
|
|
213
|
+
type: CATALOG.APP,
|
|
214
|
+
id: 'custom-ns/custom-name'
|
|
215
|
+
});
|
|
216
|
+
});
|
|
154
217
|
});
|
|
155
218
|
|
|
156
219
|
describe('action', () => {
|