@rancher/shell 0.1.3 → 0.1.4

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.
Files changed (131) hide show
  1. package/assets/brand/suse/dark/rancher-logo.svg +1 -148
  2. package/assets/brand/suse/rancher-logo.svg +1 -130
  3. package/assets/images/featured/img1.jpg +0 -0
  4. package/assets/images/featured.jpg +0 -0
  5. package/assets/images/generic-plugin.svg +7 -0
  6. package/assets/styles/themes/_dark.scss +3 -0
  7. package/assets/styles/themes/_light.scss +3 -0
  8. package/assets/styles/themes/_suse.scss +1 -1
  9. package/assets/translations/en-us.yaml +183 -45
  10. package/assets/translations/zh-hans.yaml +21 -24
  11. package/components/AsyncButton.vue +17 -2
  12. package/components/ButtonDropdown.vue +4 -0
  13. package/components/Carousel.vue +291 -0
  14. package/components/CommunityLinks.vue +69 -18
  15. package/components/CruResource.vue +11 -3
  16. package/components/Dialog.vue +102 -0
  17. package/components/ExplorerMembers.vue +2 -4
  18. package/components/ExplorerProjectsNamespaces.vue +6 -7
  19. package/components/IconMessage.vue +9 -1
  20. package/components/LocaleSelector.vue +62 -29
  21. package/components/ResourceTable.vue +7 -2
  22. package/components/SimpleBox.vue +6 -4
  23. package/components/SortableTable/index.vue +11 -21
  24. package/components/Tabbed/Tab.vue +5 -0
  25. package/components/Tabbed/index.vue +29 -2
  26. package/components/auth/Principal.vue +1 -0
  27. package/components/fleet/FleetBundles.vue +8 -3
  28. package/components/fleet/FleetSummary.vue +6 -0
  29. package/components/form/KeyValue.vue +80 -58
  30. package/components/form/NameNsDescription.vue +10 -4
  31. package/components/form/ResourceTabs/index.vue +5 -1
  32. package/components/formatter/ClusterLink.vue +3 -7
  33. package/components/nav/NamespaceFilter.vue +3 -3
  34. package/components/nav/TopLevelMenu.vue +10 -28
  35. package/config/footer.js +13 -14
  36. package/config/labels-annotations.js +2 -1
  37. package/config/product/explorer.js +5 -4
  38. package/config/product/legacy.js +0 -47
  39. package/config/product/multi-cluster-apps.js +0 -12
  40. package/config/product/settings.js +12 -1
  41. package/config/product/uiplugins.js +17 -0
  42. package/config/settings.js +21 -2
  43. package/config/types.js +5 -1
  44. package/config/uiplugins.js +60 -0
  45. package/content/docs/en-us/getting-started.md +1 -26
  46. package/core/plugins.js +12 -0
  47. package/detail/provisioning.cattle.io.cluster.vue +3 -3
  48. package/detail/workload/index.vue +2 -2
  49. package/dialog/DiagnosticTimingsDialog.vue +116 -0
  50. package/dialog/RotateCertificatesDialog.vue +9 -3
  51. package/edit/auth/azuread.vue +28 -9
  52. package/edit/networking.k8s.io.ingress/index.vue +2 -2
  53. package/edit/persistentvolume/index.vue +3 -0
  54. package/edit/pod.vue +27 -0
  55. package/edit/provisioning.cattle.io.cluster/rke2.vue +76 -5
  56. package/edit/service.vue +7 -5
  57. package/edit/workload/__tests__/Upgrading.test.ts +1 -0
  58. package/edit/workload/index.vue +13 -1
  59. package/edit/workload/mixins/workload.js +13 -13
  60. package/edit/workload/storage/ContainerMountPaths.vue +240 -0
  61. package/edit/workload/storage/Mount.vue +1 -0
  62. package/edit/workload/storage/awsElasticBlockStore.vue +20 -1
  63. package/edit/workload/storage/azureDisk.vue +22 -2
  64. package/edit/workload/storage/azureFile.vue +20 -2
  65. package/edit/workload/storage/csi/index.vue +23 -1
  66. package/edit/workload/storage/gcePersistentDisk.vue +20 -2
  67. package/edit/workload/storage/index.vue +23 -49
  68. package/edit/workload/storage/vsphereVolume.vue +11 -1
  69. package/layouts/default.vue +14 -8
  70. package/layouts/home.vue +9 -4
  71. package/layouts/plain.vue +10 -5
  72. package/list/management.cattle.io.setting.vue +3 -3
  73. package/list/provisioning.cattle.io.cluster.vue +1 -1
  74. package/machine-config/harvester.vue +5 -3
  75. package/models/catalog.cattle.io.uiplugin.js +34 -0
  76. package/models/cluster/node.js +25 -2
  77. package/models/fleet.cattle.io.bundle.js +1 -1
  78. package/models/harvesterhci.io.management.cluster.js +11 -5
  79. package/models/provisioning.cattle.io.cluster.js +12 -6
  80. package/models/workload.js +5 -3
  81. package/nuxt.config.js +69 -25
  82. package/package.json +108 -109
  83. package/pages/auth/login.vue +1 -1
  84. package/pages/c/_cluster/apps/charts/index.vue +46 -1
  85. package/pages/c/_cluster/apps/charts/install.vue +10 -9
  86. package/pages/c/_cluster/explorer/index.vue +72 -9
  87. package/pages/c/_cluster/explorer/tools/index.vue +12 -5
  88. package/pages/c/_cluster/mcapps/index.vue +1 -1
  89. package/pages/c/_cluster/settings/brand.vue +0 -40
  90. package/pages/c/_cluster/settings/links.vue +200 -0
  91. package/pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue +232 -0
  92. package/pages/c/_cluster/uiplugins/InstallDialog.vue +242 -0
  93. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +284 -0
  94. package/pages/c/_cluster/uiplugins/RemoveUIPlugins.vue +130 -0
  95. package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +253 -0
  96. package/pages/c/_cluster/uiplugins/UninstallDialog.vue +115 -0
  97. package/pages/c/_cluster/uiplugins/index.vue +694 -0
  98. package/pages/diagnostic.vue +185 -101
  99. package/pages/docs/_doc.vue +3 -1
  100. package/pages/home.vue +21 -56
  101. package/pages/prefs.vue +108 -88
  102. package/pages/safeMode.vue +17 -0
  103. package/pages/support/index.vue +23 -15
  104. package/pkg/dynamic-importer.lib.js +4 -0
  105. package/plugins/dashboard-store/resource-class.js +2 -2
  106. package/plugins/formatters.js +15 -0
  107. package/plugins/plugin.js +56 -4
  108. package/plugins/steve/mutations.js +1 -1
  109. package/plugins/steve/subscribe.js +94 -72
  110. package/plugins/steve/web-worker.steve-sub-worker.js +24 -15
  111. package/promptRemove/management.cattle.io.globalrole.vue +47 -0
  112. package/promptRemove/management.cattle.io.roletemplate.vue +47 -0
  113. package/promptRemove/mixin/roleDeletionCheck.js +97 -0
  114. package/scripts/publish-shell.sh +1 -1
  115. package/scripts/sync-shell-deps +37 -0
  116. package/store/catalog.js +9 -8
  117. package/store/i18n.js +10 -1
  118. package/store/prefs.js +16 -0
  119. package/store/type-map.js +32 -5
  120. package/store/uiplugins.ts +15 -61
  121. package/utils/__tests__/object.test.ts +0 -24
  122. package/utils/__tests__/selector.test.ts +1 -1
  123. package/utils/dynamic-importer.js +4 -0
  124. package/utils/grafana.js +2 -6
  125. package/utils/socket.js +41 -20
  126. package/utils/string.js +1 -7
  127. package/utils/validators/formRules/__tests__/index.test.ts +108 -0
  128. package/utils/validators/formRules/index.ts +9 -1
  129. package/yarn-error.log +195 -0
  130. package/pages/plugins.vue +0 -387
  131. package/server/verdaccio-middleware.js +0 -56
@@ -0,0 +1,116 @@
1
+ <script>
2
+ import { exceptionToErrorsArray } from '@shell/utils/error';
3
+
4
+ import AsyncButton from '@shell/components/AsyncButton';
5
+ import { Card } from '@components/Card';
6
+ import { Banner } from '@components/Banner';
7
+
8
+ export default {
9
+ components: {
10
+ Card,
11
+ AsyncButton,
12
+ Banner,
13
+ },
14
+
15
+ props: {
16
+ resources: {
17
+ type: Array,
18
+ required: true
19
+ }
20
+ },
21
+
22
+ data() {
23
+ return { errors: [] };
24
+ },
25
+
26
+ computed: {
27
+ config() {
28
+ return this.resources[0];
29
+ },
30
+
31
+ applyMode() {
32
+ return 'diagnostic';
33
+ },
34
+
35
+ title() {
36
+ return this.t('about.diagnostic.modal.title');
37
+ },
38
+
39
+ body() {
40
+ return this.t('about.diagnostic.modal.body');
41
+ }
42
+ },
43
+
44
+ methods: {
45
+ close() {
46
+ this.$emit('close');
47
+ },
48
+
49
+ apply(btnCb) {
50
+ try {
51
+ this.config.downloadData(btnCb);
52
+ this.close();
53
+ } catch (err) {
54
+ console.error(err); // eslint-disable-line
55
+ this.errors = exceptionToErrorsArray(err);
56
+ btnCb(false);
57
+ }
58
+ }
59
+ }
60
+ };
61
+ </script>
62
+
63
+ <template>
64
+ <Card class="prompt-restore" :show-highlight-border="false">
65
+ <h4 slot="title" class="text-default-text" v-html="title" />
66
+
67
+ <template slot="body">
68
+ <slot name="body">
69
+ <div class="pl-10 pr-10" style="min-height: 50px;">
70
+ <div class="row">
71
+ {{ body }}
72
+ </div>
73
+
74
+ <AsyncButton
75
+ mode="timing"
76
+ class="row mt-20"
77
+ @click="config.gatherResponseTimes"
78
+ />
79
+ </div>
80
+ </slot>
81
+ </template>
82
+
83
+ <div slot="actions" class="bottom">
84
+ <Banner v-for="(err, i) in errors" :key="i" color="error" :label="err" />
85
+ <div class="buttons">
86
+ <button class="btn role-secondary mr-10" @click="close">
87
+ {{ t('generic.cancel') }}
88
+ </button>
89
+
90
+ <AsyncButton
91
+ :mode="applyMode"
92
+ @click="apply"
93
+ />
94
+ </div>
95
+ </div>
96
+ </Card>
97
+ </template>
98
+ <style lang='scss' scoped>
99
+ .prompt-restore {
100
+ margin: 0;
101
+ }
102
+ .bottom {
103
+ display: flex;
104
+ flex-direction: column;
105
+ flex: 1;
106
+ .banner {
107
+ margin-top: 0
108
+ }
109
+ .buttons {
110
+ display: flex;
111
+ justify-content: flex-end;
112
+ width: 100%;
113
+ }
114
+ }
115
+
116
+ </style>
@@ -40,19 +40,25 @@ export default {
40
40
 
41
41
  serviceOptions() {
42
42
  if (this.cluster.isRke2) {
43
- return [
43
+ const options = [
44
44
  'admin',
45
45
  'api-server',
46
46
  'controller-manager',
47
47
  'scheduler',
48
- 'rke2-controller',
49
- 'rke2-server',
50
48
  'cloud-controller',
51
49
  'etcd',
52
50
  'auth-proxy',
53
51
  'kubelet',
54
52
  'kube-proxy'
55
53
  ];
54
+
55
+ if ( this.cluster.isK3s ) {
56
+ options.push('k3s-controller', 'k3s-server');
57
+ } else {
58
+ options.push('rke2-controller', 'rke2-server');
59
+ }
60
+
61
+ return options.sort();
56
62
  }
57
63
  // For RKE1 clusters, Norman provides the list of service options:
58
64
  // type RotateCertificateInput struct {
@@ -1,4 +1,5 @@
1
1
  <script>
2
+ import isEqual from 'lodash/isEqual';
2
3
  import Loading from '@shell/components/Loading';
3
4
  import CreateEditView from '@shell/mixins/create-edit-view';
4
5
  import CruResource from '@shell/components/CruResource';
@@ -76,6 +77,8 @@ export default {
76
77
  data() {
77
78
  return {
78
79
  endpoint: 'standard',
80
+ oldEndpoint: false,
81
+
79
82
  // Storing the applicationSecret is necessary because norman doesn't support returning secrets and when we
80
83
  // override the steve authconfig with a norman config the applicationSecret is lost
81
84
  applicationSecret: this.value.applicationSecret
@@ -147,9 +150,6 @@ export default {
147
150
  handler() {
148
151
  this.model.accessMode = this.model.accessMode || 'unrestricted';
149
152
  this.model.rancherUrl = this.model.rancherUrl || this.replyUrl;
150
- if (this.endpoint !== 'custom') {
151
- this.setEndpoints(this.endpoint);
152
- }
153
153
 
154
154
  if (this.model.applicationSecret) {
155
155
  this.$set(this, 'applicationSecret', this.model.applicationSecret);
@@ -162,11 +162,13 @@ export default {
162
162
  methods: {
163
163
  setEndpoints(endpoint) {
164
164
  if (this.editConfig || !this.model.enabled) {
165
- Object.keys(ENDPOINT_MAPPING[endpoint]).forEach((key) => {
165
+ const endpointType = this.oldEndpoint && endpoint !== 'custom' ? OLD_ENDPOINTS : ENDPOINT_MAPPING;
166
+
167
+ Object.keys(endpointType[endpoint]).forEach((key) => {
166
168
  this.$set(
167
169
  this.model,
168
170
  key,
169
- ENDPOINT_MAPPING[endpoint][key].replace(
171
+ endpointType[endpoint][key].replace(
170
172
  TENANT_ID_TOKEN,
171
173
  this.model.tenantId
172
174
  )
@@ -176,13 +178,30 @@ export default {
176
178
  },
177
179
 
178
180
  setInitialEndpoint(endpoint) {
179
- const endpointKey = Object.keys(ENDPOINT_MAPPING).find(key => ENDPOINT_MAPPING[key].graphEndpoint === endpoint);
181
+ const newEndpointKey = this.determineEndpointKeyType(ENDPOINT_MAPPING);
182
+ const oldEndpointKey = Object.keys(OLD_ENDPOINTS).find(key => OLD_ENDPOINTS[key].graphEndpoint === endpoint);
180
183
 
181
- if ( endpointKey ) {
182
- this.endpoint = endpointKey;
184
+ if ( oldEndpointKey ) {
185
+ this.endpoint = this.determineEndpointKeyType(OLD_ENDPOINTS);
186
+ this.oldEndpoint = true;
183
187
  } else {
184
- this.endpoint = 'custom';
188
+ this.endpoint = newEndpointKey;
189
+ }
190
+ },
191
+
192
+ determineEndpointKeyType(endpointTypes) {
193
+ let out = 'custom';
194
+
195
+ for ( const [endpointKey, endpointKeyValues] of Object.entries(endpointTypes) ) {
196
+ const mappedValues = Object.values(endpointKeyValues).map(endpoint => endpoint.replace(TENANT_ID_TOKEN, this.model?.tenantId));
197
+ const valuesToCheck = Object.keys(endpointKeyValues).map(key => this.value[key]);
198
+
199
+ if ( isEqual(mappedValues, valuesToCheck) ) {
200
+ out = endpointKey;
201
+ }
185
202
  }
203
+
204
+ return out;
186
205
  },
187
206
 
188
207
  getNewApplicationSecret() {
@@ -65,7 +65,7 @@ export default {
65
65
  path: 'metadata.name', rules: ['required', 'hostname'], translationKey: 'nameNsDescription.name.label'
66
66
  },
67
67
  {
68
- path: 'spec.rules.host', rules: ['hostname'], translationKey: 'ingress.rules.requestHost.label'
68
+ path: 'spec.rules.host', rules: ['wildcardHostname'], translationKey: 'ingress.rules.requestHost.label'
69
69
  },
70
70
  {
71
71
  path: 'spec.rules.http.paths.path', rules: ['absolutePath'], translationKey: 'ingress.rules.path.label'
@@ -83,7 +83,7 @@ export default {
83
83
  {
84
84
  path: 'spec.defaultBackend.service.port.number', rules: ['required', 'requiredInt', 'portNumber'], translationKey: 'ingress.defaultBackend.port.label'
85
85
  },
86
- { path: 'spec.tls.hosts', rules: ['required', 'hostname'] }
86
+ { path: 'spec.tls.hosts', rules: ['required', 'wildcardHostname'] }
87
87
  ],
88
88
  fvReportedValidationPaths: ['spec.rules.http.paths.backend.service.port.number', 'spec.rules.http.paths.path', 'spec.rules.http.paths.backend.service.name']
89
89
  };
@@ -295,4 +295,7 @@ export default {
295
295
  max-height: 100%;
296
296
  height: 100%;
297
297
  }
298
+ .info-box {
299
+ flex-grow: 0;
300
+ }
298
301
  </style>
package/edit/pod.vue ADDED
@@ -0,0 +1,27 @@
1
+ <script>
2
+ import CreateEditView from '@shell/mixins/create-edit-view';
3
+ import FormValidation from '@shell/mixins/form-validation';
4
+ import WorkLoadMixin from '@shell/edit/workload/mixins/workload';
5
+ import Workload from '@shell/edit/workload/index.vue';
6
+
7
+ export default {
8
+ name: 'WorkloadDeployments',
9
+ components: { Workload },
10
+ mixins: [CreateEditView, FormValidation, WorkLoadMixin],
11
+ props: {
12
+ value: {
13
+ type: Object,
14
+ required: true,
15
+ },
16
+
17
+ mode: {
18
+ type: String,
19
+ default: 'create',
20
+ },
21
+ },
22
+ };
23
+ </script>
24
+
25
+ <template>
26
+ <Workload v-bind="$props" />
27
+ </template>
@@ -14,7 +14,8 @@ import {
14
14
  NORMAN,
15
15
  SCHEMA,
16
16
  DEFAULT_WORKSPACE,
17
- SECRET
17
+ SECRET,
18
+ HCI,
18
19
  } from '@shell/config/types';
19
20
  import { _CREATE, _EDIT, _VIEW } from '@shell/config/query-params';
20
21
 
@@ -311,7 +312,8 @@ export default {
311
312
  clusterIsAlreadyCreated: !!this.value.id,
312
313
  fvFormRuleSets: [{
313
314
  path: 'metadata.name', rules: ['subDomain'], translationKey: 'nameNsDescription.name.label'
314
- }]
315
+ }],
316
+ harvesterVersionRange: {},
315
317
  };
316
318
  },
317
319
 
@@ -499,7 +501,7 @@ export default {
499
501
  const isExternal = opt === 'external';
500
502
  let disabled = false;
501
503
 
502
- if (this.isHarvesterExternalCredential && isPreferred) {
504
+ if ((this.isHarvesterExternalCredential || this.isHarvesterIncompatible) && isPreferred) {
503
505
  disabled = true;
504
506
  }
505
507
 
@@ -850,6 +852,33 @@ export default {
850
852
  isHarvesterExternalCredential() {
851
853
  return this.credential?.harvestercredentialConfig?.clusterType === 'external';
852
854
  },
855
+
856
+ isHarvesterIncompatible() {
857
+ let ccmRke2Version = (this.chartVersions['harvester-cloud-provider'] || {})['version'];
858
+ let csiRke2Version = (this.chartVersions['harvester-csi-driver'] || {})['version'];
859
+
860
+ const ccmVersion = this.harvesterVersionRange?.['harvester-cloud-provider'];
861
+ const csiVersion = this.harvesterVersionRange?.['harvester-csi-provider'];
862
+
863
+ if ((ccmRke2Version || '').endsWith('00')) {
864
+ ccmRke2Version = ccmRke2Version.slice(0, -2);
865
+ }
866
+
867
+ if ((csiRke2Version || '').endsWith('00')) {
868
+ csiRke2Version = csiRke2Version.slice(0, -2);
869
+ }
870
+
871
+ if (ccmVersion && csiVersion) {
872
+ if (semver.satisfies(ccmRke2Version, ccmVersion) &&
873
+ semver.satisfies(csiRke2Version, csiVersion)) {
874
+ return false;
875
+ } else {
876
+ return true;
877
+ }
878
+ } else {
879
+ return false;
880
+ }
881
+ },
853
882
  },
854
883
 
855
884
  watch: {
@@ -867,7 +896,10 @@ export default {
867
896
  credentialId(val) {
868
897
  if ( val ) {
869
898
  this.credential = this.$store.getters['rancher/byId'](NORMAN.CLOUD_CREDENTIAL, this.credentialId);
870
- this.setHarvesterDefaultCloudProvider();
899
+
900
+ if (this.isHarvesterDriver) {
901
+ this.setHarvesterVersionRange();
902
+ }
871
903
  } else {
872
904
  this.credential = null;
873
905
  }
@@ -1562,12 +1594,36 @@ export default {
1562
1594
  get,
1563
1595
 
1564
1596
  setHarvesterDefaultCloudProvider() {
1565
- if (this.isHarvesterDriver && this.mode === _CREATE && this.agentConfig['cloud-provider-name'] === undefined && !this.isHarvesterExternalCredential) {
1597
+ if (this.isHarvesterDriver &&
1598
+ this.mode === _CREATE &&
1599
+ !this.agentConfig['cloud-provider-name'] &&
1600
+ !this.isHarvesterExternalCredential &&
1601
+ !this.isHarvesterIncompatible
1602
+ ) {
1566
1603
  this.agentConfig['cloud-provider-name'] = HARVESTER;
1567
1604
  } else {
1568
1605
  this.agentConfig['cloud-provider-name'] = '';
1569
1606
  }
1570
1607
  },
1608
+
1609
+ async setHarvesterVersionRange() {
1610
+ const clusterId = this.credential?.decodedData?.clusterId;
1611
+ const clusterType = this.credential?.decodedData?.clusterType;
1612
+
1613
+ if (clusterId && clusterType === 'imported') {
1614
+ const url = `/k8s/clusters/${ clusterId }/v1`;
1615
+ const res = await this.$store.dispatch('cluster/request', { url: `${ url }/${ HCI.SETTING }s` });
1616
+
1617
+ const version = (res?.data || []).find(s => s.id === 'harvester-csi-ccm-versions');
1618
+
1619
+ if (version) {
1620
+ this.harvesterVersionRange = JSON.parse(version.value || version.default || '{}');
1621
+ } else {
1622
+ this.harvesterVersionRange = {};
1623
+ }
1624
+ }
1625
+ this.setHarvesterDefaultCloudProvider();
1626
+ },
1571
1627
  },
1572
1628
  };
1573
1629
  </script>
@@ -1686,6 +1742,14 @@ export default {
1686
1742
  <Tab name="basic" label-key="cluster.tabs.basic" :weight="11" @active="refreshYamls">
1687
1743
  <Banner v-if="!haveArgInfo" color="warning" label="Configuration information is not available for the selected Kubernetes version. The options available in this screen will be limited, you may want to use the YAML editor." />
1688
1744
  <Banner v-if="showk8s21LegacyWarning" color="warning" :label="t('cluster.legacyWarning')" />
1745
+ <Banner
1746
+ v-if="isHarvesterDriver && isHarvesterIncompatible && showCloudProvider"
1747
+ color="warning"
1748
+ >
1749
+ <span
1750
+ v-html="t('cluster.harvester.warning.cloudProvider.incompatible', null, true)"
1751
+ />
1752
+ </Banner>
1689
1753
  <div class="row mb-10">
1690
1754
  <div class="col span-6">
1691
1755
  <LabeledSelect
@@ -1788,6 +1852,12 @@ export default {
1788
1852
  </div>
1789
1853
  </div>
1790
1854
 
1855
+ <div v-if="serverConfig.cni === 'cilium' && value.spec.enableNetworkPolicy" class="row">
1856
+ <div class="col span-12">
1857
+ <Banner color="info" :label="t('cluster.rke2.enableNetworkPolicy.warning')" />
1858
+ </div>
1859
+ </div>
1860
+
1791
1861
  <div class="spacer" />
1792
1862
 
1793
1863
  <div v-if="serverArgs.disable" class="row">
@@ -2089,6 +2159,7 @@ export default {
2089
2159
  </Tab>
2090
2160
 
2091
2161
  <Tab
2162
+ v-if="haveArgInfo || agentArgs['protect-kernel-defaults']"
2092
2163
  name="advanced"
2093
2164
  label-key="cluster.tabs.advanced"
2094
2165
  :weight="-1"
package/edit/service.vue CHANGED
@@ -198,7 +198,8 @@ export default {
198
198
  },
199
199
 
200
200
  watch: {
201
- 'value.spec.selector': 'updateMatchingPods',
201
+ 'value.metadata.namespace': 'updateMatchingPods',
202
+ 'value.spec.selector': 'updateMatchingPods',
202
203
  'value.spec.sessionAffinity'(val) {
203
204
  if (val === 'ClientIP') {
204
205
  this.value.spec.sessionAffinityConfig = { clientIP: { timeoutSeconds: null } };
@@ -240,21 +241,22 @@ export default {
240
241
 
241
242
  methods: {
242
243
  updateMatchingPods: throttle(function() {
243
- const { allPods, value: { spec: { selector = { } } } } = this;
244
+ const { value: { spec: { selector = { } } } } = this;
245
+ const allInNamespace = this.allPods.filter(pod => pod.metadata.namespace === this.value?.metadata?.namespace);
244
246
 
245
247
  if (isEmpty(selector)) {
246
248
  this.matchingPods = {
247
249
  matched: 0,
248
- total: allPods.length,
250
+ total: allInNamespace.length,
249
251
  none: true,
250
252
  sample: null,
251
253
  };
252
254
  } else {
253
- const match = matching(allPods, selector);
255
+ const match = matching(allInNamespace, selector);
254
256
 
255
257
  this.matchingPods = {
256
258
  matched: match.length,
257
- total: allPods.length,
259
+ total: allInNamespace.length,
258
260
  none: match.length === 0,
259
261
  sample: match[0] ? match[0].nameDisplay : null,
260
262
  };
@@ -26,6 +26,7 @@ describe('component: Upgrading', () => {
26
26
  });
27
27
 
28
28
  // TODO: #6179: Integrate test with component fix, as the scope is not to check the value of the input
29
+ // eslint-disable-next-line jest/no-disabled-tests
29
30
  it.skip.each([
30
31
  ['surge', 'maxSurge', '%'],
31
32
  ['unavailable', 'maxUnavailable', '%'],
@@ -197,6 +197,19 @@ export default {
197
197
  <Tab :label="t('workload.container.titles.securityContext')" name="securityContext" :weight="tabWeightMap['securityContext']">
198
198
  <Security v-model="allContainers[i].securityContext" :mode="mode" />
199
199
  </Tab>
200
+ <Tab :label="t('workload.storage.title')" name="storage" :weight="tabWeightMap['storage']">
201
+ <ContainerMountPaths
202
+ v-model="podTemplateSpec"
203
+ :namespace="value.metadata.namespace"
204
+ :register-before-hook="registerBeforeHook"
205
+ :mode="mode"
206
+ :secrets="namespacedSecrets"
207
+ :config-maps="namespacedConfigMaps"
208
+ :container="allContainers[i]"
209
+ :save-pvc-hook-name="savePvcHookName"
210
+ @removePvcForm="clearPvcFormState"
211
+ />
212
+ </Tab>
200
213
  </Tabbed>
201
214
  </Tab>
202
215
  <Tab v-if="!isPod" :label="nameDisplayFor(type)" :name="nameDisplayFor(type)" :weight="99">
@@ -220,7 +233,6 @@ export default {
220
233
  :mode="mode"
221
234
  :secrets="namespacedSecrets"
222
235
  :config-maps="namespacedConfigMaps"
223
- :container="container"
224
236
  :save-pvc-hook-name="savePvcHookName"
225
237
  @removePvcForm="clearPvcFormState"
226
238
  />
@@ -9,6 +9,7 @@ import {
9
9
  PVC,
10
10
  SERVICE_ACCOUNT,
11
11
  CAPI,
12
+ POD,
12
13
  } from '@shell/config/types';
13
14
  import Tab from '@shell/components/Tabbed/Tab';
14
15
  import CreateEditView from '@shell/mixins/create-edit-view';
@@ -36,6 +37,7 @@ import CruResource from '@shell/components/CruResource';
36
37
  import Command from '@shell/components/form/Command';
37
38
  import LifecycleHooks from '@shell/components/form/LifecycleHooks';
38
39
  import Storage from '@shell/edit/workload/storage';
40
+ import ContainerMountPaths from '@shell/edit/workload/storage/ContainerMountPaths.vue';
39
41
  import Labels from '@shell/components/form/Labels';
40
42
  import { RadioGroup } from '@components/Form/Radio';
41
43
  import { UI_MANAGED } from '@shell/config/labels-annotations';
@@ -89,6 +91,7 @@ export default {
89
91
  Upgrading,
90
92
  VolumeClaimTemplate,
91
93
  WorkloadPorts,
94
+ ContainerMountPaths
92
95
  },
93
96
 
94
97
  mixins: [CreateEditView],
@@ -148,7 +151,6 @@ export default {
148
151
  },
149
152
 
150
153
  data() {
151
- let defaultTab;
152
154
  let type = this.$route.params.resource;
153
155
  const createSidecar = !!this.$route.query.sidecar;
154
156
  const isInitContainer = !!this.$route.query.init;
@@ -159,24 +161,18 @@ export default {
159
161
 
160
162
  if (!this.value.spec) {
161
163
  this.value.spec = {};
162
- if (this.value.type === WORKLOAD_TYPES.POD) {
164
+ if (this.value.type === POD) {
163
165
  const podContainers = [{
164
166
  imagePullPolicy: 'Always',
165
167
  name: `container-0`,
166
168
  }];
167
169
 
168
- defaultTab = 'container-0';
169
-
170
170
  const podSpec = { template: { spec: { containers: podContainers, initContainers: [] } } };
171
171
 
172
172
  this.$set(this.value, 'spec', podSpec);
173
173
  }
174
174
  }
175
175
 
176
- if (this.mode === _CREATE) {
177
- defaultTab = 'container-0';
178
- }
179
-
180
176
  if ((this.mode === _EDIT || this.mode === _VIEW ) && this.value.type === 'pod' ) {
181
177
  const podSpec = { ...this.value.spec };
182
178
 
@@ -214,7 +210,6 @@ export default {
214
210
  imagePullPolicy: 'Always',
215
211
  name: `container-${ allContainers.length }`,
216
212
  });
217
- defaultTab = 'container-0';
218
213
 
219
214
  containers = podTemplateSpec.initContainers;
220
215
  }
@@ -224,8 +219,6 @@ export default {
224
219
  name: `container-${ allContainers.length }`,
225
220
  };
226
221
 
227
- defaultTab = 'container-0';
228
-
229
222
  containers.push(container);
230
223
  } else {
231
224
  container = containers[0];
@@ -261,7 +254,6 @@ export default {
261
254
  path: 'image', rootObject: this.container, rules: ['required'], translationKey: 'workload.container.image'
262
255
  }],
263
256
  fvReportedValidationPaths: ['spec'],
264
- defaultTab
265
257
 
266
258
  };
267
259
  },
@@ -271,6 +263,14 @@ export default {
271
263
  return { general: this.fvGetPathErrors(['image'])?.length > 0 };
272
264
  },
273
265
 
266
+ defaultTab() {
267
+ if (!!this.$route.query.sidecar || this.$route.query.init || this.mode === _CREATE) {
268
+ return 'container-0';
269
+ }
270
+
271
+ return this.allContainers.length ? this.allContainers[0].name : '';
272
+ },
273
+
274
274
  isEdit() {
275
275
  return this.mode === _EDIT;
276
276
  },
@@ -297,7 +297,7 @@ export default {
297
297
  },
298
298
 
299
299
  isPod() {
300
- return this.value.type === WORKLOAD_TYPES.POD;
300
+ return this.value.type === POD;
301
301
  },
302
302
 
303
303
  isStatefulSet() {