@rancher/shell 3.0.9-rc.5 → 3.0.9
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/images/providers/oci-open-containers.svg +22 -0
- package/assets/images/providers/traefik.png +0 -0
- package/assets/styles/themes/_dark.scss +2 -0
- package/assets/styles/themes/_light.scss +2 -0
- package/assets/styles/themes/_modern.scss +6 -0
- package/assets/translations/en-us.yaml +129 -25
- package/components/CruResource.vue +3 -1
- package/components/ExplorerProjectsNamespaces.vue +12 -12
- package/components/IconOrSvg.vue +61 -42
- package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +109 -0
- package/components/Resource/Detail/Card/StatusCard/index.vue +21 -4
- package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +19 -2
- package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +19 -11
- package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +12 -0
- package/components/Resource/Detail/ResourcePopover/index.vue +2 -0
- package/components/Resource/Detail/ResourceRow.vue +2 -2
- package/components/ResourceList/index.vue +7 -4
- package/components/SortableTable/index.vue +2 -2
- package/components/Window/ContainerLogs.vue +48 -37
- package/components/fleet/FleetClusterTargets/TargetsList.vue +2 -2
- package/components/fleet/FleetClusterTargets/index.vue +6 -1
- package/components/fleet/GitRepoAdvancedTab.vue +333 -0
- package/components/fleet/GitRepoMetadataTab.vue +43 -0
- package/components/fleet/GitRepoRepositoryTab.vue +101 -0
- package/components/fleet/GitRepoTargetTab.vue +77 -0
- package/components/fleet/HelmOpAdvancedTab.vue +247 -0
- package/components/fleet/HelmOpChartTab.vue +158 -0
- package/components/fleet/HelmOpMetadataTab.vue +46 -0
- package/components/fleet/HelmOpTargetTab.vue +84 -0
- package/components/fleet/HelmOpValuesTab.vue +147 -0
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +119 -70
- package/components/form/BannerSettings.vue +2 -2
- package/components/form/NodeScheduling.vue +81 -7
- package/components/form/NotificationSettings.vue +2 -2
- package/components/form/PodAffinity.vue +1 -36
- package/components/form/ResourceLabeledSelect.vue +8 -4
- package/components/form/ResourceQuota/Namespace.vue +30 -9
- package/components/form/ResourceQuota/NamespaceRow.vue +25 -7
- package/components/form/ResourceQuota/Project.vue +140 -82
- package/components/form/ResourceQuota/ResourceQuotaEntry.vue +145 -0
- package/components/form/ResourceQuota/__tests__/Namespace.test.ts +307 -0
- package/components/form/ResourceQuota/__tests__/NamespaceRow.test.ts +281 -0
- package/components/form/ResourceQuota/__tests__/Project.test.ts +274 -27
- package/components/form/ResourceQuota/__tests__/ResourceQuotaEntry.test.ts +215 -0
- package/components/form/SchedulingCustomization.vue +14 -6
- package/components/form/SelectOrCreateAuthSecret.vue +107 -18
- package/components/form/__tests__/NodeScheduling.test.ts +12 -9
- package/components/form/__tests__/PodAffinity.test.ts +21 -2
- package/components/form/__tests__/SchedulingCustomization.test.ts +240 -0
- package/components/formatter/ClusterLink.vue +8 -0
- package/components/formatter/SecretOrigin.vue +79 -0
- package/config/labels-annotations.js +7 -6
- package/config/pagination-table-headers.js +6 -4
- package/config/product/explorer.js +1 -11
- package/config/product/manager.js +0 -1
- package/config/query-params.js +3 -0
- package/config/settings.ts +15 -2
- package/config/table-headers.js +21 -17
- package/config/types.js +23 -8
- package/detail/fleet.cattle.io.cluster.vue +1 -1
- package/detail/workload/index.vue +11 -16
- package/dialog/DeactivateDriverDialog.vue +1 -1
- package/dialog/FeatureFlagListDialog.vue +1 -1
- package/dialog/Ipv6NetworkingDialog.vue +156 -0
- package/dialog/ScalePoolDownDialog.vue +2 -2
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -1
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +1 -0
- package/edit/__tests__/management.cattle.io.project.test.js +56 -128
- package/edit/auth/oidc.vue +1 -1
- package/edit/catalog.cattle.io.clusterrepo.vue +155 -25
- package/edit/fleet.cattle.io.gitrepo.vue +153 -283
- package/edit/fleet.cattle.io.helmop.vue +190 -332
- package/edit/management.cattle.io.project.vue +5 -42
- package/edit/management.cattle.io.setting.vue +6 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/auth.spec.ts +145 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/index.test.ts +202 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/tls.spec.ts +226 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/auth.vue +24 -21
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/opsgenie.spec.ts +157 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/pagerduty.spec.ts +132 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/slack.spec.ts +108 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/pagerduty.vue +2 -1
- package/edit/monitoring.coreos.com.receiver/__tests__/auth.spec.ts +165 -0
- package/edit/monitoring.coreos.com.receiver/__tests__/index.test.ts +153 -0
- package/edit/monitoring.coreos.com.receiver/__tests__/tls.spec.ts +115 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/email.spec.ts +86 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/opsgenie.spec.ts +209 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/pagerduty.spec.ts +105 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/slack.spec.ts +92 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/webhook.spec.ts +131 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +55 -24
- package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +1 -103
- package/edit/provisioning.cattle.io.cluster/__tests__/index.test.ts +13 -1
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2-fleet-cluster-agent.test.ts +283 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +65 -49
- package/edit/provisioning.cattle.io.cluster/ingress/IngressCards.vue +114 -0
- package/edit/provisioning.cattle.io.cluster/ingress/IngressConfiguration.vue +158 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +167 -69
- package/edit/provisioning.cattle.io.cluster/shared.ts +36 -1
- package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +2 -1
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +70 -7
- package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +343 -0
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +2 -1
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/__tests__/S3Config.test.ts +13 -1
- package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +10 -44
- package/edit/secret/index.vue +1 -1
- package/edit/token.vue +68 -29
- package/edit/workload/__tests__/index.test.ts +2 -37
- package/edit/workload/index.vue +6 -2
- package/edit/workload/mixins/workload.js +0 -32
- package/list/__tests__/management.cattle.io.setting.test.ts +198 -0
- package/list/management.cattle.io.setting.vue +13 -0
- package/list/provisioning.cattle.io.cluster.vue +50 -1
- package/list/secret.vue +4 -9
- package/list/service.vue +6 -8
- package/machine-config/amazonec2.vue +11 -4
- package/machine-config/components/EC2Networking.vue +46 -30
- package/machine-config/components/__tests__/EC2Networking.test.ts +7 -7
- package/machine-config/components/__tests__/utils/vpcSubnetMockData.js +0 -9
- package/machine-config/digitalocean.vue +3 -3
- package/models/__tests__/chart.test.ts +2 -2
- package/models/__tests__/namespace.test.ts +11 -0
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +96 -0
- package/models/__tests__/workload.test.ts +42 -1
- package/models/catalog.cattle.io.clusterrepo.js +30 -4
- package/models/chart.js +3 -3
- package/models/ext.cattle.io.token.js +48 -0
- package/models/kontainerdriver.js +2 -2
- package/models/namespace.js +7 -1
- package/models/nodedriver.js +2 -2
- package/models/provisioning.cattle.io.cluster.js +28 -7
- package/models/secret.js +0 -17
- package/models/service.js +44 -1
- package/models/token.js +4 -0
- package/models/workload.js +12 -6
- package/package.json +1 -1
- package/pages/account/index.vue +96 -67
- package/pages/auth/setup.vue +5 -14
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +45 -18
- package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +4 -1
- package/pages/c/_cluster/apps/charts/index.vue +82 -3
- package/pages/c/_cluster/apps/charts/install.vue +317 -42
- package/pages/c/_cluster/explorer/tools/index.vue +1 -1
- package/pages/c/_cluster/manager/cloudCredential/index.vue +1 -1
- package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +5 -4
- package/pages/c/_cluster/settings/index.vue +3 -1
- package/pages/c/_cluster/uiplugins/index.vue +1 -1
- package/plugins/dashboard-store/__tests__/getters.test.ts +108 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +27 -0
- package/plugins/dashboard-store/actions.js +3 -8
- package/plugins/dashboard-store/getters.js +7 -5
- package/plugins/dashboard-store/mutations.js +4 -1
- package/plugins/dashboard-store/resource-class.js +3 -3
- package/plugins/steve/__tests__/steve-class.test.ts +102 -141
- package/plugins/steve/steve-class.js +12 -3
- package/plugins/steve/steve-pagination-utils.ts +6 -2
- package/rancher-components/RcIcon/types.ts +2 -0
- package/rancher-components/RcItemCard/RcItemCard.vue +72 -20
- package/store/prefs.js +3 -0
- package/types/aws-sdk.d.ts +121 -0
- package/types/resources/node.ts +15 -0
- package/types/shell/index.d.ts +537 -506
- package/types/store/pagination.types.ts +5 -5
- package/utils/__tests__/array.test.ts +1 -29
- package/utils/__tests__/cluster-agent-configuration.test.ts +203 -0
- package/utils/array.ts +0 -11
- package/utils/aws.ts +21 -0
- package/utils/cluster.js +22 -2
- package/utils/selector-typed.ts +1 -1
- package/utils/svg-filter.js +4 -3
- package/components/__tests__/ProjectRow.test.ts +0 -206
- package/components/form/ResourceQuota/ProjectRow.vue +0 -277
|
@@ -25,7 +25,10 @@ import Wizard from '@shell/components/Wizard';
|
|
|
25
25
|
import TypeDescription from '@shell/components/TypeDescription';
|
|
26
26
|
import ChartMixin from '@shell/mixins/chart';
|
|
27
27
|
import ChildHook, { BEFORE_SAVE_HOOKS, AFTER_SAVE_HOOKS } from '@shell/mixins/child-hook';
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
CLUSTER_REPO_APPCO_AUTH_GENERATE_NAME, CATALOG, MANAGEMENT, DEFAULT_WORKSPACE, CAPI, SECRET,
|
|
30
|
+
AUTH_TYPE, NAMESPACE as NAMESPACE_TYPE
|
|
31
|
+
} from '@shell/config/types';
|
|
29
32
|
import {
|
|
30
33
|
CHART, FROM_CLUSTER, FROM_TOOLS, HIDE_SIDE_NAV, NAMESPACE, REPO, REPO_TYPE, VERSION, _FLAGGED
|
|
31
34
|
} from '@shell/config/query-params';
|
|
@@ -40,6 +43,8 @@ import { findBy, insertAt } from '@shell/utils/array';
|
|
|
40
43
|
import { saferDump } from '@shell/utils/create-yaml';
|
|
41
44
|
import { LINUX, WINDOWS } from '@shell/store/catalog';
|
|
42
45
|
import { SETTING } from '@shell/config/settings';
|
|
46
|
+
import SelectOrCreateAuthSecret from '@shell/components/form/SelectOrCreateAuthSecret.vue';
|
|
47
|
+
import { generateRandomAlphaString } from '@shell/utils/string';
|
|
43
48
|
|
|
44
49
|
const VALUES_STATE = {
|
|
45
50
|
FORM: 'FORM',
|
|
@@ -91,7 +96,8 @@ export default {
|
|
|
91
96
|
UnitInput,
|
|
92
97
|
YamlEditor,
|
|
93
98
|
Wizard,
|
|
94
|
-
TypeDescription
|
|
99
|
+
TypeDescription,
|
|
100
|
+
SelectOrCreateAuthSecret
|
|
95
101
|
},
|
|
96
102
|
|
|
97
103
|
mixins: [
|
|
@@ -321,6 +327,8 @@ export default {
|
|
|
321
327
|
userValues = {};
|
|
322
328
|
}
|
|
323
329
|
|
|
330
|
+
this.userValues = userValues;
|
|
331
|
+
|
|
324
332
|
/*
|
|
325
333
|
Remove global values if they are identical to
|
|
326
334
|
the currently available information about the cluster
|
|
@@ -370,49 +378,84 @@ export default {
|
|
|
370
378
|
|
|
371
379
|
/* Look for annotation to say this app is a legacy migrated app (we look in either place for now) */
|
|
372
380
|
this.migratedApp = (this.existing?.spec?.chart?.metadata?.annotations?.[CATALOG_ANNOTATIONS.MIGRATED] === 'true');
|
|
381
|
+
|
|
382
|
+
if (this.repo.isSuseAppCollection) {
|
|
383
|
+
let defaultSelectedSecret = await this.$store.getters['cluster/byId'](SECRET, `cattle-system/${ this.repo.spec.clientSecret.name }`);
|
|
384
|
+
|
|
385
|
+
if (!defaultSelectedSecret) {
|
|
386
|
+
try {
|
|
387
|
+
defaultSelectedSecret = (await this.$store.dispatch('cluster/find', { type: SECRET, id: `cattle-system/${ this.repo.spec.clientSecret.name }` }));
|
|
388
|
+
} catch (e) {
|
|
389
|
+
// If cannot get the secret for any reason, permission or doesn't exist
|
|
390
|
+
// We can fallback to use the name only and with that name move forward.
|
|
391
|
+
// On only other required data is the DecodedData but not having it will only trigger a different flow.
|
|
392
|
+
defaultSelectedSecret = { name: this.repo.spec.clientSecret.name };
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
this.selectedSecret = defaultSelectedSecret;
|
|
397
|
+
this.defaultGeneratedNameForImagePullSecret = `${ this.selectedSecret.name }-image-pull-secret`;
|
|
398
|
+
this.generatedNameForImagePullSecret = `${ this.selectedSecret.name }-image-pull-secret-${ generateRandomAlphaString(5) }`;
|
|
399
|
+
this.appCoDataFetched = true;
|
|
400
|
+
await this.initializeDataForNamespaceChanges();
|
|
401
|
+
await this.setImagePullSecretData();
|
|
402
|
+
}
|
|
373
403
|
},
|
|
374
404
|
|
|
375
405
|
data() {
|
|
376
406
|
return {
|
|
377
|
-
defaultRegistrySetting:
|
|
378
|
-
customRegistrySetting:
|
|
379
|
-
serverUrlSetting:
|
|
380
|
-
chartValues:
|
|
381
|
-
clusterRegistry:
|
|
382
|
-
originalYamlValues:
|
|
383
|
-
previousYamlValues:
|
|
384
|
-
errors:
|
|
385
|
-
existing:
|
|
386
|
-
globalRegistry:
|
|
387
|
-
forceNamespace:
|
|
388
|
-
loadedVersion:
|
|
389
|
-
loadedVersionValues:
|
|
390
|
-
legacyApp:
|
|
391
|
-
mcapp:
|
|
392
|
-
mode:
|
|
393
|
-
value:
|
|
394
|
-
valuesComponent:
|
|
395
|
-
valuesYaml:
|
|
396
|
-
project:
|
|
397
|
-
migratedApp:
|
|
407
|
+
defaultRegistrySetting: '',
|
|
408
|
+
customRegistrySetting: '',
|
|
409
|
+
serverUrlSetting: null,
|
|
410
|
+
chartValues: null,
|
|
411
|
+
clusterRegistry: '',
|
|
412
|
+
originalYamlValues: null,
|
|
413
|
+
previousYamlValues: null,
|
|
414
|
+
errors: null,
|
|
415
|
+
existing: null,
|
|
416
|
+
globalRegistry: '',
|
|
417
|
+
forceNamespace: null,
|
|
418
|
+
loadedVersion: null,
|
|
419
|
+
loadedVersionValues: null,
|
|
420
|
+
legacyApp: null,
|
|
421
|
+
mcapp: null,
|
|
422
|
+
mode: null,
|
|
423
|
+
value: null,
|
|
424
|
+
valuesComponent: null,
|
|
425
|
+
valuesYaml: '',
|
|
426
|
+
project: null,
|
|
427
|
+
migratedApp: false,
|
|
398
428
|
defaultCmdOpts,
|
|
399
|
-
customCmdOpts:
|
|
400
|
-
autoInstallInfo:
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
429
|
+
customCmdOpts: { ...defaultCmdOpts },
|
|
430
|
+
autoInstallInfo: [],
|
|
431
|
+
nameDisabled: false,
|
|
432
|
+
preFormYamlOption: VALUES_STATE.YAML,
|
|
433
|
+
formYamlOption: VALUES_STATE.YAML,
|
|
434
|
+
showDiff: false,
|
|
435
|
+
showValuesComponent: true,
|
|
436
|
+
showQuestions: true,
|
|
437
|
+
showSlideIn: false,
|
|
438
|
+
shownReadmeWindows: [],
|
|
439
|
+
showCommandStep: false,
|
|
440
|
+
showCustomRegistryInput: false,
|
|
441
|
+
isNamespaceNew: false,
|
|
442
|
+
selectedSecret: null,
|
|
443
|
+
secrets: [],
|
|
444
|
+
secretsView: [],
|
|
445
|
+
appCoSecretsView: [],
|
|
446
|
+
selectedImagePullSecret: null,
|
|
447
|
+
appCoImagePullSecretView: [],
|
|
448
|
+
generatedNameForImagePullSecret: null,
|
|
449
|
+
defaultGeneratedNameForImagePullSecret: null,
|
|
450
|
+
defaultImagePullSecret: null,
|
|
451
|
+
clientSecret: null,
|
|
452
|
+
showCreateAuthSecret: false,
|
|
453
|
+
dontUseDefaultOption: null,
|
|
454
|
+
disabledCheckbox: false,
|
|
455
|
+
appCoDataFetched: false,
|
|
456
|
+
AUTH_TYPE,
|
|
457
|
+
CLUSTER_REPO_APPCO_AUTH_GENERATE_NAME,
|
|
458
|
+
stepBasic: {
|
|
416
459
|
name: 'basics',
|
|
417
460
|
label: this.t('catalog.install.steps.basics.label'),
|
|
418
461
|
subtext: this.t('catalog.install.steps.basics.subtext'),
|
|
@@ -465,6 +508,11 @@ export default {
|
|
|
465
508
|
return ignoreVariables(this.versionInfo);
|
|
466
509
|
},
|
|
467
510
|
|
|
511
|
+
hasDecodedDataAvailable() {
|
|
512
|
+
// Will return false if doesn't have access to neither the decodedData or the selectedSecret, or if the decodedData is empty
|
|
513
|
+
return this.selectedSecret?.decodedData;
|
|
514
|
+
},
|
|
515
|
+
|
|
468
516
|
namespaceIsNew() {
|
|
469
517
|
const all = this.$store.getters['cluster/all'](NAMESPACE);
|
|
470
518
|
const want = this.value?.metadata?.namespace;
|
|
@@ -480,6 +528,22 @@ export default {
|
|
|
480
528
|
return this.isRancher && !this.existing && this.forceNamespace;
|
|
481
529
|
},
|
|
482
530
|
|
|
531
|
+
selectedRepoAuthBanner() {
|
|
532
|
+
if (!this.selectedSecret) {
|
|
533
|
+
return '';
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (!this.dontUseDefaultOption && !this.selectedImagePullSecret) {
|
|
537
|
+
return `${ this.t('catalog.install.steps.basics.generatedImagePullSecretBannerFromPreviousAuth', { imagePullSecretName: this.defaultGeneratedNameForImagePullSecret, repoAuthenticationName: this.selectedSecret.name }, {}, true) }`;
|
|
538
|
+
} else if (!this.selectedImagePullSecret) {
|
|
539
|
+
return `${ this.t('catalog.install.steps.basics.generatedNewImagePullSecret', { imagePullSecretName: this.generatedNameForImagePullSecret }, {}, true) }`;
|
|
540
|
+
} else if (this.selectedImagePullSecret === this.defaultImagePullSecret?.name) {
|
|
541
|
+
return `${ this.t('catalog.install.steps.basics.usePreviouslyGeneratedImagePullSecretBanner', { imagePullSecretName: this.selectedImagePullSecret, repoAuthenticationName: this.selectedSecret.name }, {}, true) }`;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
return '';
|
|
545
|
+
},
|
|
546
|
+
|
|
483
547
|
projectOpts() {
|
|
484
548
|
const cluster = this.currentCluster;
|
|
485
549
|
const projects = this.$store.getters['management/all'](MANAGEMENT.PROJECT);
|
|
@@ -729,6 +793,13 @@ export default {
|
|
|
729
793
|
return global.systemDefaultRegistry !== undefined || global.cattle?.systemDefaultRegistry !== undefined;
|
|
730
794
|
},
|
|
731
795
|
|
|
796
|
+
setImagePullSecretDataTrigger() {
|
|
797
|
+
return `
|
|
798
|
+
${ this.defaultImagePullSecret?.name }
|
|
799
|
+
${ this.dontUseDefaultOption }
|
|
800
|
+
${ this.selectedImagePullSecret }`;
|
|
801
|
+
}
|
|
802
|
+
|
|
732
803
|
},
|
|
733
804
|
|
|
734
805
|
watch: {
|
|
@@ -741,7 +812,7 @@ export default {
|
|
|
741
812
|
}
|
|
742
813
|
},
|
|
743
814
|
|
|
744
|
-
'value.metadata.namespace'(neu, old) {
|
|
815
|
+
async 'value.metadata.namespace'(neu, old) {
|
|
745
816
|
if (neu) {
|
|
746
817
|
const ns = this.$store.getters['cluster/byId'](NAMESPACE, this.value.metadata.namespace);
|
|
747
818
|
|
|
@@ -751,6 +822,14 @@ export default {
|
|
|
751
822
|
this.project = project.replace(':', '/');
|
|
752
823
|
}
|
|
753
824
|
}
|
|
825
|
+
|
|
826
|
+
if (this.repo.isSuseAppCollection) {
|
|
827
|
+
await this.initializeDataForNamespaceChanges();
|
|
828
|
+
}
|
|
829
|
+
},
|
|
830
|
+
|
|
831
|
+
async setImagePullSecretDataTrigger() {
|
|
832
|
+
await this.setImagePullSecretData();
|
|
754
833
|
},
|
|
755
834
|
|
|
756
835
|
preFormYamlOption(neu, old) {
|
|
@@ -818,6 +897,9 @@ export default {
|
|
|
818
897
|
window.scrollTop = 0;
|
|
819
898
|
|
|
820
899
|
this.preFormYamlOption = this.valuesComponent || this.hasQuestions ? VALUES_STATE.FORM : VALUES_STATE.YAML;
|
|
900
|
+
|
|
901
|
+
// Register the image pull secret creation hook with lower priority (runs after SelectOrCreateAuthSecret at 99)
|
|
902
|
+
this.registerBeforeHook(this.createImagePullSecret, 'createImagePullSecret', 150);
|
|
821
903
|
},
|
|
822
904
|
|
|
823
905
|
beforeUnmount() {
|
|
@@ -854,6 +936,54 @@ export default {
|
|
|
854
936
|
}
|
|
855
937
|
},
|
|
856
938
|
|
|
939
|
+
async initializeDataForNamespaceChanges() {
|
|
940
|
+
// Skip the flow if the data still not fetched, it will trigger after fetching manually
|
|
941
|
+
if (!this.appCoDataFetched) {
|
|
942
|
+
try {
|
|
943
|
+
this.defaultImagePullSecret = await this.$store.dispatch('cluster/find', { type: SECRET, id: `${ this.targetNamespace }/${ this.repo.spec.clientSecret.name }-image-pull-secret` });
|
|
944
|
+
} catch (e) {
|
|
945
|
+
// If the secret doesn't exist, that's fine, we'll just create a new one later
|
|
946
|
+
this.defaultImagePullSecret = null;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// Reset if doesnt have the defaultImagePullSecret and doesn't have decoded data
|
|
950
|
+
// Disable the checkbox
|
|
951
|
+
const previousDontUseDefaultOption = this.dontUseDefaultOption;
|
|
952
|
+
let dontUseDefaultOption = false;
|
|
953
|
+
|
|
954
|
+
if (!this.hasDecodedDataAvailable && !this.defaultImagePullSecret) {
|
|
955
|
+
dontUseDefaultOption = true;
|
|
956
|
+
this.disabledCheckbox = true;
|
|
957
|
+
} else {
|
|
958
|
+
dontUseDefaultOption = false;
|
|
959
|
+
this.disabledCheckbox = false;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// On upgrade mode you cannot change namespace so this works as a full setup
|
|
963
|
+
if (!!this.existing) {
|
|
964
|
+
if (this.userValues?.global?.imagePullSecrets?.[0]) {
|
|
965
|
+
this.selectedImagePullSecret = this.userValues?.global?.imagePullSecrets[0];
|
|
966
|
+
}
|
|
967
|
+
this.dontUseDefaultOption = true;
|
|
968
|
+
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
this.dontUseDefaultOption = dontUseDefaultOption;
|
|
973
|
+
// Setting default values if changing to avoid duplicated trigger
|
|
974
|
+
if (this.dontUseDefaultOption !== previousDontUseDefaultOption) {
|
|
975
|
+
if (this.defaultImagePullSecret) {
|
|
976
|
+
// If the default option is used and the default secret already exists, use it
|
|
977
|
+
this.selectedImagePullSecret = this.defaultImagePullSecret.name;
|
|
978
|
+
this.chartValues.global.imagePullSecrets = [this.selectedImagePullSecret];
|
|
979
|
+
} else if (!this.defaultImagePullSecret) {
|
|
980
|
+
// Create new option with default generated name if the default option is selected
|
|
981
|
+
this.selectedImagePullSecret = null;
|
|
982
|
+
this.chartValues.global.imagePullSecrets = [this.defaultGeneratedNameForImagePullSecret];
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
},
|
|
857
987
|
async getClusterRegistry() {
|
|
858
988
|
const hasPermissionToSeeProvCluster = this.$store.getters[`management/schemaFor`](CAPI.RANCHER_CLUSTER);
|
|
859
989
|
|
|
@@ -897,6 +1027,30 @@ export default {
|
|
|
897
1027
|
}
|
|
898
1028
|
},
|
|
899
1029
|
|
|
1030
|
+
async setImagePullSecretData() {
|
|
1031
|
+
if (this.selectedSecret && this.repo.isSuseAppCollection && this.dontUseDefaultOption !== null) {
|
|
1032
|
+
if (!this.dontUseDefaultOption && this.defaultImagePullSecret) {
|
|
1033
|
+
// If the default option is used and the default secret already exists, use it
|
|
1034
|
+
this.selectedImagePullSecret = this.defaultImagePullSecret.name;
|
|
1035
|
+
this.chartValues.global.imagePullSecrets = [this.selectedImagePullSecret];
|
|
1036
|
+
} else if (!this.dontUseDefaultOption && !this.defaultImagePullSecret) {
|
|
1037
|
+
// Create new option with default generated name if the default option is selected
|
|
1038
|
+
this.selectedImagePullSecret = null;
|
|
1039
|
+
this.chartValues.global.imagePullSecrets = [this.defaultGeneratedNameForImagePullSecret];
|
|
1040
|
+
} else if (this.dontUseDefaultOption) {
|
|
1041
|
+
// If doesn't have the dontUseDefaultOption selected, it will respect the SELECT
|
|
1042
|
+
// New one with new username and password
|
|
1043
|
+
if (!this.selectedImagePullSecret) {
|
|
1044
|
+
this.chartValues.global.imagePullSecrets = [this.generatedNameForImagePullSecret];
|
|
1045
|
+
} else {
|
|
1046
|
+
this.chartValues.global.imagePullSecrets = [this.selectedImagePullSecret];
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
this.valuesYaml = saferDump(this.chartValues);
|
|
1051
|
+
}
|
|
1052
|
+
},
|
|
1053
|
+
|
|
900
1054
|
async getGlobalRegistry() {
|
|
901
1055
|
// Use the global registry as a fallback.
|
|
902
1056
|
// If it is an empty string, the container
|
|
@@ -973,12 +1127,42 @@ export default {
|
|
|
973
1127
|
}
|
|
974
1128
|
},
|
|
975
1129
|
|
|
1130
|
+
async createNamespaceIfNeeded() {
|
|
1131
|
+
const namespace = this.targetNamespace;
|
|
1132
|
+
|
|
1133
|
+
// Check if namespace already exists
|
|
1134
|
+
try {
|
|
1135
|
+
await this.$store.dispatch('cluster/find', {
|
|
1136
|
+
type: NAMESPACE_TYPE,
|
|
1137
|
+
id: namespace
|
|
1138
|
+
});
|
|
1139
|
+
|
|
1140
|
+
// Namespace exists, no need to create it
|
|
1141
|
+
return;
|
|
1142
|
+
} catch (e) {
|
|
1143
|
+
// Namespace doesn't exist, create it
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
// Create the namespace
|
|
1147
|
+
const nsResource = await this.$store.dispatch('cluster/createNamespace', { name: namespace });
|
|
1148
|
+
|
|
1149
|
+
// Apply any defaults and save
|
|
1150
|
+
nsResource.applyDefaults();
|
|
1151
|
+
await nsResource.save();
|
|
1152
|
+
},
|
|
1153
|
+
|
|
976
1154
|
async finish(btnCb) {
|
|
977
1155
|
try {
|
|
978
1156
|
const isUpgrade = !!this.existing;
|
|
979
1157
|
|
|
980
1158
|
this.errors = [];
|
|
981
1159
|
|
|
1160
|
+
// Create namespace if it doesn't exist (before hooks run)
|
|
1161
|
+
// And only if it is SUSE APP Collection, overall should just do the same flow
|
|
1162
|
+
if (!isUpgrade && this.isNamespaceNew && this.repo.isSuseAppCollection) {
|
|
1163
|
+
await this.createNamespaceIfNeeded();
|
|
1164
|
+
}
|
|
1165
|
+
|
|
982
1166
|
await this.applyHooks(BEFORE_SAVE_HOOKS);
|
|
983
1167
|
|
|
984
1168
|
const { errors, input } = this.actionInput(isUpgrade);
|
|
@@ -1170,7 +1354,8 @@ export default {
|
|
|
1170
1354
|
annotations: {
|
|
1171
1355
|
...migratedAnnotations,
|
|
1172
1356
|
[CATALOG_ANNOTATIONS.SOURCE_REPO_TYPE]: this.chart.repoType,
|
|
1173
|
-
[CATALOG_ANNOTATIONS.SOURCE_REPO_NAME]: this.chart.repoName
|
|
1357
|
+
[CATALOG_ANNOTATIONS.SOURCE_REPO_NAME]: this.chart.repoName,
|
|
1358
|
+
...(this.repo.isSuseAppCollection ? { [CATALOG_ANNOTATIONS.SUSE_APP_COLLECTION]: 'true' } : {}),
|
|
1174
1359
|
},
|
|
1175
1360
|
values,
|
|
1176
1361
|
};
|
|
@@ -1309,6 +1494,49 @@ export default {
|
|
|
1309
1494
|
step[prop] = update[prop];
|
|
1310
1495
|
}
|
|
1311
1496
|
}
|
|
1497
|
+
},
|
|
1498
|
+
|
|
1499
|
+
async createImagePullSecret() {
|
|
1500
|
+
if (!this.repo.isSuseAppCollection) {
|
|
1501
|
+
return;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
let imagePullSecretName = '';
|
|
1505
|
+
|
|
1506
|
+
// If wants to use the default one, it will create the Image Pull Secret
|
|
1507
|
+
if (!this.dontUseDefaultOption && !this.selectedImagePullSecret && this.hasDecodedDataAvailable) {
|
|
1508
|
+
// Create the secret for app collections
|
|
1509
|
+
const secret = await this.$store.dispatch('cluster/create', {
|
|
1510
|
+
type: SECRET,
|
|
1511
|
+
_type: 'kubernetes.io/dockerconfigjson',
|
|
1512
|
+
metadata: {
|
|
1513
|
+
name: this.defaultGeneratedNameForImagePullSecret,
|
|
1514
|
+
namespace: this.targetNamespace,
|
|
1515
|
+
},
|
|
1516
|
+
data: {}
|
|
1517
|
+
});
|
|
1518
|
+
|
|
1519
|
+
const registryHost = this.repo?.spec?.url ? new URL(this.repo.spec.url).host : '';
|
|
1520
|
+
const config = { auths: { [registryHost]: this.selectedSecret?.decodedData } };
|
|
1521
|
+
const json = JSON.stringify(config);
|
|
1522
|
+
|
|
1523
|
+
secret.setData('.dockerconfigjson', json);
|
|
1524
|
+
|
|
1525
|
+
const result = await secret.save();
|
|
1526
|
+
|
|
1527
|
+
// Now use the secret to add to the input
|
|
1528
|
+
imagePullSecretName = result.id.split('/')[1];
|
|
1529
|
+
|
|
1530
|
+
// Now if it is not default, and there is a selectedImagePullSecret, ideally we should use it
|
|
1531
|
+
} else if (this.dontUseDefaultOption && !this.selectedImagePullSecret) {
|
|
1532
|
+
imagePullSecretName = this.selectedImagePullSecret;
|
|
1533
|
+
}
|
|
1534
|
+
// If the dontUseDefaultOption is false, than there is no need to change the data
|
|
1535
|
+
|
|
1536
|
+
// Finally add the imagePullSecrets to the input, if it is already a DOCKER_JSON, it can use what has been setup on the page
|
|
1537
|
+
if (imagePullSecretName) {
|
|
1538
|
+
this.chartValues.global.imagePullSecrets = [imagePullSecretName];
|
|
1539
|
+
}
|
|
1312
1540
|
}
|
|
1313
1541
|
},
|
|
1314
1542
|
};
|
|
@@ -1427,6 +1655,7 @@ export default {
|
|
|
1427
1655
|
</div>
|
|
1428
1656
|
</div>
|
|
1429
1657
|
<NameNsDescription
|
|
1658
|
+
v-if="showNameEditor"
|
|
1430
1659
|
v-model:value="value"
|
|
1431
1660
|
:description-hidden="true"
|
|
1432
1661
|
:mode="mode"
|
|
@@ -1456,6 +1685,52 @@ export default {
|
|
|
1456
1685
|
/>
|
|
1457
1686
|
</template>
|
|
1458
1687
|
</NameNsDescription>
|
|
1688
|
+
<div
|
|
1689
|
+
v-if="repo.isSuseAppCollection"
|
|
1690
|
+
class="mb-20"
|
|
1691
|
+
>
|
|
1692
|
+
<Banner
|
|
1693
|
+
color="info"
|
|
1694
|
+
class="mt-10"
|
|
1695
|
+
label-key="catalog.install.steps.basics.requiresImagePullSecret"
|
|
1696
|
+
/>
|
|
1697
|
+
<Checkbox
|
|
1698
|
+
v-model:value="dontUseDefaultOption"
|
|
1699
|
+
label-key="catalog.install.steps.basics.dontUseDefaultImagePullSecret"
|
|
1700
|
+
:disabled="disabledCheckbox"
|
|
1701
|
+
/>
|
|
1702
|
+
|
|
1703
|
+
<div v-if="dontUseDefaultOption">
|
|
1704
|
+
<SelectOrCreateAuthSecret
|
|
1705
|
+
v-model:value="selectedImagePullSecret"
|
|
1706
|
+
:mode="mode"
|
|
1707
|
+
data-testid="clusterrepo-auth-secret"
|
|
1708
|
+
:register-before-hook="registerBeforeHook"
|
|
1709
|
+
:namespace="value.namespace"
|
|
1710
|
+
:pre-select="{ selected: AUTH_TYPE._IMAGE_PULL_SECRET }"
|
|
1711
|
+
:limit-to-namespace="true"
|
|
1712
|
+
:in-store="inStore"
|
|
1713
|
+
:allow-ssh="false"
|
|
1714
|
+
:allow-none="false"
|
|
1715
|
+
:allow-basic="false"
|
|
1716
|
+
:generate-name="`${CLUSTER_REPO_APPCO_AUTH_GENERATE_NAME}image-pull-secret-`"
|
|
1717
|
+
:cache-secrets="true"
|
|
1718
|
+
:fixed-image-pull-secret="true"
|
|
1719
|
+
:client-generated-name="generatedNameForImagePullSecret"
|
|
1720
|
+
:image-pull-secret-docker-json-url-config="repo?.spec?.url"
|
|
1721
|
+
/>
|
|
1722
|
+
</div>
|
|
1723
|
+
<Banner
|
|
1724
|
+
v-if="selectedRepoAuthBanner"
|
|
1725
|
+
color="info"
|
|
1726
|
+
class="mt-10"
|
|
1727
|
+
>
|
|
1728
|
+
<span
|
|
1729
|
+
v-clean-html="selectedRepoAuthBanner"
|
|
1730
|
+
/>
|
|
1731
|
+
</Banner>
|
|
1732
|
+
</div>
|
|
1733
|
+
|
|
1459
1734
|
<Checkbox
|
|
1460
1735
|
v-model:value="showCommandStep"
|
|
1461
1736
|
class="mb-20"
|
|
@@ -98,7 +98,7 @@ export default {
|
|
|
98
98
|
},
|
|
99
99
|
subHeaderItems: chart.cardContent.subHeaderItems,
|
|
100
100
|
footerItems: chart.deploysOnWindows ? [{
|
|
101
|
-
icon: '
|
|
101
|
+
icon: 'tag-alt',
|
|
102
102
|
iconTooltip: { key: 'generic.tags' },
|
|
103
103
|
labels: [this.t('catalog.charts.deploysOnWindows')],
|
|
104
104
|
}] : [],
|
|
@@ -37,14 +37,15 @@ export default {
|
|
|
37
37
|
async refreshK8sMetadata(buttonDone) {
|
|
38
38
|
try {
|
|
39
39
|
await this.$store.dispatch('rancher/request', {
|
|
40
|
-
url:
|
|
41
|
-
method:
|
|
42
|
-
data:
|
|
40
|
+
url: '/v3/kontainerdrivers?action=refresh',
|
|
41
|
+
method: 'post',
|
|
42
|
+
data: { },
|
|
43
|
+
timeout: 15000,
|
|
43
44
|
});
|
|
44
45
|
|
|
45
46
|
buttonDone(true);
|
|
46
47
|
} catch (err) {
|
|
47
|
-
this.$store.dispatch('growl/fromError', { title: '
|
|
48
|
+
this.$store.dispatch('growl/fromError', { title: this.t('drivers.kontainer.refreshError', { error: err }) }, { root: true });
|
|
48
49
|
buttonDone(false);
|
|
49
50
|
}
|
|
50
51
|
}
|
|
@@ -18,7 +18,9 @@ export default {
|
|
|
18
18
|
product: SETTINGS,
|
|
19
19
|
// Will have one or t'other
|
|
20
20
|
resource: hasSettings ? MANAGEMENT.SETTING : MANAGEMENT.FEATURE,
|
|
21
|
-
}
|
|
21
|
+
},
|
|
22
|
+
// Used to keep the route on the redirected page when coming from a link with a hash
|
|
23
|
+
hash: this.$route.hash
|
|
22
24
|
});
|
|
23
25
|
}
|
|
24
26
|
};
|
|
@@ -47,6 +47,7 @@ describe('dashboard-store: getters', () => {
|
|
|
47
47
|
expect(urlForGetter('typeFoo', 'idBar')).toBe('protocol/urlFoo/idBar');
|
|
48
48
|
});
|
|
49
49
|
});
|
|
50
|
+
|
|
50
51
|
describe('dashboard-store > getters > urlOptions', () => {
|
|
51
52
|
// we're not testing function output based off of state or getter inputs here since they are dependencies
|
|
52
53
|
const state = { config: { baseUrl: 'protocol' } };
|
|
@@ -97,4 +98,111 @@ describe('dashboard-store: getters', () => {
|
|
|
97
98
|
expect(urlOptionsGetter('foo', { sortBy: 'bar', sortOrder: 'baz' })).toBe('foo');
|
|
98
99
|
});
|
|
99
100
|
});
|
|
101
|
+
|
|
102
|
+
describe('dashboard-store > getters > matchingLabelSelector', () => {
|
|
103
|
+
const { matchingLabelSelector } = getters;
|
|
104
|
+
const labelSelector = { matchLabels: { foo: 'bar' } };
|
|
105
|
+
const selectorString = 'foo=bar';
|
|
106
|
+
const type = 'pod';
|
|
107
|
+
const namespace = 'default';
|
|
108
|
+
const allResources = [{ id: '1' }];
|
|
109
|
+
const matchingResources = [{ id: '1' }];
|
|
110
|
+
|
|
111
|
+
it('returns all resources if store has a VAI page matching the selector', () => {
|
|
112
|
+
const state = {};
|
|
113
|
+
const rootState = {};
|
|
114
|
+
const gettersMock = {
|
|
115
|
+
normalizeType: jest.fn((t) => t),
|
|
116
|
+
havePage: jest.fn().mockReturnValue({
|
|
117
|
+
request: {
|
|
118
|
+
namespace,
|
|
119
|
+
pagination: {
|
|
120
|
+
filters: [],
|
|
121
|
+
labelSelector
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}),
|
|
125
|
+
all: jest.fn().mockReturnValue(allResources),
|
|
126
|
+
haveSelector: jest.fn(),
|
|
127
|
+
haveAll: jest.fn(),
|
|
128
|
+
matching: jest.fn(),
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const result = matchingLabelSelector(state, gettersMock, rootState)(type, labelSelector, namespace);
|
|
132
|
+
|
|
133
|
+
expect(result).toStrictEqual(allResources);
|
|
134
|
+
expect(gettersMock.all).toHaveBeenCalledWith(type);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('returns all resources if store has the specific selector cached', () => {
|
|
138
|
+
const state = {};
|
|
139
|
+
const rootState = {};
|
|
140
|
+
const gettersMock = {
|
|
141
|
+
normalizeType: jest.fn((t) => t),
|
|
142
|
+
havePage: jest.fn().mockReturnValue(null),
|
|
143
|
+
all: jest.fn().mockReturnValue(allResources),
|
|
144
|
+
haveSelector: jest.fn().mockReturnValue(true),
|
|
145
|
+
haveAll: jest.fn(),
|
|
146
|
+
matching: jest.fn(),
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const result = matchingLabelSelector(state, gettersMock, rootState)(type, labelSelector, namespace);
|
|
150
|
+
|
|
151
|
+
expect(result).toStrictEqual(allResources);
|
|
152
|
+
expect(gettersMock.haveSelector).toHaveBeenCalledWith(type, selectorString);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('returns matching resources if store has a page (subset)', () => {
|
|
156
|
+
const state = {};
|
|
157
|
+
const rootState = {};
|
|
158
|
+
const gettersMock = {
|
|
159
|
+
normalizeType: jest.fn((t) => t),
|
|
160
|
+
havePage: jest.fn().mockReturnValue({ request: {} }), // Truthy, but doesn't match first if
|
|
161
|
+
all: jest.fn(),
|
|
162
|
+
haveSelector: jest.fn().mockReturnValue(false),
|
|
163
|
+
haveAll: jest.fn(),
|
|
164
|
+
matching: jest.fn().mockReturnValue(matchingResources),
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const result = matchingLabelSelector(state, gettersMock, rootState)(type, labelSelector, namespace);
|
|
168
|
+
|
|
169
|
+
expect(result).toStrictEqual(matchingResources);
|
|
170
|
+
expect(gettersMock.matching).toHaveBeenCalledWith(type, selectorString, namespace);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('returns matching resources if store has all resources', () => {
|
|
174
|
+
const state = {};
|
|
175
|
+
const rootState = {};
|
|
176
|
+
const gettersMock = {
|
|
177
|
+
normalizeType: jest.fn((t) => t),
|
|
178
|
+
havePage: jest.fn().mockReturnValue(null),
|
|
179
|
+
all: jest.fn(),
|
|
180
|
+
haveSelector: jest.fn().mockReturnValue(false),
|
|
181
|
+
haveAll: jest.fn().mockReturnValue(true),
|
|
182
|
+
matching: jest.fn().mockReturnValue(matchingResources),
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const result = matchingLabelSelector(state, gettersMock, rootState)(type, labelSelector, namespace);
|
|
186
|
+
|
|
187
|
+
expect(result).toStrictEqual(matchingResources);
|
|
188
|
+
expect(gettersMock.matching).toHaveBeenCalledWith(type, selectorString, namespace);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('returns empty array if no conditions met', () => {
|
|
192
|
+
const state = {};
|
|
193
|
+
const rootState = {};
|
|
194
|
+
const gettersMock = {
|
|
195
|
+
normalizeType: jest.fn((t) => t),
|
|
196
|
+
havePage: jest.fn().mockReturnValue(null),
|
|
197
|
+
all: jest.fn(),
|
|
198
|
+
haveSelector: jest.fn().mockReturnValue(false),
|
|
199
|
+
haveAll: jest.fn().mockReturnValue(false),
|
|
200
|
+
matching: jest.fn(),
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const result = matchingLabelSelector(state, gettersMock, rootState)(type, labelSelector, namespace);
|
|
204
|
+
|
|
205
|
+
expect(result).toStrictEqual([]);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
100
208
|
});
|