@rancher/shell 3.0.11 → 3.0.12-rc.1

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 (98) hide show
  1. package/assets/styles/base/_mixins.scss +31 -0
  2. package/assets/styles/base/_variables.scss +2 -0
  3. package/assets/styles/themes/_modern.scss +6 -5
  4. package/assets/translations/en-us.yaml +5 -4
  5. package/assets/translations/zh-hans.yaml +0 -3
  6. package/components/EmptyProductPage.vue +76 -0
  7. package/components/Resource/Detail/CopyToClipboard.vue +1 -2
  8. package/components/Resource/Detail/Metadata/KeyValueRow.vue +9 -3
  9. package/components/Resource/Detail/TitleBar/__tests__/__snapshots__/index.test.ts.snap +31 -0
  10. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +45 -1
  11. package/components/Resource/Detail/TitleBar/index.vue +1 -1
  12. package/components/Resource/Detail/ViewOptions/__tests__/__snapshots__/index.test.ts.snap +9 -0
  13. package/components/Resource/Detail/ViewOptions/__tests__/index.test.ts +62 -0
  14. package/components/Resource/Detail/ViewOptions/index.vue +2 -1
  15. package/components/ResourceList/Masthead.vue +25 -2
  16. package/components/SideNav.vue +13 -0
  17. package/components/__tests__/PromptModal.test.ts +2 -0
  18. package/components/fleet/FleetClusters.vue +1 -0
  19. package/components/fleet/__tests__/FleetClusters.test.ts +71 -0
  20. package/components/form/NodeScheduling.vue +17 -3
  21. package/components/form/PrivateRegistry.vue +69 -0
  22. package/components/form/__tests__/PrivateRegistry.test.ts +133 -0
  23. package/components/formatter/WorkloadHealthScale.vue +3 -1
  24. package/components/nav/Group.vue +26 -3
  25. package/components/nav/Header.vue +32 -7
  26. package/components/nav/TopLevelMenu.vue +15 -1
  27. package/config/pagination-table-headers.js +8 -1
  28. package/config/product/apps.js +2 -1
  29. package/config/product/auth.js +1 -0
  30. package/config/product/backup.js +1 -0
  31. package/config/product/compliance.js +1 -1
  32. package/config/product/explorer.js +25 -6
  33. package/config/product/fleet.js +1 -0
  34. package/config/product/gatekeeper.js +1 -0
  35. package/config/product/istio.js +1 -0
  36. package/config/product/logging.js +1 -0
  37. package/config/product/longhorn.js +2 -1
  38. package/config/product/manager.js +1 -0
  39. package/config/product/monitoring.js +1 -0
  40. package/config/product/navlinks.js +1 -0
  41. package/config/product/neuvector.js +2 -1
  42. package/config/product/settings.js +1 -0
  43. package/config/product/uiplugins.js +1 -0
  44. package/core/__tests__/plugin-products-helpers.test.ts +454 -0
  45. package/core/__tests__/plugin-products.test.ts +3219 -0
  46. package/core/extension-manager-impl.js +30 -1
  47. package/core/plugin-products-base.ts +375 -0
  48. package/core/plugin-products-extending.ts +44 -0
  49. package/core/plugin-products-helpers.ts +262 -0
  50. package/core/plugin-products-top-level.ts +66 -0
  51. package/core/plugin-products-type-guards.ts +33 -0
  52. package/core/plugin-products.ts +50 -0
  53. package/core/plugin-types.ts +222 -0
  54. package/core/plugin.ts +45 -10
  55. package/core/productDebugger.js +48 -0
  56. package/core/types.ts +95 -11
  57. package/detail/__tests__/__snapshots__/fleet.cattle.io.bundle.test.ts.snap +52 -0
  58. package/detail/__tests__/fleet.cattle.io.bundle.test.ts +171 -0
  59. package/detail/fleet.cattle.io.bundle.vue +21 -34
  60. package/dialog/ExtensionCatalogInstallDialog.vue +1 -1
  61. package/dialog/InstallExtensionDialog.vue +6 -27
  62. package/dialog/UninstallExistingExtensionDialog.vue +141 -0
  63. package/dialog/UninstallExtensionDialog.vue +4 -26
  64. package/dialog/__tests__/UninstallExistingExtensionDialog.test.ts +114 -0
  65. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -0
  66. package/edit/provisioning.cattle.io.cluster/__tests__/Ingress.test.ts +176 -0
  67. package/edit/provisioning.cattle.io.cluster/rke2.vue +4 -1
  68. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +6 -0
  69. package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +7 -2
  70. package/list/provisioning.cattle.io.cluster.vue +0 -1
  71. package/list/workload.vue +11 -4
  72. package/mixins/resource-fetch.js +12 -3
  73. package/models/pod.js +18 -0
  74. package/models/workload.js +20 -2
  75. package/package.json +1 -2
  76. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +0 -1
  77. package/pages/c/_cluster/settings/brand.vue +4 -4
  78. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +231 -13
  79. package/pages/c/_cluster/uiplugins/index.vue +143 -37
  80. package/plugins/dashboard-store/__tests__/resource-class.test.ts +1 -0
  81. package/plugins/dashboard-store/actions.js +3 -2
  82. package/plugins/dashboard-store/resource-class.js +62 -6
  83. package/plugins/plugin.js +16 -0
  84. package/plugins/steve/steve-pagination-utils.ts +7 -0
  85. package/scripts/typegen.sh +13 -1
  86. package/store/__tests__/type-map.test.ts +84 -24
  87. package/store/type-map.js +42 -3
  88. package/tsconfig.paths.json +1 -0
  89. package/types/resources/pod.ts +18 -0
  90. package/types/shell/index.d.ts +8506 -2909
  91. package/types/store/dashboard-store.types.ts +5 -0
  92. package/types/store/pagination.types.ts +6 -0
  93. package/utils/axios.js +1 -4
  94. package/utils/dynamic-importer.js +3 -2
  95. package/utils/pagination-utils.ts +1 -1
  96. package/utils/uiplugins.ts +12 -16
  97. package/utils/validators/__tests__/private-registry.test.ts +76 -0
  98. package/utils/validators/private-registry.ts +28 -0
@@ -83,6 +83,11 @@ export interface ActionFindPageArgs extends ActionCoreFindArgs {
83
83
 
84
84
  saveCountAs?: string,
85
85
 
86
+ /**
87
+ * When making a supporting HTTP request include associated resource data
88
+ */
89
+ includeAssociatedData?: boolean,
90
+
86
91
  /**
87
92
  * The target minimum revision for the resource.
88
93
  *
@@ -607,6 +607,7 @@ export interface StorePaginationRequest {
607
607
  * The single namespace to filter results by (as part of url path, not pagination params)
608
608
  */
609
609
  namespace?: string,
610
+
610
611
  /**
611
612
  * The set of pagination args used to create the request
612
613
  */
@@ -616,6 +617,11 @@ export interface StorePaginationRequest {
616
617
  * Does this request stem from a list with manual refresh?
617
618
  */
618
619
  hasManualRefresh?: boolean,
620
+
621
+ /**
622
+ * When making a supporting HTTP request include associated resource data
623
+ */
624
+ includeAssociatedData?: boolean,
619
625
  }
620
626
 
621
627
  /**
package/utils/axios.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import Axios from 'axios';
2
- import defu from 'defu';
2
+
3
3
  import axiosRetry from 'axios-retry';
4
4
 
5
5
  // Axios.prototype cannot be modified
@@ -37,9 +37,6 @@ const axiosExtra = {
37
37
  onError(fn) {
38
38
  this.onRequestError(fn);
39
39
  this.onResponseError(fn);
40
- },
41
- create(options) {
42
- return createAxiosInstance(defu(options, this.defaults));
43
40
  }
44
41
  };
45
42
 
@@ -94,8 +94,9 @@ export function loadProduct(name) {
94
94
  }
95
95
 
96
96
  export function listProducts() {
97
- const ctx = require.context('@shell/config/product', true, /.*/);
98
- const products = ctx.keys().filter(path => !path.endsWith('.js') && path.startsWith('./')).map(path => path.substr(2));
97
+ // We just want the .js or .ts files
98
+ const ctx = require.context('@shell/config/product', true, /\.\/.*\.[js|ts]/);
99
+ const products = ctx.keys().map(path => path.substr(2)).map(path => path.slice(0, -3));
99
100
 
100
101
  return products;
101
102
  }
@@ -299,7 +299,7 @@ class PaginationUtils {
299
299
  return isEqual(aPrimitiveTypes, bPrimitiveTypes) &&
300
300
  this.paginationFiltersEqual(aFilter, bFilter) &&
301
301
  this.paginationFiltersEqual(aPN, bPN) &&
302
- sameArrayObjects<PaginationSort>(aSort, bSort, true);
302
+ sameArrayObjects<PaginationSort>(aSort, bSort, false);
303
303
  }
304
304
  }
305
305
 
@@ -5,14 +5,15 @@ import { UI_PLUGIN_BASE_URL, isSupportedChartVersion, UI_PLUGIN_LABELS } from '@
5
5
  import { Plugin, Version } from '@shell/types/uiplugins';
6
6
 
7
7
  const MAX_RETRIES = 10;
8
- const RETRY_WAIT = 2500;
8
+ const RETRY_WAIT = 2500; // 2.5 seconds
9
+ const ACTIVE_STATUS_TIMEOUT = 200000; // 20 seconds
9
10
 
10
11
  type Action = 'install' | 'upgrade';
11
12
  export type HelmRepository = any;
12
13
  export type HelmChart = any;
13
14
 
14
15
  /**
15
- *
16
+ * Get the latest compatible version of a Helm Chart extension
16
17
  * @param store Vue store
17
18
  * @param chartName The chartName
18
19
  * @param rancherVersion Rancher version
@@ -68,8 +69,8 @@ export async function waitForUIExtension(store: any, name: string, maxRetries =
68
69
  return extension;
69
70
  }
70
71
  } catch (e) {
72
+ console.error('waiting for UI extension to be available: error =', e); // eslint-disable-line no-console
71
73
  }
72
-
73
74
  tries++;
74
75
 
75
76
  if (tries > maxRetries) {
@@ -106,7 +107,6 @@ export async function waitForUIPackage(store: any, extension: any, maxRetries =
106
107
  return true;
107
108
  } catch (error) {
108
109
  }
109
-
110
110
  tries++;
111
111
 
112
112
  if (tries > maxRetries) {
@@ -199,7 +199,7 @@ export async function getHelmRepositoryMatch(store: any, urlRegexes: string[], c
199
199
  }
200
200
 
201
201
  /**
202
- *
202
+ * Get a Helm Repository matching the given criteria.
203
203
  * @param store Vue store
204
204
  * @param matchFn Match function for repository's urls
205
205
  * @returns HelmRepository
@@ -225,15 +225,15 @@ export async function refreshHelmRepository(store: any, url: string): Promise<vo
225
225
  const now = (new Date()).toISOString().replace(/\.\d+Z$/, 'Z');
226
226
 
227
227
  repository.spec.forceUpdate = now;
228
-
229
228
  await repository.save();
230
229
 
231
- await repository.waitForState('active', 10000, 1000);
230
+ await repository.waitForState('active', ACTIVE_STATUS_TIMEOUT, RETRY_WAIT);
232
231
 
233
232
  await new Promise((resolve) => setTimeout(resolve, 2000));
234
233
  }
235
234
 
236
235
  /**
236
+ * Create a Helm Repository and wait for it to be downloaded
237
237
  *
238
238
  * @param store Vue store
239
239
  * @param name Repository name
@@ -260,7 +260,7 @@ export async function createHelmRepository(store: any, name: string, url: string
260
260
 
261
261
  const helmRepo = await repo.save();
262
262
 
263
- // Poll the repository until it says it has been downloaded
263
+ // Poll the repository status MAX_RETRIES times until it has been downloaded
264
264
  let fetched = false;
265
265
  let tries = 0;
266
266
 
@@ -275,23 +275,19 @@ export async function createHelmRepository(store: any, name: string, url: string
275
275
 
276
276
  const downloaded = repo.status.conditions.find((s: any) => s.type === 'Downloaded');
277
277
 
278
- if (downloaded) {
279
- if (downloaded.status === 'True') {
280
- fetched = true;
281
- }
278
+ console.log(`Waiting for helm repository to be downloaded... try ${ tries } time(s).`); // eslint-disable-line no-console
279
+
280
+ if (downloaded && downloaded.status === 'True') {
281
+ fetched = true;
282
282
  }
283
283
 
284
284
  if (!fetched) {
285
- tries++;
286
-
287
285
  if (tries > MAX_RETRIES) {
288
286
  throw new Error('Failed to add Helm Chart Repository');
289
287
  }
290
288
 
291
289
  await new Promise((resolve) => setTimeout(resolve, RETRY_WAIT));
292
290
  }
293
-
294
- fetched = true;
295
291
  }
296
292
 
297
293
  // Return the Helm Repository
@@ -0,0 +1,76 @@
1
+ import { privateRegistryRequired } from '@shell/utils/validators/private-registry';
2
+
3
+ const makeCtx = (overrides: any = {}) => ({
4
+ t: jest.fn((key: string, params?: any) => (params ? `${ key }:${ JSON.stringify(params) }` : key)),
5
+ privateRegistryEnabled: false,
6
+ normanCluster: { importedConfig: { privateRegistryURL: null } },
7
+ isImportedCluster: true,
8
+ ...overrides,
9
+ });
10
+
11
+ describe('privateRegistryRequired', () => {
12
+ it('should return undefined when the cluster is not imported', () => {
13
+ const ctx = makeCtx({ isImportedCluster: false, privateRegistryEnabled: true });
14
+ const rule = privateRegistryRequired(ctx);
15
+
16
+ expect(rule()).toBeUndefined();
17
+ });
18
+
19
+ it('should default isImportedCluster to true when the property is absent from the context', () => {
20
+ const ctx: any = {
21
+ t: jest.fn((key: string, params?: any) => (params ? `${ key }:${ JSON.stringify(params) }` : key)),
22
+ privateRegistryEnabled: true,
23
+ normanCluster: { importedConfig: { privateRegistryURL: null } },
24
+ };
25
+ const rule = privateRegistryRequired(ctx);
26
+
27
+ expect(rule()).toStrictEqual('validation.required:{"key":"cluster.privateRegistry.label"}');
28
+ });
29
+
30
+ it('should return undefined when the registry toggle is off', () => {
31
+ const ctx = makeCtx({ privateRegistryEnabled: false });
32
+ const rule = privateRegistryRequired(ctx);
33
+
34
+ expect(rule()).toBeUndefined();
35
+ });
36
+
37
+ it('should return a required error when enabled but the url is empty', () => {
38
+ const ctx = makeCtx({
39
+ privateRegistryEnabled: true,
40
+ normanCluster: { importedConfig: { privateRegistryURL: '' } },
41
+ });
42
+ const rule = privateRegistryRequired(ctx);
43
+
44
+ expect(rule()).toStrictEqual('validation.required:{"key":"cluster.privateRegistry.label"}');
45
+ });
46
+
47
+ it('should return a required error when normanCluster.importedConfig is missing', () => {
48
+ const ctx = makeCtx({
49
+ privateRegistryEnabled: true,
50
+ normanCluster: {},
51
+ });
52
+ const rule = privateRegistryRequired(ctx);
53
+
54
+ expect(rule()).toStrictEqual('validation.required:{"key":"cluster.privateRegistry.label"}');
55
+ });
56
+
57
+ it('should return a format error when enabled and the url is malformed', () => {
58
+ const ctx = makeCtx({
59
+ privateRegistryEnabled: true,
60
+ normanCluster: { importedConfig: { privateRegistryURL: 'goober' } },
61
+ });
62
+ const rule = privateRegistryRequired(ctx);
63
+
64
+ expect(rule()).toStrictEqual('cluster.privateRegistry.privateRegistryUrlError');
65
+ });
66
+
67
+ it('should return undefined when enabled and the url is valid', () => {
68
+ const ctx = makeCtx({
69
+ privateRegistryEnabled: true,
70
+ normanCluster: { importedConfig: { privateRegistryURL: 'registry.io:5000' } },
71
+ });
72
+ const rule = privateRegistryRequired(ctx);
73
+
74
+ expect(rule()).toBeUndefined();
75
+ });
76
+ });
@@ -0,0 +1,28 @@
1
+ import { Translation } from '@shell/types/t';
2
+ import formRulesGenerator from '@shell/utils/validators/formRules';
3
+
4
+ interface PrivateRegistryRuleContext {
5
+ t: Translation;
6
+ privateRegistryEnabled: boolean;
7
+ normanCluster: { importedConfig?: { privateRegistryURL?: string | null } } | null;
8
+ isImportedCluster?: boolean;
9
+ }
10
+
11
+ export function privateRegistryRequired(ctx: PrivateRegistryRuleContext) {
12
+ return () => {
13
+ // Check existence using `in` rather than direct access to avoid Vue's render-time warning
14
+ const isImported = 'isImportedCluster' in ctx ? !!ctx.isImportedCluster : true;
15
+
16
+ if (!isImported || !ctx.privateRegistryEnabled) {
17
+ return undefined;
18
+ }
19
+ const url = ctx.normanCluster?.importedConfig?.privateRegistryURL;
20
+
21
+ if (!url) {
22
+ return ctx.t('validation.required', { key: ctx.t('cluster.privateRegistry.label') });
23
+ }
24
+ const { registryUrl } = formRulesGenerator(ctx.t, { key: ctx.t('cluster.privateRegistry.label') });
25
+
26
+ return (registryUrl as (val: string) => string | undefined)(url);
27
+ };
28
+ }