@rancher/shell 3.0.2-rc.4 → 3.0.2-rc.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/assets/images/providers/nutanix.svg +12 -1
  2. package/assets/styles/base/_basic.scss +2 -1
  3. package/assets/styles/base/_helpers.scss +4 -0
  4. package/assets/styles/base/_variables.scss +2 -0
  5. package/assets/styles/global/_labeled-input.scss +5 -13
  6. package/assets/styles/global/_layout.scss +1 -0
  7. package/assets/styles/global/_select.scss +5 -0
  8. package/assets/styles/themes/_dark.scss +1 -3
  9. package/assets/styles/themes/_light.scss +5 -1
  10. package/assets/translations/en-us.yaml +142 -22
  11. package/assets/translations/zh-hans.yaml +0 -3
  12. package/cloud-credential/azure.vue +1 -1
  13. package/components/ActionMenuShell.vue +105 -0
  14. package/components/AppModal.vue +2 -2
  15. package/components/AsyncButton.vue +2 -0
  16. package/components/ButtonGroup.vue +15 -1
  17. package/components/ButtonMultiAction.vue +5 -1
  18. package/components/ClusterBadge.vue +1 -0
  19. package/components/ClusterIconMenu.vue +3 -0
  20. package/components/ClusterProviderIcon.vue +14 -1
  21. package/components/CodeMirror.vue +96 -5
  22. package/components/Collapse.vue +16 -3
  23. package/components/CopyToClipboardText.vue +3 -1
  24. package/components/CruResource.vue +9 -0
  25. package/components/CruResourceFooter.vue +1 -1
  26. package/components/ExplorerMembers.vue +2 -1
  27. package/components/ExplorerProjectsNamespaces.vue +7 -0
  28. package/components/Import.vue +14 -1
  29. package/components/LandingPagePreference.vue +4 -2
  30. package/components/PodSecurityAdmission.vue +8 -6
  31. package/components/PromptChangePassword.vue +1 -0
  32. package/components/PromptRemove.vue +23 -21
  33. package/components/ResourceDetail/Masthead.vue +30 -11
  34. package/components/ResourceDetail/__tests__/Masthead.test.ts +61 -0
  35. package/components/ResourceDetail/index.vue +6 -0
  36. package/components/ResourceTable.vue +6 -14
  37. package/components/ResourceYaml.vue +1 -0
  38. package/components/SelectIconGrid.vue +2 -0
  39. package/components/Setting.vue +115 -0
  40. package/components/SortableTable/THead.vue +2 -0
  41. package/components/SortableTable/index.vue +38 -14
  42. package/components/Tabbed/index.vue +16 -15
  43. package/components/Wizard.vue +108 -104
  44. package/components/YamlEditor.vue +12 -2
  45. package/components/__tests__/Collapse.test.ts +2 -2
  46. package/components/auth/Principal.vue +29 -17
  47. package/components/auth/__tests__/Principal.test.ts +40 -0
  48. package/components/auth/login/ldap.vue +7 -0
  49. package/components/fleet/FleetBundles.vue +1 -1
  50. package/components/fleet/FleetRepos.vue +1 -1
  51. package/components/fleet/FleetResources.vue +0 -2
  52. package/components/fleet/FleetSummary.vue +60 -65
  53. package/components/fleet/ForceDirectedTreeChart/index.vue +5 -1
  54. package/components/fleet/__tests__/FleetSummary.test.ts +49 -9
  55. package/components/form/ArrayList.vue +6 -2
  56. package/components/form/ColorInput.vue +1 -0
  57. package/components/form/KeyValue.vue +11 -12
  58. package/components/form/LabeledSelect.vue +16 -3
  59. package/components/form/Labels.vue +8 -1
  60. package/components/form/Members/MembershipEditor.vue +230 -222
  61. package/components/form/Members/__tests__/MembershipEditor.test.ts +62 -0
  62. package/components/form/Password.vue +3 -0
  63. package/components/form/ProjectMemberEditor.vue +6 -3
  64. package/components/form/ResourceTabs/index.vue +15 -13
  65. package/components/form/SSHKnownHosts/KnownHostsEditDialog.vue +5 -4
  66. package/components/form/SchedulingCustomization.vue +85 -0
  67. package/components/form/Select.vue +4 -2
  68. package/components/form/SelectOrCreateAuthSecret.vue +2 -1
  69. package/components/form/UnitInput.vue +1 -2
  70. package/components/form/__tests__/ArrayList.test.ts +9 -6
  71. package/components/form/__tests__/LabeledSelect.test.ts +37 -0
  72. package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +34 -0
  73. package/components/formatter/LiveDate.vue +3 -1
  74. package/components/formatter/ServiceType.vue +12 -4
  75. package/components/formatter/WorkloadHealthScale.vue +2 -1
  76. package/components/nav/Header.vue +35 -2
  77. package/components/nav/HeaderPageActionMenu.vue +11 -40
  78. package/components/nav/Jump.vue +8 -2
  79. package/components/nav/NamespaceFilter.vue +5 -4
  80. package/components/nav/Pinned.vue +1 -1
  81. package/components/nav/TopLevelMenu.helper.ts +5 -5
  82. package/components/nav/WindowManager/ContainerLogs.vue +97 -49
  83. package/components/nav/WindowManager/ContainerShell.vue +99 -18
  84. package/components/nav/WindowManager/index.vue +85 -6
  85. package/components/templates/default.vue +2 -47
  86. package/config/features.js +1 -0
  87. package/config/home-links.js +1 -1
  88. package/config/labels-annotations.js +11 -1
  89. package/config/router/navigation-guards/index.js +2 -1
  90. package/config/router/navigation-guards/record-last-route.js +24 -0
  91. package/config/settings.ts +66 -98
  92. package/config/version.js +1 -1
  93. package/core/types-provisioning.ts +7 -0
  94. package/detail/fleet.cattle.io.bundle.vue +7 -0
  95. package/detail/fleet.cattle.io.cluster.vue +0 -3
  96. package/detail/fleet.cattle.io.gitrepo.vue +8 -15
  97. package/detail/provisioning.cattle.io.cluster.vue +8 -2
  98. package/dialog/DeactivateDriverDialog.vue +5 -5
  99. package/dialog/GitRepoForceUpdateDialog.vue +132 -0
  100. package/directives/strip-html-aria-label.js +19 -0
  101. package/edit/__tests__/cis.cattle.io.clusterscan.test.ts +87 -0
  102. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +217 -37
  103. package/edit/auth/__tests__/oidc.test.ts +26 -9
  104. package/edit/auth/ldap/__tests__/config.test.ts +40 -0
  105. package/edit/auth/ldap/config.vue +67 -89
  106. package/edit/auth/oidc.vue +15 -1
  107. package/edit/catalog.cattle.io.clusterrepo.vue +12 -8
  108. package/edit/cis.cattle.io.clusterscan.vue +13 -1
  109. package/edit/fleet.cattle.io.gitrepo.vue +198 -72
  110. package/edit/logging-flow/Match.vue +0 -21
  111. package/edit/management.cattle.io.project.vue +1 -1
  112. package/edit/monitoring.coreos.com.prometheusrule/AlertingRule.vue +10 -3
  113. package/edit/monitoring.coreos.com.prometheusrule/RecordingRule.vue +5 -1
  114. package/edit/monitoring.coreos.com.prometheusrule/index.vue +5 -2
  115. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +8 -1
  116. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +2 -0
  117. package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.test.ts +55 -15
  118. package/edit/provisioning.cattle.io.cluster/index.vue +39 -39
  119. package/edit/provisioning.cattle.io.cluster/rke2.vue +63 -12
  120. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +37 -2
  121. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +3 -2
  122. package/edit/resources.cattle.io.backup.vue +150 -15
  123. package/edit/secret/__tests__/ssh.test.ts +79 -0
  124. package/edit/secret/ssh.vue +7 -1
  125. package/edit/workload/Job.vue +2 -2
  126. package/edit/workload/index.vue +3 -1
  127. package/initialize/install-directives.js +2 -0
  128. package/initialize/install-plugins.js +6 -1
  129. package/list/catalog.cattle.io.app.vue +21 -4
  130. package/list/fleet.cattle.io.bundle.vue +1 -1
  131. package/list/management.cattle.io.setting.vue +34 -129
  132. package/list/provisioning.cattle.io.cluster.vue +11 -3
  133. package/machine-config/vmwarevsphere.vue +15 -8
  134. package/mixins/__tests__/auth-config.test.ts +74 -0
  135. package/mixins/__tests__/chart.test.ts +5 -4
  136. package/mixins/__tests__/create-edit-view.test.ts +38 -0
  137. package/mixins/auth-config.js +9 -1
  138. package/mixins/chart.js +2 -2
  139. package/mixins/create-edit-view/impl.js +4 -1
  140. package/mixins/vue-select-overrides.js +10 -0
  141. package/models/__tests__/catalog.cattle.io.app.test.ts +148 -0
  142. package/models/__tests__/fleet.cattle.io.gitrepo.test.ts +157 -0
  143. package/models/__tests__/secret.test.ts +56 -13
  144. package/models/catalog.cattle.io.app.js +112 -37
  145. package/models/cluster.js +11 -0
  146. package/models/fleet.cattle.io.bundle.js +40 -2
  147. package/models/fleet.cattle.io.gitrepo.js +169 -109
  148. package/models/management.cattle.io.fleetworkspace.js +4 -0
  149. package/models/management.cattle.io.kontainerdriver.js +7 -0
  150. package/models/nodedriver.js +4 -1
  151. package/models/provisioning.cattle.io.cluster.js +24 -0
  152. package/models/secret.js +1 -1
  153. package/package.json +4 -4
  154. package/pages/auth/login.vue +4 -2
  155. package/pages/auth/verify.vue +11 -1
  156. package/pages/c/_cluster/apps/charts/chart.vue +1 -0
  157. package/pages/c/_cluster/apps/charts/index.vue +6 -4
  158. package/pages/c/_cluster/apps/charts/install.vue +1 -1
  159. package/pages/c/_cluster/explorer/ConfigBadge.vue +3 -5
  160. package/pages/c/_cluster/explorer/EventsTable.vue +3 -2
  161. package/pages/c/_cluster/explorer/__tests__/index.test.ts +9 -9
  162. package/pages/c/_cluster/explorer/index.vue +33 -35
  163. package/pages/c/_cluster/explorer/tools/index.vue +17 -4
  164. package/pages/c/_cluster/fleet/index.vue +0 -5
  165. package/pages/c/_cluster/legacy/project/index.vue +1 -1
  166. package/pages/c/_cluster/settings/performance.vue +52 -53
  167. package/pages/c/_cluster/uiplugins/index.vue +21 -22
  168. package/pages/home.vue +17 -12
  169. package/pages/prefs.vue +5 -1
  170. package/plugins/shortkey.js +10 -1
  171. package/plugins/steve/steve-pagination-utils.ts +58 -8
  172. package/promptRemove/management.cattle.io.fleetworkspace.vue +98 -0
  173. package/promptRemove/management.cattle.io.globalrole.vue +1 -1
  174. package/promptRemove/management.cattle.io.project.vue +2 -8
  175. package/promptRemove/management.cattle.io.roletemplate.vue +1 -1
  176. package/promptRemove/mixin/roleDeletionCheck.js +1 -7
  177. package/promptRemove/pod.vue +7 -28
  178. package/rancher-components/Card/Card.vue +9 -1
  179. package/rancher-components/Form/Checkbox/Checkbox.vue +42 -6
  180. package/rancher-components/Form/LabeledInput/LabeledInput.vue +30 -3
  181. package/rancher-components/Form/Radio/RadioButton.vue +18 -3
  182. package/rancher-components/Form/Radio/RadioGroup.vue +39 -5
  183. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +13 -1
  184. package/rancher-components/RcButton/RcButton.test.ts +97 -0
  185. package/rancher-components/RcButton/RcButton.vue +14 -9
  186. package/rancher-components/RcDropdown/RcDropdown.vue +3 -1
  187. package/rancher-components/RcDropdown/RcDropdownItem.vue +8 -14
  188. package/rancher-components/RcDropdown/RcDropdownMenu.vue +66 -0
  189. package/rancher-components/RcDropdown/index.ts +1 -0
  190. package/rancher-components/RcDropdown/types.ts +27 -0
  191. package/rancher-components/RcDropdown/useDropdownContext.ts +5 -2
  192. package/scripts/typegen.sh +1 -0
  193. package/store/__tests__/auth.test.ts +120 -0
  194. package/store/action-menu.js +13 -3
  195. package/store/auth.js +14 -9
  196. package/store/catalog.js +14 -7
  197. package/store/features.js +1 -0
  198. package/store/prefs.js +9 -28
  199. package/store/type-map.utils.ts +4 -0
  200. package/types/resources/settings.d.ts +27 -20
  201. package/types/shell/index.d.ts +18 -2
  202. package/utils/__tests__/array.test.ts +13 -1
  203. package/utils/__tests__/string.test.ts +80 -1
  204. package/utils/array.ts +13 -0
  205. package/utils/auth.js +4 -0
  206. package/utils/cluster.js +1 -1
  207. package/{edit/monitoring.coreos.com.prometheusrule → utils}/duration.js +5 -3
  208. package/utils/pagination-utils.ts +15 -2
  209. package/utils/string.js +31 -7
  210. package/utils/validators/formRules/__tests__/index.test.ts +27 -0
  211. package/utils/validators/formRules/index.ts +16 -0
  212. package/edit/provisioning.cattle.io.cluster/import.vue +0 -198
package/store/auth.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { GITHUB_NONCE, GITHUB_REDIRECT, GITHUB_SCOPE } from '@shell/config/query-params';
2
2
  import { NORMAN } from '@shell/config/types';
3
3
  import { _MULTI } from '@shell/plugins/dashboard-store/actions';
4
- import { addObjects, findBy } from '@shell/utils/array';
4
+ import { addObjects, findBy, joinStringList } from '@shell/utils/array';
5
5
  import { openAuthPopup, returnTo } from '@shell/utils/auth';
6
6
  import { base64Encode } from '@shell/utils/crypto';
7
7
  import { removeEmberPage } from '@shell/utils/ember-page';
@@ -18,12 +18,12 @@ export const BASE_SCOPES = {
18
18
 
19
19
  const KEY = 'rc_nonce';
20
20
 
21
- const ERR_NONCE = 'nonce';
22
-
23
21
  export const LOGIN_ERRORS = {
24
22
  CLIENT: 'client',
25
23
  CLIENT_UNAUTHORIZED: 'client_unauthorized',
26
- SERVER: 'server'
24
+ SERVER: 'server',
25
+ NONCE: 'nonce',
26
+ USER_UNAUTHORIZED: 'user_unauthorized',
27
27
  };
28
28
 
29
29
  export const state = function() {
@@ -230,18 +230,22 @@ export const actions = {
230
230
  const encodedNonce = await dispatch('encodeNonce', baseNonce);
231
231
 
232
232
  const fromQuery = unescape(parseUrl(redirectUrl).query?.[GITHUB_SCOPE] || '');
233
- const scopes = fromQuery.split(/[, ]+/).filter((x) => !!x);
233
+ let scopes = fromQuery.split(/[, ]+/).filter((x) => !!x);
234
234
 
235
235
  if (BASE_SCOPES[provider]) {
236
236
  addObjects(scopes, BASE_SCOPES[provider]);
237
237
  }
238
238
 
239
- if ( opt.scopes ) {
240
- addObjects(scopes, opt.scopes);
239
+ // Need to merge these 2 formats preventing duplicates between code and UI, e.g.
240
+ // [ 'openid profile email' ] from BASE_SCOPES
241
+ // 'openid profile email customScope' from the UI
242
+ if (opt.scopes) {
243
+ scopes = [joinStringList(scopes[0], opt.scopes)];
241
244
  }
242
245
 
243
246
  let url = removeParam(redirectUrl, GITHUB_SCOPE);
244
247
 
248
+ // TODO: #13457 - Verify use case of scopesJoinChar anywhere outside this repository
245
249
  const params = {
246
250
  [GITHUB_SCOPE]: scopes.join(opt.scopesJoinChar || ','), // Some providers won't accept comma separated scopes
247
251
  [GITHUB_NONCE]: encodedNonce
@@ -267,13 +271,13 @@ export const actions = {
267
271
  try {
268
272
  parsed = JSON.parse(expectJSON);
269
273
  } catch {
270
- return ERR_NONCE;
274
+ return LOGIN_ERRORS.NONCE;
271
275
  }
272
276
 
273
277
  const expect = parsed.nonce;
274
278
 
275
279
  if ( !expect || expect !== nonce ) {
276
- return ERR_NONCE;
280
+ return LOGIN_ERRORS.NONCE;
277
281
  }
278
282
 
279
283
  const body = { code };
@@ -310,6 +314,7 @@ export const actions = {
310
314
  const url = await dispatch('redirectTo', {
311
315
  provider,
312
316
  redirectUrl,
317
+ scopes: body.scope,
313
318
  test: true,
314
319
  redirect: false
315
320
  });
package/store/catalog.js CHANGED
@@ -31,6 +31,13 @@ const CERTIFIED_SORTS = {
31
31
  other: 3,
32
32
  };
33
33
 
34
+ export const APP_UPGRADE_STATUS = {
35
+ NOT_APPLICABLE: 'not_applicable', // managed by fleet
36
+ NO_UPGRADE: 'no_upgrade', // no upgrade found
37
+ SINGLE_UPGRADE: 'single_upgrade', // a version available to upgrade to
38
+ MULTIPLE_UPGRADES: 'multiple_upgrades' // more than one match found
39
+ };
40
+
34
41
  export const WINDOWS = 'windows';
35
42
  export const LINUX = 'linux';
36
43
 
@@ -101,7 +108,7 @@ export const getters = {
101
108
 
102
109
  chart(state, getters) {
103
110
  return ({
104
- key, repoType, repoName, chartName, preferRepoType, preferRepoName, includeHidden, showDeprecated
111
+ key, repoType, repoName, chartName, includeHidden, showDeprecated, multiple
105
112
  }) => {
106
113
  if ( key && !repoType && !repoName && !chartName) {
107
114
  const parsed = parseKey(key);
@@ -111,7 +118,7 @@ export const getters = {
111
118
  chartName = parsed.chartName;
112
119
  }
113
120
 
114
- let matching = filterBy(getters.charts, {
121
+ let matchingCharts = filterBy(getters.charts, {
115
122
  repoType,
116
123
  repoName,
117
124
  chartName,
@@ -119,18 +126,18 @@ export const getters = {
119
126
  });
120
127
 
121
128
  if ( includeHidden === false ) {
122
- matching = matching.filter((x) => !x.hidden);
129
+ matchingCharts = matchingCharts.filter((x) => !x.hidden);
123
130
  }
124
131
 
125
- if ( !matching.length ) {
132
+ if ( !matchingCharts.length ) {
126
133
  return;
127
134
  }
128
135
 
129
- if ( preferRepoType && preferRepoName ) {
130
- preferSameRepo(matching, preferRepoType, preferRepoName);
136
+ if (multiple) {
137
+ return matchingCharts;
131
138
  }
132
139
 
133
- return matching[0];
140
+ return matchingCharts[0];
134
141
  };
135
142
  },
136
143
 
package/store/features.js CHANGED
@@ -36,6 +36,7 @@ export const FLEET_WORKSPACE_BACK = create('provisioningv2-fleet-workspace-back-
36
36
  export const STEVE_CACHE = create('ui-sql-cache', false);
37
37
  export const UIEXTENSION = create('uiextension', true);
38
38
  export const PROVISIONING_PRE_BOOTSTRAP = create('provisioningprebootstrap', false);
39
+ export const SCHEDULING_CUSTOMIZATION = create('cluster-agent-scheduling-customization', false);
39
40
 
40
41
  // Not currently used.. no point defining ones we don't use
41
42
  // export const EMBEDDED_CLUSTER_API = create('embedded-cluster-api', true);
package/store/prefs.js CHANGED
@@ -130,6 +130,7 @@ export const state = function() {
130
130
  cookiesLoaded: false,
131
131
  data: {},
132
132
  definitions,
133
+ authRedirect: null
133
134
  };
134
135
  };
135
136
 
@@ -215,6 +216,9 @@ export const getters = {
215
216
  case (afterLoginRoutePref === 'home'):
216
217
  return { name: 'home' };
217
218
  case (afterLoginRoutePref === 'last-visited'): {
219
+ if (state.authRedirect) {
220
+ return state.authRedirect;
221
+ }
218
222
  const lastVisitedPref = getters['get'](LAST_VISITED);
219
223
 
220
224
  if (lastVisitedPref) {
@@ -265,6 +269,10 @@ export const mutations = {
265
269
  setDefinition(state, { name, definition = {} }) {
266
270
  state.definitions[name] = definition;
267
271
  },
272
+
273
+ setAuthRedirect(state, route) {
274
+ state.authRedirect = route;
275
+ }
268
276
  };
269
277
 
270
278
  export const actions = {
@@ -491,9 +499,7 @@ export const actions = {
491
499
  return;
492
500
  }
493
501
 
494
- const toSave = getLoginRoute(route);
495
-
496
- return dispatch('set', { key: LAST_VISITED, value: toSave });
502
+ return dispatch('set', { key: LAST_VISITED, value: route });
497
503
  },
498
504
 
499
505
  toggleTheme({ getters, dispatch }) {
@@ -523,28 +529,3 @@ export const actions = {
523
529
  }
524
530
  }
525
531
  };
526
-
527
- function getLoginRoute(route) {
528
- let parts = route.name?.split('-') || [];
529
- const params = {};
530
- const routeParams = route.params || {};
531
-
532
- // Find the 'resource' part of the route, if it is there
533
- const index = parts.findIndex((p) => p === 'resource');
534
-
535
- if (index >= 0) {
536
- parts = parts.slice(0, index);
537
- }
538
-
539
- // Just keep the params that are needed
540
- parts.forEach((param) => {
541
- if (routeParams[param]) {
542
- params[param] = routeParams[param];
543
- }
544
- });
545
-
546
- return {
547
- name: parts.join('-'),
548
- params
549
- };
550
- }
@@ -123,6 +123,10 @@ export function createHeaders(
123
123
  * Given a schema's attribute.column value create a header
124
124
  */
125
125
  export function headerFromSchemaColString(colName: string, schema: Schema, rootGetters: VuexStoreGetters, pagination: boolean, ageColumn: TableColumn): TableColumn {
126
+ if (!schema) {
127
+ throw new Error(`Unable to create header for column '${ colName }' from schema: schema is missing`);
128
+ }
129
+
126
130
  const col = schema.attributes.columns.find((c) => c.name === colName);
127
131
 
128
132
  if (!col) {
@@ -1,3 +1,25 @@
1
+
2
+ export interface PaginationSettingsStore {
3
+ [name: string]: {
4
+ resources: {
5
+ /**
6
+ * Enable for all resources in this store
7
+ */
8
+ enableAll: boolean,
9
+ enableSome: {
10
+ /**
11
+ * Specific resource type to enable
12
+ */
13
+ enabled: (string | { resource: string, context: string[]})[],
14
+ /**
15
+ * There's no hardcoded headers or custom list for the resource type, headers will be generated from schema attributes.columns
16
+ */
17
+ generic: boolean,
18
+ },
19
+ }
20
+ }
21
+ }
22
+
1
23
  /**
2
24
  * Settings to handle server side pagination
3
25
  */
@@ -6,29 +28,14 @@ export interface PaginationSettings {
6
28
  * Global setting to enable or disable
7
29
  */
8
30
  enabled: boolean,
31
+ /**
32
+ * Override `stores` and apply pagination to a set of default resource types that can change between versions
33
+ */
34
+ useDefaultStores: boolean,
9
35
  /**
10
36
  * Should pagination be enabled for resources in a store
11
37
  */
12
- stores: {
13
- [name: string]: {
14
- resources: {
15
- /**
16
- * Enable for all resources in this store
17
- */
18
- enableAll: boolean,
19
- enableSome: {
20
- /**
21
- * Specific resource type to enable
22
- */
23
- enabled: (string | { resource: string, context: string[]})[],
24
- /**
25
- * There's no hardcoded headers or custom list for the resource type, headers will be generated from schema attributes.columns
26
- */
27
- generic: boolean,
28
- },
29
- }
30
- }
31
- }
38
+ stores: PaginationSettingsStore | undefined
32
39
  }
33
40
 
34
41
  type Links = {
@@ -42,6 +42,7 @@ export const RESOURCE_QUOTA: "field.cattle.io/resourceQuota";
42
42
  export const AZURE_MIGRATED: "auth.cattle.io/azuread-endpoint-migrated";
43
43
  export const WORKSPACE_ANNOTATION: "objectset.rio.cattle.io/id";
44
44
  export const NODE_ARCHITECTURE: "kubernetes.io/arch";
45
+ export const IMPORTED_CLUSTER_VERSION_MANAGEMENT: "rancher.io/imported-cluster-version-management";
45
46
  export namespace KUBERNETES {
46
47
  let SERVICE_ACCOUNT_UID: string;
47
48
  let SERVICE_ACCOUNT_NAME: string;
@@ -81,6 +82,7 @@ export namespace CAPI {
81
82
  let MACHINE_NAME: string;
82
83
  let DELETE_MACHINE: string;
83
84
  let PROVIDER: string;
85
+ let HUMAN_NAME: string;
84
86
  let SECRET_AUTH: string;
85
87
  let SECRET_WILL_DELETE: string;
86
88
  let UI_CUSTOM_PROVIDER: string;
@@ -122,6 +124,7 @@ export namespace CATALOG {
122
124
  let HIDDEN_REPO: string;
123
125
  }
124
126
  export namespace FLEET {
127
+ export let REPO_NAME: string;
125
128
  export let CLUSTER_DISPLAY_NAME: string;
126
129
  export let CLUSTER_NAME: string;
127
130
  export let BUNDLE_ID: string;
@@ -132,6 +135,8 @@ export namespace FLEET {
132
135
  let CLUSTER_NAMESPACE_1: string;
133
136
  export { CLUSTER_NAMESPACE_1 as CLUSTER_NAMESPACE };
134
137
  export let CLUSTER: string;
138
+ export let CREATED_BY_USER_ID: string;
139
+ export let CREATED_BY_USER_NAME: string;
135
140
  }
136
141
  export namespace RBAC {
137
142
  let PRODUCT: string;
@@ -2220,7 +2225,7 @@ export function getVersionData(): {
2220
2225
  export function setVersionData(v: any): void;
2221
2226
  export function getKubeVersionData(): {};
2222
2227
  export function setKubeVersionData(v: any): void;
2223
- export const CURRENT_RANCHER_VERSION: "2.10";
2228
+ export const CURRENT_RANCHER_VERSION: "2.11";
2224
2229
  }
2225
2230
 
2226
2231
  // @shell/mixins/create-edit-view/impl
@@ -3172,6 +3177,7 @@ export const FLEET_WORKSPACE_BACK: any;
3172
3177
  export const STEVE_CACHE: any;
3173
3178
  export const UIEXTENSION: any;
3174
3179
  export const PROVISIONING_PRE_BOOTSTRAP: any;
3180
+ export const SCHEDULING_CUSTOMIZATION: any;
3175
3181
  export namespace getters {
3176
3182
  function get(state: any, getters: any, rootState: any, rootGetters: any): (name: any) => any;
3177
3183
  }
@@ -3260,6 +3266,7 @@ export function state(): {
3260
3266
  cookiesLoaded: boolean;
3261
3267
  data: {};
3262
3268
  definitions: {};
3269
+ authRedirect: any;
3263
3270
  };
3264
3271
  export namespace getters {
3265
3272
  function get(state: any): (key: any) => any;
@@ -3280,6 +3287,7 @@ export namespace mutations {
3280
3287
  name: any;
3281
3288
  definition?: {};
3282
3289
  }): void;
3290
+ function setAuthRedirect(state: any, route: any): void;
3283
3291
  }
3284
3292
  export namespace actions {
3285
3293
  function set({ dispatch, commit, rootGetters, state }: {
@@ -3785,6 +3793,13 @@ export function generateZip(files: any): any;
3785
3793
  export function downloadUrl(url: any, id?: string): void;
3786
3794
  }
3787
3795
 
3796
+ // @shell/utils/duration
3797
+
3798
+ declare module '@shell/utils/duration' {
3799
+ export function toMilliseconds(input: any): number;
3800
+ export function toSeconds(input: any): number;
3801
+ }
3802
+
3788
3803
  // @shell/utils/dynamic-importer
3789
3804
 
3790
3805
  declare module '@shell/utils/dynamic-importer' {
@@ -4356,7 +4371,7 @@ export function random32(count: any): number | number[];
4356
4371
  export function randomStr(length?: number, chars?: string): any;
4357
4372
  export function formatPercent(value: any, maxPrecision?: number): string;
4358
4373
  export function pluralize(str: any): any;
4359
- export function resourceNames(names: any, plusMore: any, t: any): any;
4374
+ export function resourceNames(names: any, t: any, options?: {}): any;
4360
4375
  export function indent(lines: any, count?: number, token?: string, afterRegex?: any): any;
4361
4376
  export function decamelize(str: any): any;
4362
4377
  export function dasherize(str: any): any;
@@ -4379,6 +4394,7 @@ export function sanitizeIP(v: any): any;
4379
4394
  */
4380
4395
  export function xOfy(x: any, y: any): string;
4381
4396
  export function isBase64(value: any): boolean;
4397
+ export function generateRandomAlphaString(length: any): string;
4382
4398
  export namespace CHARSET {
4383
4399
  export { num as NUMERIC };
4384
4400
  export let NO_VOWELS: string;
@@ -1,5 +1,5 @@
1
1
  import {
2
- addObject, addObjects, clear, filterBy, findBy, getUniqueLabelKeys, insertAt, isArray, removeAt, removeObject, removeObjects, replaceWith, sameContents, uniq
2
+ addObject, addObjects, clear, filterBy, findBy, getUniqueLabelKeys, insertAt, isArray, joinStringList, removeAt, removeObject, removeObjects, replaceWith, sameContents, uniq
3
3
  } from '@shell/utils/array';
4
4
 
5
5
  interface Obj {
@@ -500,3 +500,15 @@ describe('fx: getUniqueLabelKeys', () => {
500
500
  expect(result).toStrictEqual(expected);
501
501
  });
502
502
  });
503
+
504
+ describe('fx: joinStringList', () => {
505
+ it('should join two lists of strings', () => {
506
+ const a = 'a b c';
507
+ const b = 'b c d';
508
+ const separator = ' ';
509
+
510
+ const result = joinStringList(a, b, separator);
511
+
512
+ expect(result).toBe('a b c d');
513
+ });
514
+ });
@@ -1,4 +1,4 @@
1
- import { decodeHtml } from '@shell/utils/string';
1
+ import { decodeHtml, resourceNames } from '@shell/utils/string';
2
2
 
3
3
  describe('fx: decodeHtml', () => {
4
4
  it('should decode HTML values from escaped string into valid markup', () => {
@@ -10,3 +10,82 @@ describe('fx: decodeHtml', () => {
10
10
  expect(result).toStrictEqual(expectation);
11
11
  });
12
12
  });
13
+
14
+ describe('fx: resourceNames', () => {
15
+ // default plusMore function
16
+ const t = (key: string, options: { count: number }) => {
17
+ switch (key) {
18
+ case 'generic.and':
19
+ return ' and ';
20
+ case 'generic.comma':
21
+ return ', ';
22
+ case 'promptRemove.andOthers': {
23
+ if (options.count === 0) {
24
+ return '.';
25
+ }
26
+ if (options.count === 1) {
27
+ return 'and <b>one other</b>.';
28
+ }
29
+
30
+ return `and <b>${ options.count } others</b>.`;
31
+ }
32
+ }
33
+ };
34
+
35
+ describe.each([
36
+ ['no options',
37
+ [
38
+ [[], '', {}],
39
+ [['item1'], '<b>item1</b>.', {}],
40
+ [['item1', 'item2'], '<b>item1</b> and <b>item2</b>.', {}],
41
+ [['item1', 'item2', 'item3'], '<b>item1</b>, <b>item2</b> and <b>item3</b>.', {}],
42
+ [['item1', 'item2', 'item3', 'item4', 'item5'], '<b>item1</b>, <b>item2</b>, <b>item3</b>, <b>item4</b> and <b>item5</b>.', {}],
43
+ [['item1', 'item2', 'item3', 'item4', 'item5', 'item6'], '<b>item1</b>, <b>item2</b>, <b>item3</b>, <b>item4</b>, <b>item5</b>and <b>one other</b>.', {}],
44
+ [['item1', 'item2', 'item3', 'item4', 'item5', 'item6', 'item7'], '<b>item1</b>, <b>item2</b>, <b>item3</b>, <b>item4</b>, <b>item5</b>and <b>2 others</b>.', {}],
45
+ ]
46
+ ],
47
+ ['plusMore option',
48
+ [
49
+ [[], '', { plusMore: 'foo' }],
50
+ [['item1'], '<b>item1</b>.', { plusMore: 'foo' }],
51
+ [['item1', 'item2'], '<b>item1</b> and <b>item2</b>.', { plusMore: 'foo' }],
52
+ [['item1', 'item2', 'item3'], '<b>item1</b>, <b>item2</b> and <b>item3</b>.', { plusMore: 'foo' }],
53
+ [['item1', 'item2', 'item3', 'item4', 'item5'], '<b>item1</b>, <b>item2</b>, <b>item3</b>, <b>item4</b> and <b>item5</b>.', { plusMore: 'foo' }],
54
+ [['item1', 'item2', 'item3', 'item4', 'item5', 'item6'], '<b>item1</b>, <b>item2</b>, <b>item3</b>, <b>item4</b>, <b>item5</b>foo', { plusMore: 'foo' }],
55
+ [['item1', 'item2', 'item3', 'item4', 'item5', 'item6', 'item7'], '<b>item1</b>, <b>item2</b>, <b>item3</b>, <b>item4</b>, <b>item5</b>foo', { plusMore: 'foo' }],
56
+ ]
57
+ ],
58
+ ['endString option',
59
+ [
60
+ [[], '', { endString: false }],
61
+ [['item1'], '<b>item1</b> ', { endString: false }],
62
+ [['item1', 'item2'], '<b>item1</b> and <b>item2</b> ', { endString: false }],
63
+ [['item1', 'item2'], '<b>item1</b> and <b>item2</b>.', { endString: '' }],
64
+ [['item1', 'item2'], '<b>item1</b> and <b>item2</b>foo', { endString: 'foo' }],
65
+ ]
66
+ ],
67
+ [
68
+ 'plusMore & endString options', [
69
+ [[], '', { plusMore: '', endString: false }],
70
+ [[], '', { plusMore: 'foo', endString: 'foo' }],
71
+ [['item1'], '<b>item1</b>foo', { plusMore: 'foo', endString: 'foo' }],
72
+ [['item1', 'item2'], '<b>item1</b> and <b>item2</b>foo', { plusMore: 'foo', endString: 'foo' }],
73
+ [['item1', 'item2'], '<b>item1</b> and <b>item2</b> ', { plusMore: 'foo', endString: false }],
74
+ [['item1', 'item2', 'item3', 'item4', 'item5', 'item6'], '<b>item1</b>, <b>item2</b>, <b>item3</b>, <b>item4</b>, <b>item5</b>foo', { plusMore: 'foo', endString: 'foo' }],
75
+ [['item1', 'item2', 'item3', 'item4', 'item5', 'item6', 'item7'], '<b>item1</b>, <b>item2</b>, <b>item3</b>, <b>item4</b>, <b>item5</b>foo', { plusMore: 'foo', endString: 'foo' }],
76
+ [['item1', 'item2', 'item3', 'item4', 'item5', 'item6'], '<b>item1</b>, <b>item2</b>, <b>item3</b>, <b>item4</b>, <b>item5</b>foo', { plusMore: 'foo', endString: false }],
77
+ [['item1', 'item2', 'item3', 'item4', 'item5', 'item6', 'item7'], '<b>item1</b>, <b>item2</b>, <b>item3</b>, <b>item4</b>, <b>item5</b>foo', { plusMore: 'foo', endString: false }],
78
+ ]
79
+ ]
80
+ ])('should build a single message from a list of names, with: %p', (_, args) => {
81
+ it.each(args)(`having: %p`, (
82
+ names,
83
+ expectation,
84
+ options,
85
+ ) => {
86
+ const result = resourceNames(names, t, options);
87
+
88
+ expect(result).toStrictEqual(expectation);
89
+ });
90
+ });
91
+ });
package/utils/array.ts CHANGED
@@ -241,3 +241,16 @@ export function getUniqueLabelKeys<T extends KubeResource>(aryResources: T[]): s
241
241
 
242
242
  return Object.keys(uniqueObj).sort();
243
243
  }
244
+
245
+ /**
246
+ * Join list as string into a new string without duplicates
247
+ * @param {string} a 'a b c'
248
+ * @param {string} b 'b c d'
249
+ * @param {string} [separator=' ']
250
+ * @return {string} 'a b c d'
251
+ */
252
+ export const joinStringList = (a: string, b: string, separator = ' '): string => {
253
+ const all = a.split(separator).concat(b.split(separator));
254
+
255
+ return [...new Set(all)].join(separator);
256
+ };
package/utils/auth.js CHANGED
@@ -315,6 +315,10 @@ export function isLoggedIn(store, me) {
315
315
  export function notLoggedIn(store, redirect, route) {
316
316
  store.commit('auth/hasAuth', true);
317
317
 
318
+ if (!route.name.includes('auth')) {
319
+ store.commit('prefs/setAuthRedirect', route);
320
+ }
321
+
318
322
  if ( route.name === 'index' ) {
319
323
  return redirect('/auth/login');
320
324
  } else {
package/utils/cluster.js CHANGED
@@ -81,7 +81,7 @@ export function paginationFilterOnlyKubernetesClusters(store) {
81
81
 
82
82
  return PaginationParamFilter.createMultipleFields([
83
83
  new PaginationFilterField({
84
- field: `metadata.labels."${ CAPI.PROVIDER }"`,
84
+ field: `metadata.labels[${ CAPI.PROVIDER }]`,
85
85
  equals: false,
86
86
  value: VIRTUAL_HARVESTER_PROVIDER,
87
87
  exact: true
@@ -8,9 +8,7 @@ const UNIT_TO_MS =
8
8
  w: 7 * 24 * 60 * 60 * 1000,
9
9
  y: 365 * 24 * 60 * 60 * 1000
10
10
  };
11
- // see:
12
- // https://prometheus.io/docs/prometheus/latest/configuration/recording_rules/#rule
13
- // https://prometheus.io/docs/prometheus/latest/configuration/configuration/#duration
11
+
14
12
  const DURATION_REGEX = /^(?:([0-9]+)y)?(?:([0-9]+)w)?(?:([0-9]+)d)?(?:([0-9]+)h)?(?:([0-9]+)m)?(?:([0-9]+)s)?(?:([0-9]+)ms)?$/;
15
13
 
16
14
  export function toMilliseconds(input) {
@@ -39,3 +37,7 @@ export function toMilliseconds(input) {
39
37
 
40
38
  return 0;
41
39
  }
40
+
41
+ export function toSeconds(input) {
42
+ return Math.floor(toMilliseconds(input) / 1000);
43
+ }
@@ -1,4 +1,4 @@
1
- import { PaginationSettings } from '@shell/types/resources/settings';
1
+ import { PaginationSettings, PaginationSettingsStore } from '@shell/types/resources/settings';
2
2
  import {
3
3
  NAMESPACE_FILTER_ALL_USER as ALL_USER,
4
4
  NAMESPACE_FILTER_ALL as ALL,
@@ -14,6 +14,7 @@ import { sameArrayObjects } from '@shell/utils/array';
14
14
  import { isEqual } from '@shell/utils/object';
15
15
  import { STEVE_CACHE } from '@shell/store/features';
16
16
  import { getPerformanceSetting } from '@shell/utils/settings';
17
+ import { PAGINATION_SETTINGS_STORE_DEFAULTS } from '@shell/plugins/steve/steve-pagination-utils';
17
18
 
18
19
  /**
19
20
  * Helper functions for server side pagination
@@ -32,6 +33,18 @@ class PaginationUtils {
32
33
  return perf.serverPagination;
33
34
  }
34
35
 
36
+ public getStoreSettings(ctx: any): PaginationSettingsStore
37
+ public getStoreSettings(serverPagination: PaginationSettings): PaginationSettingsStore
38
+ public getStoreSettings(arg: any | PaginationSettings): PaginationSettingsStore {
39
+ const serverPagination: PaginationSettings = arg?.rootGetters !== undefined ? this.getSettings(arg) : arg;
40
+
41
+ return serverPagination?.useDefaultStores ? this.getStoreDefault() : serverPagination?.stores || this.getStoreDefault();
42
+ }
43
+
44
+ public getStoreDefault(): PaginationSettingsStore {
45
+ return PAGINATION_SETTINGS_STORE_DEFAULTS;
46
+ }
47
+
35
48
  isSteveCacheEnabled({ rootGetters }: any): boolean {
36
49
  // We always get Feature flags as part of start up (see `dispatch('features/loadServer')` in loadManagement)
37
50
  return rootGetters['features/get']?.(STEVE_CACHE);
@@ -58,7 +71,7 @@ class PaginationUtils {
58
71
  return false;
59
72
  }
60
73
 
61
- const storeSettings = settings.stores?.[enabledFor.store];
74
+ const storeSettings = this.getStoreSettings(settings)?.[enabledFor.store];
62
75
 
63
76
  // No pagination setting for target store, not enabled
64
77
  if (!storeSettings) {
package/utils/string.js CHANGED
@@ -151,16 +151,36 @@ export function pluralize(str) {
151
151
  }
152
152
  }
153
153
 
154
- export function resourceNames(names, plusMore, t) {
154
+ export function resourceNames(names, t, options = {}) {
155
+ const MAX_NAMES_COUNT = 5;
156
+
157
+ let { plusMore, endString } = options;
158
+
159
+ // plusMore default value
160
+ if (!plusMore) {
161
+ plusMore = t('promptRemove.andOthers', { count: names.length > MAX_NAMES_COUNT ? names.length - MAX_NAMES_COUNT : 0 });
162
+ }
163
+
164
+ // endString default value
165
+ if (!endString) {
166
+ endString = endString === false ? ' ' : '.';
167
+ }
168
+
155
169
  return names.reduce((res, name, i) => {
156
- if (i >= 5) {
157
- return res;
170
+ if (i < MAX_NAMES_COUNT) {
171
+ res += `<b>${ escapeHtml( name ) }</b>`;
172
+
173
+ if (i === names.length - 1) {
174
+ res += endString;
175
+ } else if (i === names.length - 2) {
176
+ res += names.length <= 5 ? t('generic.and') : '';
177
+ } else {
178
+ res += i < MAX_NAMES_COUNT - 1 ? t('generic.comma') : '';
179
+ }
158
180
  }
159
- res += `<b>${ escapeHtml( name ) }</b>`;
160
- if (i === names.length - 1) {
181
+
182
+ if (i === MAX_NAMES_COUNT) {
161
183
  res += plusMore;
162
- } else {
163
- res += i === names.length - 2 ? t('generic.and') : t('generic.comma');
164
184
  }
165
185
 
166
186
  return res;
@@ -336,3 +356,7 @@ export function isBase64(value) {
336
356
 
337
357
  return base64regex.test(value);
338
358
  }
359
+
360
+ export function generateRandomAlphaString(length) {
361
+ return Array.from({ length }, () => String.fromCharCode(97 + Math.random() * 26 | 0)).join('');
362
+ }
@@ -244,6 +244,33 @@ describe('formRules', () => {
244
244
  expect(formRuleResult).toStrictEqual(expectedResult);
245
245
  });
246
246
 
247
+ describe('"registryUrl": has the expected output for each input', () => {
248
+ const expectedTranslation = JSON.stringify({ message: 'cluster.privateRegistry.privateRegistryUrlError' });
249
+ const testCases = [
250
+ // Empty
251
+ [undefined, undefined],
252
+
253
+ // Word
254
+ ['registry', expectedTranslation],
255
+
256
+ // Without schema
257
+ ['registry.io', undefined],
258
+
259
+ // With schemas
260
+ ['http://registry.io', undefined],
261
+ ['https://registry.io', undefined],
262
+ ];
263
+
264
+ it.each(testCases)(
265
+ 'should return undefined or correct message based on the provided url',
266
+ (url, expected) => {
267
+ const formRuleResult = formRules.registryUrl(url);
268
+
269
+ expect(formRuleResult).toStrictEqual(expected);
270
+ }
271
+ );
272
+ });
273
+
247
274
  it('"ruleGroups" : returns undefined when rulegroups are supplied', () => {
248
275
  const testValue = { groups: ['group1'] };
249
276
  const formRuleResult = formRules.ruleGroups(testValue);