@rancher/shell 2.0.2-rc.1 → 2.0.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.
Files changed (37) hide show
  1. package/assets/translations/en-us.yaml +44 -30
  2. package/components/PromptRemove.vue +8 -3
  3. package/components/ResourceDetail/Masthead.vue +1 -0
  4. package/components/fleet/FleetClusters.vue +0 -3
  5. package/components/formatter/CloudCredExpired.vue +69 -0
  6. package/components/formatter/Date.vue +1 -1
  7. package/components/nav/Header.vue +9 -5
  8. package/components/nav/TopLevelMenu.vue +115 -51
  9. package/components/nav/__tests__/TopLevelMenu.test.ts +53 -27
  10. package/config/labels-annotations.js +2 -0
  11. package/detail/catalog.cattle.io.app.vue +17 -4
  12. package/detail/fleet.cattle.io.cluster.vue +11 -9
  13. package/detail/fleet.cattle.io.gitrepo.vue +1 -1
  14. package/edit/provisioning.cattle.io.cluster/rke2.vue +13 -0
  15. package/list/provisioning.cattle.io.cluster.vue +56 -5
  16. package/mixins/chart.js +6 -2
  17. package/models/catalog.cattle.io.app.js +108 -21
  18. package/models/cloudcredential.js +159 -2
  19. package/models/fleet.cattle.io.gitrepo.js +4 -13
  20. package/models/management.cattle.io.cluster.js +13 -2
  21. package/models/provisioning.cattle.io.cluster.js +37 -3
  22. package/package.json +1 -1
  23. package/pages/c/_cluster/apps/charts/install.vue +2 -1
  24. package/pages/c/_cluster/explorer/__tests__/index.test.ts +1 -1
  25. package/pages/c/_cluster/explorer/index.vue +1 -2
  26. package/pages/c/_cluster/fleet/index.vue +11 -5
  27. package/pages/c/_cluster/manager/cloudCredential/index.vue +68 -4
  28. package/pages/c/_cluster/uiplugins/index.vue +4 -2
  29. package/pages/home.vue +1 -0
  30. package/scripts/extension/bundle +1 -1
  31. package/scripts/publish-shell.sh +3 -4
  32. package/scripts/typegen.sh +26 -23
  33. package/types/shell/index.d.ts +4595 -0
  34. package/utils/cluster.js +1 -1
  35. package/utils/string.js +9 -0
  36. package/utils/v-sphere.ts +251 -0
  37. package/shell/types/shell/index.d.ts +0 -2
@@ -360,8 +360,8 @@ addClusterMemberDialog:
360
360
  title: Add Cluster Member
361
361
 
362
362
  addonConfigConfirmation:
363
- title: Add-On Config Reset
364
- body: Changing the Kubernetes Version can reset the Add-On Config values. You should check that the values are as expected before continuing.
363
+ title: Add-On Reset
364
+ body: Changing the Kubernetes Version can reset Add-On values. You should check that the values are as expected before continuing.
365
365
 
366
366
  addProjectMemberDialog:
367
367
  title: Add Project Member
@@ -1187,38 +1187,38 @@ cluster:
1187
1187
  <code>Cluster Management > Advanced > JWT Authentication</code>"
1188
1188
  addonChart:
1189
1189
  rancher-vsphere-cpi:
1190
- label: vSphere CPI
1191
- configuration: vSphere CPI Configuration
1190
+ label: "Add-on: vSphere CPI"
1191
+ configuration: vSphere CPI
1192
1192
  rancher-vsphere-csi:
1193
- label: vSphere CSI
1194
- configuration: vSphere CSI Configuration
1193
+ label: "Add-on: vSphere CSI"
1194
+ configuration: vSphere CSI
1195
1195
  rke2-calico:
1196
- label: Calico
1197
- configuration: Calico Configuration
1196
+ label: "Add-on: Calico"
1197
+ configuration: Calico
1198
1198
  rke2-calico-crd:
1199
- label: Calico
1200
- configuration: Calico Configuration
1199
+ label: "Add-on: Calico"
1200
+ configuration: Calico
1201
1201
  rke2-canal:
1202
- label: Canal
1203
- configuration: Canal Configuration
1202
+ label: "Add-on: Canal"
1203
+ configuration: Canal
1204
1204
  rke2-cilium:
1205
- label: Cilium
1206
- configuration: Cilium Configuration
1205
+ label: "Add-on: Cilium"
1206
+ configuration: Cilium
1207
1207
  rke2-coredns:
1208
- label: CoreDNS
1209
- configuration: CoreDNS Configuration
1208
+ label: "Add-on: CoreDNS"
1209
+ configuration: CoreDNS
1210
1210
  rke2-ingress-nginx:
1211
- label: NGINX
1212
- configuration: NGINX Ingress Configuration
1211
+ label: "Add-on: NGINX"
1212
+ configuration: NGINX Ingress
1213
1213
  rke2-kube-proxy:
1214
- label: Kube Proxy
1215
- configuration: Kube Proxy Configuration
1214
+ label: "Add-on: Kube Proxy"
1215
+ configuration: Kube Proxy
1216
1216
  rke2-metrics-server:
1217
- label: Metrics Server
1218
- configuration: Metrics Server Configuration
1217
+ label: "Add-on: Metrics Server"
1218
+ configuration: Metrics Server
1219
1219
  rke2-multus:
1220
- label: Multus
1221
- configuration: Multus Configuration
1220
+ label: "Add-on: Multus"
1221
+ configuration: Multus
1222
1222
  agentEnvVars:
1223
1223
  label: Agent Environment
1224
1224
  detail: Add additional environment variables to the agent container. This is most commonly useful for configuring a HTTP proxy.
@@ -1234,7 +1234,7 @@ cluster:
1234
1234
  label: Google
1235
1235
  rancher-vsphere:
1236
1236
  label: vSphere
1237
- note: '<b>Important:</b> Configure the vSphere Cloud Provider and Storage Provider options in the Add-On Config tab.'
1237
+ note: '<b>Important:</b> Configure the vSphere Cloud Provider and Storage Provider options in the Add-on tabs.'
1238
1238
  harvester:
1239
1239
  label: Harvester
1240
1240
  copyConfig: Copy KubeConfig to Clipboard
@@ -1380,8 +1380,10 @@ cluster:
1380
1380
  macAddress: Mac Address
1381
1381
  macFormat: 'Invalid MAC address format.'
1382
1382
  vGpus:
1383
+ warnings:
1384
+ minimumAllocatable: It's highly recommended to select a vGPU with a number of allocatable devices greater than the number of nodes (Machine Count) to avoid "un-schedulable" errors after cluster updates.
1383
1385
  errors:
1384
- notAllocatable: '"VGPUs" not allocatable. There are not enough [{vGpus}] devices to be allocated to each node in machine pool [{pool}]'
1386
+ notAllocatable: '[{vGpu}] vGPU device is not allocatable; required: {allocated}, allocatable: {allocatable}'
1385
1387
  volume:
1386
1388
  title: Volumes
1387
1389
  volume: Volume
@@ -1745,7 +1747,7 @@ cluster:
1745
1747
  serverOs:
1746
1748
  label: OS
1747
1749
  addOns:
1748
- dependencyBanner: Add-On Configurations can vary between Kubernetes versions. Changing the Kubernetes version may reset the values below.
1750
+ dependencyBanner: Add-On Configuration can vary between Kubernetes versions. Changing the Kubernetes version may reset the values below.
1749
1751
  additionalManifest:
1750
1752
  title: Additional Manifest
1751
1753
  tooltip: 'Additional Kubernetes Manifest YAML to be applied to the cluster on startup.'
@@ -1809,11 +1811,18 @@ cluster:
1809
1811
  other { {pool_name}: The provided values for {fields} were not found in the list of expected values. This can happen with clusters provisioned outside of Rancher or when options for the provider have changed. }
1810
1812
  }
1811
1813
  rkeTemplateUpgrade: Template revision {name} available for upgrade
1814
+ cloudCredentials:
1815
+ renew: Renew Cloud Credentials
1816
+ expired: Cloud Credential expired, please Renew Cloud Credentials
1817
+ expiring: Cloud Credential will expire on { expires }, please Renew Cloud Credentials
1818
+ banners:
1819
+ expiring: "{count} {count, plural, =1 { Cluster has a Cloud Credential that expires soon} other { Clusters have Cloud Credentials that expire soon}}, please Renew Cloud {count, plural, =1 { Credential} other { Credentials}}"
1820
+ expired: "{count} {count, plural, =1 { Cluster has a Cloud Credential that has expired} other { Clusters have Cloud Credentials that have expired}}, please Renew Cloud {count, plural, =1 { Credential} other { Credentials}}"
1812
1821
 
1813
1822
  architecture:
1814
1823
  label:
1815
- unknown: Unknown
1816
- mixed: Mixed
1824
+ unknown: unknown
1825
+ mixed: mixed
1817
1826
 
1818
1827
  availabilityWarnings:
1819
1828
  node: Node {name} is inactive
@@ -5624,7 +5633,6 @@ tableHeaders:
5624
5633
  createdAt: Created At
5625
5634
  customVerbs: Custom Verbs
5626
5635
  description: Description
5627
- expires: Expires
5628
5636
  cpu: CPU
5629
5637
  currentReplicas: Current Replicas
5630
5638
  date: Date
@@ -5641,6 +5649,7 @@ tableHeaders:
5641
5649
  distinguisherMethod: Distinguisher Method
5642
5650
  effect: Effect
5643
5651
  endpoints: Endpoints
5652
+ expires: Expires
5644
5653
  firstSeen: First Seen
5645
5654
  fleetBundleType: Type
5646
5655
  flow: Flow
@@ -7719,6 +7728,11 @@ volumeClaimTemplate:
7719
7728
  manager:
7720
7729
  cloudCredentials:
7721
7730
  label: Cloud Credentials
7731
+ renew: Renew
7732
+ expired: Expired
7733
+ banners:
7734
+ expiring: "{count} Cloud {count, plural, =1 { Credential expires soon} other { Credentials expire soon}}, please Renew"
7735
+ expired: "{count} Cloud {count, plural, =1 { Credential has expired} other { Credentials have expired}}, please Renew"
7722
7736
  drivers:
7723
7737
  label: Drivers
7724
7738
  rkeTemplates:
@@ -105,8 +105,8 @@ export default {
105
105
  return null;
106
106
  }
107
107
 
108
- if (this.toRemove[0].doneLocationRemove) {
109
- return this.toRemove[0].doneLocationRemove;
108
+ if (this.toRemove[0].doneOverride) {
109
+ return this.toRemove[0].doneOverride;
110
110
  }
111
111
 
112
112
  const currentRoute = this.toRemove[0].currentRoute();
@@ -300,7 +300,12 @@ export default {
300
300
  },
301
301
  done() {
302
302
  if ( this.cachedDoneLocation && !isEmpty(this.cachedDoneLocation) ) {
303
- this.currentRouter.push(this.cachedDoneLocation);
303
+ // Only push if the route will change otherwise we'll get a `NavigationDuplicated` exception
304
+ const resolvedRoute = this.currentRouter.resolve(this.cachedDoneLocation);
305
+
306
+ if (resolvedRoute.resolved.fullPath !== this.$route.fullPath) {
307
+ this.currentRouter.push(this.cachedDoneLocation);
308
+ }
304
309
  }
305
310
  this.close();
306
311
  },
@@ -516,6 +516,7 @@ export default {
516
516
  <button
517
517
  v-if="isView"
518
518
  ref="actions"
519
+ data-testid="mathead-action-menu"
519
520
  aria-haspopup="true"
520
521
  type="button"
521
522
  class="btn role-multi-action actions"
@@ -42,7 +42,6 @@ export default {
42
42
  value: 'status.display.readyBundles',
43
43
  sort: 'status.summary.ready',
44
44
  search: false,
45
- align: 'center',
46
45
  },
47
46
  {
48
47
  name: 'reposReady',
@@ -50,7 +49,6 @@ export default {
50
49
  value: 'status.readyGitRepos',
51
50
  sort: 'status.summary.ready',
52
51
  search: false,
53
- align: 'center',
54
52
  },
55
53
  FLEET_SUMMARY,
56
54
  {
@@ -62,7 +60,6 @@ export default {
62
60
  formatter: 'LiveDate',
63
61
  formatterOpts: { addSuffix: true },
64
62
  width: 120,
65
- align: 'right'
66
63
  },
67
64
  AGE,
68
65
  ];
@@ -0,0 +1,69 @@
1
+ <script>
2
+
3
+ export default {
4
+ props: {
5
+ value: {
6
+ type: [Number, String],
7
+ required: true
8
+ },
9
+
10
+ row: {
11
+ type: Object,
12
+ default: () => {
13
+ return {};
14
+ }
15
+ },
16
+
17
+ verbose: {
18
+ type: Boolean,
19
+ default: false,
20
+ }
21
+
22
+ },
23
+
24
+ computed: {
25
+ outputString() {
26
+ return this.verbose ? this.verboseOutputString : this.row.expiresString;
27
+ },
28
+
29
+ verboseOutputString() {
30
+ const expireData = this.row?.expireData;
31
+
32
+ if (expireData?.expired) {
33
+ return this.t('cluster.cloudCredentials.expired');
34
+ } else if (expireData?.expiring) {
35
+ return this.t('cluster.cloudCredentials.expiring', { expires: this.row.expiresString });
36
+ }
37
+
38
+ return null;
39
+ }
40
+ }
41
+ };
42
+ </script>
43
+
44
+ <template>
45
+ <div
46
+ v-if="outputString"
47
+ class="cloud-cred-expired"
48
+ :class="{ 'text-error': row.expireData.expired, 'text-warning': row.expireData.expiring}"
49
+ >
50
+ <div class="token-icon mr-5">
51
+ <i
52
+ class="icon"
53
+ :class="{'icon-error': row.expireData.expired, 'icon-warning': row.expireData.expiring}"
54
+ />
55
+ </div>
56
+ {{ outputString }}
57
+ </div>
58
+ </template>
59
+
60
+ <style lang="scss" scoped>
61
+ .cloud-cred-expired {
62
+ display: flex;
63
+ align-items: center;
64
+ .token-icon {
65
+ display: flex;
66
+ align-items: center;
67
+ }
68
+ }
69
+ </style>
@@ -11,7 +11,7 @@ export default {
11
11
  },
12
12
 
13
13
  value: {
14
- type: [String, Date],
14
+ type: [String, Date, Number],
15
15
  default: ''
16
16
  },
17
17
 
@@ -188,12 +188,16 @@ export default {
188
188
  }
189
189
  },
190
190
  // since the Header is a "persistent component" we need to update it at every route change...
191
- $route(nue) {
192
- if (nue) {
193
- this.extensionHeaderActions = getApplicableExtensionEnhancements(this, ExtensionPoint.ACTION, ActionLocation.HEADER, nue);
191
+ $route: {
192
+ handler(nue) {
193
+ if (nue) {
194
+ this.extensionHeaderActions = getApplicableExtensionEnhancements(this, ExtensionPoint.ACTION, ActionLocation.HEADER, nue);
194
195
 
195
- this.navHeaderRight = this.$plugin?.getDynamic('component', 'NavHeaderRight');
196
- }
196
+ this.navHeaderRight = this.$plugin?.getDynamic('component', 'NavHeaderRight');
197
+ }
198
+ },
199
+ immediate: true,
200
+ deep: true,
197
201
  }
198
202
  },
199
203
 
@@ -76,58 +76,102 @@ export default {
76
76
  return this.clusters.length > this.maxClustersToShow;
77
77
  },
78
78
 
79
+ /**
80
+ * Filter mgmt clusters by
81
+ * 1. Harvester type 1 (filterOnlyKubernetesClusters)
82
+ * 2. Harvester type 2 (filterHiddenLocalCluster)
83
+ * 3. There's a matching prov cluster
84
+ *
85
+ * Convert remaining clusters to special format
86
+ */
79
87
  clusters() {
80
- const all = this.$store.getters['management/all'](MANAGEMENT.CLUSTER);
81
- let kubeClusters = filterHiddenLocalCluster(filterOnlyKubernetesClusters(all, this.$store), this.$store);
82
- let pClusters = null;
88
+ if (!this.hasProvCluster) {
89
+ // We're filtering out mgmt clusters without prov clusters, so if the user can't see any prov clusters at all
90
+ // exit early
91
+ return [];
92
+ }
83
93
 
84
- if (this.hasProvCluster) {
85
- pClusters = this.$store.getters['management/all'](CAPI.RANCHER_CLUSTER);
86
- const available = pClusters.reduce((p, c) => {
87
- p[c.mgmt] = true;
94
+ const all = this.$store.getters['management/all'](MANAGEMENT.CLUSTER);
95
+ const mgmtClusters = filterHiddenLocalCluster(filterOnlyKubernetesClusters(all, this.$store), this.$store);
96
+ const provClusters = this.$store.getters['management/all'](CAPI.RANCHER_CLUSTER);
97
+ const provClustersByMgmtId = provClusters.reduce((res, provCluster) => {
98
+ if (provCluster.mgmt?.id) {
99
+ res[provCluster.mgmt.id] = provCluster;
100
+ }
88
101
 
89
- return p;
90
- }, {});
102
+ return res;
103
+ }, {});
91
104
 
105
+ return (mgmtClusters || []).reduce((res, mgmtCluster) => {
92
106
  // Filter to only show mgmt clusters that exist for the available provisioning clusters
93
107
  // Addresses issue where a mgmt cluster can take some time to get cleaned up after the corresponding
94
108
  // provisioning cluster has been deleted
95
- kubeClusters = kubeClusters.filter((c) => !!available[c]);
96
- }
109
+ if (!provClustersByMgmtId[mgmtCluster.id]) {
110
+ return res;
111
+ }
97
112
 
98
- return kubeClusters?.map((x) => {
99
- const pCluster = pClusters?.find((c) => c.mgmt?.id === x.id);
113
+ const pCluster = provClustersByMgmtId[mgmtCluster.id];
114
+
115
+ res.push({
116
+ id: mgmtCluster.id,
117
+ label: mgmtCluster.nameDisplay,
118
+ ready: mgmtCluster.isReady && !pCluster?.hasError,
119
+ osLogo: mgmtCluster.providerOsLogo,
120
+ providerNavLogo: mgmtCluster.providerMenuLogo,
121
+ badge: mgmtCluster.badge,
122
+ isLocal: mgmtCluster.isLocal,
123
+ isHarvester: mgmtCluster.isHarvester,
124
+ pinned: mgmtCluster.pinned,
125
+ description: pCluster?.description || mgmtCluster.description,
126
+ pin: () => mgmtCluster.pin(),
127
+ unpin: () => mgmtCluster.unpin(),
128
+ clusterRoute: { name: 'c-cluster-explorer', params: { cluster: mgmtCluster.id } }
129
+ });
100
130
 
101
- return {
102
- id: x.id,
103
- label: x.nameDisplay,
104
- ready: x.isReady && !pCluster?.hasError,
105
- osLogo: x.providerOsLogo,
106
- providerNavLogo: x.providerMenuLogo,
107
- badge: x.badge,
108
- isLocal: x.isLocal,
109
- isHarvester: x.isHarvester,
110
- pinned: x.pinned,
111
- description: pCluster?.description || x.description,
112
- pin: () => x.pin(),
113
- unpin: () => x.unpin(),
114
- clusterRoute: { name: 'c-cluster-explorer', params: { cluster: x.id } }
115
- };
116
- }) || [];
131
+ return res;
132
+ }, []);
117
133
  },
118
134
 
135
+ /**
136
+ * Filter clusters by
137
+ * 1. Not pinned
138
+ * 2. Includes search term
139
+ *
140
+ * Sort remaining clusters
141
+ *
142
+ * Reduce number of clusters if too many too show
143
+ *
144
+ * Important! This is used to show unpinned clusters OR results of search
145
+ */
119
146
  clustersFiltered() {
120
147
  const search = (this.clusterFilter || '').toLowerCase();
121
- const out = search ? this.clusters.filter((item) => item.label?.toLowerCase().includes(search)) : this.clusters;
122
- const sorted = sortBy(out, ['ready:desc', 'label']);
148
+ let localCluster = null;
123
149
 
124
- // put local cluster on top of list always
125
- // https://github.com/rancher/dashboard/issues/10975
126
- if (sorted.findIndex((c) => c.id === 'local') > 0) {
127
- const localCluster = sorted.find((c) => c.id === 'local');
128
- const localIndex = sorted.findIndex((c) => c.id === 'local');
150
+ const filtered = this.clusters.filter((c) => {
151
+ // If we're searching we don't care if pinned or not
152
+ if (search) {
153
+ if (!c.label?.toLowerCase().includes(search)) {
154
+ return false;
155
+ }
156
+ } else if (c.pinned) {
157
+ // Not searching, not pinned, don't care
158
+ return false;
159
+ }
160
+
161
+ if (!localCluster && c.id === 'local') {
162
+ // Local cluster is a special case, we're inserting it at top so don't include in the middle
163
+ localCluster = c;
164
+
165
+ return false;
166
+ }
129
167
 
130
- sorted.splice(localIndex, 1);
168
+ return true;
169
+ });
170
+
171
+ const sorted = sortBy(filtered, ['ready:desc', 'label']);
172
+
173
+ // put local cluster on top of list always - https://github.com/rancher/dashboard/issues/10975
174
+ if (localCluster) {
131
175
  sorted.unshift(localCluster);
132
176
  }
133
177
 
@@ -141,25 +185,45 @@ export default {
141
185
  this.searchActive = false;
142
186
 
143
187
  if (sorted.length >= this.maxClustersToShow) {
144
- const sortedPinOut = sorted.filter((item) => !item.pinned).slice(0, this.maxClustersToShow);
145
-
146
- return sortedPinOut;
147
- } else {
148
- return sorted.filter((item) => !item.pinned);
188
+ return sorted.slice(0, this.maxClustersToShow);
149
189
  }
190
+
191
+ return sorted;
150
192
  },
151
193
 
194
+ /**
195
+ * Filter clusters by
196
+ * 1. Not pinned
197
+ * 2. Includes search term
198
+ *
199
+ * Sort remaining clusters
200
+ *
201
+ * Reduce number of clusters if too many too show
202
+ *
203
+ * Important! This is hidden if there's a filter (user searching)
204
+ */
152
205
  pinFiltered() {
153
- const out = this.clusters.filter((item) => item.pinned);
154
- const sorted = sortBy(out, ['ready:desc', 'label']);
206
+ let localCluster = null;
207
+ const filtered = this.clusters.filter((c) => {
208
+ if (!c.pinned) {
209
+ // We only care about pinned clusters
210
+ return false;
211
+ }
212
+
213
+ if (c.id === 'local') {
214
+ // Special case, we're going to add this at the start so filter out
215
+ localCluster = c;
216
+
217
+ return false;
218
+ }
219
+
220
+ return true;
221
+ });
155
222
 
156
- // put local cluster on top of list always
157
- // https://github.com/rancher/dashboard/issues/10975
158
- if (sorted.findIndex((c) => c.id === 'local') > 0) {
159
- const localCluster = sorted.find((c) => c.id === 'local');
160
- const localIndex = sorted.findIndex((c) => c.id === 'local');
223
+ const sorted = sortBy(filtered, ['ready:desc', 'label']);
161
224
 
162
- sorted.splice(localIndex, 1);
225
+ // put local cluster on top of list always - https://github.com/rancher/dashboard/issues/10975
226
+ if (localCluster) {
163
227
  sorted.unshift(localCluster);
164
228
  }
165
229
 
@@ -167,7 +231,7 @@ export default {
167
231
  },
168
232
 
169
233
  pinnedClustersHeight() {
170
- const pinCount = this.clusters.filter((item) => item.pinned).length;
234
+ const pinCount = this.pinFiltered.length;
171
235
  const height = pinCount > 2 ? (pinCount * 43) : 90;
172
236
 
173
237
  return `min-height: ${ height }px`;