@rancher/shell 2.0.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 (112) hide show
  1. package/assets/translations/en-us.yaml +73 -34
  2. package/assets/translations/zh-hans.yaml +1 -0
  3. package/components/AssignTo.vue +2 -0
  4. package/components/PromptRemove.vue +8 -3
  5. package/components/Questions/index.vue +2 -2
  6. package/components/ResourceDetail/Masthead.vue +1 -0
  7. package/components/auth/RoleDetailEdit.vue +5 -4
  8. package/components/fleet/FleetClusters.vue +0 -3
  9. package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
  10. package/components/form/ProjectMemberEditor.vue +1 -1
  11. package/components/form/ResourceLabeledSelect.vue +11 -3
  12. package/components/form/labeled-select-utils/labeled-select.utils.ts +1 -1
  13. package/components/formatter/CloudCredExpired.vue +69 -0
  14. package/components/formatter/Date.vue +1 -1
  15. package/components/nav/Header.vue +9 -5
  16. package/components/nav/TopLevelMenu.vue +115 -51
  17. package/components/nav/__tests__/TopLevelMenu.test.ts +53 -27
  18. package/config/labels-annotations.js +2 -0
  19. package/config/pagination-table-headers.js +5 -4
  20. package/config/roles.ts +34 -19
  21. package/config/router/navigation-guards/attempt-first-login.js +1 -1
  22. package/config/router/navigation-guards/authentication.js +1 -1
  23. package/config/router/navigation-guards/i18n.js +1 -1
  24. package/config/router/navigation-guards/index.js +2 -1
  25. package/config/router/navigation-guards/load-initial-settings.js +1 -1
  26. package/config/router/navigation-guards/runtime-extension-route.js +31 -0
  27. package/config/router/routes.js +10 -1
  28. package/config/uiplugins.js +130 -61
  29. package/core/plugin.ts +5 -0
  30. package/core/plugins.js +7 -1
  31. package/detail/catalog.cattle.io.app.vue +17 -4
  32. package/detail/fleet.cattle.io.cluster.vue +11 -9
  33. package/detail/fleet.cattle.io.gitrepo.vue +1 -1
  34. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +86 -13
  35. package/edit/provisioning.cattle.io.cluster/__tests__/DirectoryConfig.test.ts +3 -134
  36. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +209 -0
  37. package/edit/provisioning.cattle.io.cluster/index.vue +8 -4
  38. package/edit/provisioning.cattle.io.cluster/rke2.vue +128 -17
  39. package/edit/provisioning.cattle.io.cluster/tabs/AddOnAdditionalManifest.vue +50 -0
  40. package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +29 -64
  41. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +42 -3
  42. package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +22 -86
  43. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +8 -2
  44. package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +61 -0
  45. package/initialize/entry-helpers.js +4 -21
  46. package/list/provisioning.cattle.io.cluster.vue +56 -5
  47. package/mixins/__tests__/chart.test.ts +4 -1
  48. package/mixins/chart.js +36 -16
  49. package/models/__tests__/apps.deployment.test.ts +93 -0
  50. package/models/apps.deployment.js +18 -4
  51. package/models/catalog.cattle.io.app.js +108 -21
  52. package/models/cloudcredential.js +159 -2
  53. package/models/fleet.cattle.io.gitrepo.js +4 -13
  54. package/models/management.cattle.io.cluster.js +15 -4
  55. package/models/management.cattle.io.user.js +3 -3
  56. package/models/nodedriver.js +5 -0
  57. package/models/provisioning.cattle.io.cluster.js +41 -3
  58. package/package.json +1 -1
  59. package/pages/404.vue +15 -0
  60. package/pages/auth/login.vue +4 -1
  61. package/pages/auth/setup.vue +4 -1
  62. package/pages/c/_cluster/apps/charts/install.vue +2 -1
  63. package/pages/c/_cluster/explorer/__tests__/index.test.ts +1 -1
  64. package/pages/c/_cluster/explorer/index.vue +6 -2
  65. package/pages/c/_cluster/fleet/index.vue +11 -5
  66. package/pages/c/_cluster/manager/cloudCredential/index.vue +68 -4
  67. package/pages/c/_cluster/manager/jwt.authentication/index.vue +10 -4
  68. package/pages/c/_cluster/settings/performance.vue +2 -2
  69. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +7 -10
  70. package/pages/c/_cluster/uiplugins/index.vue +28 -18
  71. package/pages/home.vue +2 -13
  72. package/plugins/dashboard-store/actions.js +1 -1
  73. package/plugins/dashboard-store/getters.js +1 -1
  74. package/plugins/steve/__tests__/getters.test.ts +5 -5
  75. package/plugins/steve/getters.js +6 -4
  76. package/plugins/steve/hybrid-class.js +1 -5
  77. package/scripts/extension/bundle +1 -1
  78. package/scripts/extension/helm/charts/ui-plugin-server/Chart.yaml +1 -1
  79. package/scripts/publish-shell.sh +56 -59
  80. package/scripts/test-plugins-build.sh +45 -39
  81. package/scripts/typegen.sh +26 -23
  82. package/store/type-map.js +4 -2
  83. package/types/shell/index.d.ts +10 -0
  84. package/types/store/pagination.types.ts +1 -1
  85. package/utils/cluster.js +9 -0
  86. package/utils/settings.ts +3 -1
  87. package/utils/string.js +9 -0
  88. package/utils/v-sphere.ts +251 -0
  89. package/creators/app/app.package.json +0 -14
  90. package/creators/app/files/.eslintignore +0 -16
  91. package/creators/app/files/.eslintrc.js +0 -173
  92. package/creators/app/files/.gitignore +0 -70
  93. package/creators/app/files/.gitlab-ci.yml +0 -14
  94. package/creators/app/files/.vscode/settings.json +0 -21
  95. package/creators/app/files/babel.config.js +0 -1
  96. package/creators/app/files/tsconfig.json +0 -42
  97. package/creators/app/files/vue.config.js +0 -6
  98. package/creators/app/init +0 -120
  99. package/creators/app/package.json +0 -25
  100. package/creators/pkg/files/.github/workflows/build-extension-catalog.yml +0 -24
  101. package/creators/pkg/files/.github/workflows/build-extension-charts.yml +0 -22
  102. package/creators/pkg/files/babel.config.js +0 -1
  103. package/creators/pkg/files/index.ts +0 -14
  104. package/creators/pkg/files/tsconfig.json +0 -53
  105. package/creators/pkg/files/vue.config.js +0 -1
  106. package/creators/pkg/init +0 -286
  107. package/creators/pkg/package.json +0 -19
  108. package/creators/pkg/pkg.package.json +0 -21
  109. package/creators/pkg/vue-shim.ts +0 -4
  110. package/creators/update/init +0 -56
  111. package/creators/update/package.json +0 -20
  112. package/creators/update/upgrade +0 -56
@@ -0,0 +1,31 @@
1
+ import dynamicPluginLoader from '@shell/pkg/dynamic-plugin-loader';
2
+ import { routeRequiresAuthentication } from '@shell/utils/router';
3
+
4
+ export function install(router, context) {
5
+ router.beforeEach((to, from, next) => runtimeExtensionRoute(to, from, next, context));
6
+ }
7
+
8
+ export async function runtimeExtensionRoute(to, from, next, { store }) {
9
+ if (!routeRequiresAuthentication(to) || to.name !== '404') {
10
+ return next();
11
+ }
12
+
13
+ try {
14
+ // Handle the loading of dynamic plugins (Harvester) because we only want to attempt to load those plugins and routes if we first couldn't find a page.
15
+ // We should probably get rid of this concept entirely and just load plugins at the start.
16
+ await store.dispatch('loadManagement');
17
+ const newLocation = await dynamicPluginLoader.check({ route: { path: window.location.pathname }, store });
18
+
19
+ // If we have a new location, double check that it's actually valid
20
+ const resolvedRoute = newLocation?.path ? store.app.router.resolve({ path: newLocation.path.replace(/^\/{0,1}dashboard/, '') }) : null;
21
+
22
+ if (resolvedRoute?.route.matched.length) {
23
+ // Note - don't use `redirect` or `store.app.route` (breaks feature by failing to run middleware in default layout)
24
+ return next(resolvedRoute.resolved.path);
25
+ }
26
+ } catch (e) {
27
+ console.error('Failed to load harvester', e); // eslint-disable-line no-console
28
+ }
29
+
30
+ next();
31
+ }
@@ -41,6 +41,7 @@ export default [
41
41
  path: '',
42
42
  component: () => interopDefault(import('@shell/components/templates/home.vue')),
43
43
  meta: { requiresAuthentication: true },
44
+ name: 'home_layout',
44
45
  children: [
45
46
  {
46
47
  path: '/home',
@@ -484,4 +485,12 @@ export default [
484
485
  component: () => interopDefault(import('@shell/pages/c/_cluster/_product/_resource/_namespace/_id.vue')),
485
486
  name: 'c-cluster-product-resource-namespace-id'
486
487
  }]
487
- }];
488
+ },
489
+ {
490
+ path: '*',
491
+ name: '404',
492
+ component: () => interopDefault(import('@shell/pages/404.vue')),
493
+ meta: { requiresAuthentication: true },
494
+ },
495
+
496
+ ];
@@ -68,6 +68,36 @@ export const UI_PLUGIN_METADATA = {
68
68
  DISPLAY_NAME: 'displayName',
69
69
  };
70
70
 
71
+ export const EXTENSIONS_INCOMPATIBILITY_TYPES = {
72
+ UI: 'uiVersion',
73
+ EXTENSIONS_API: 'extensionsApiVersion',
74
+ KUBE: 'kubeVersion',
75
+ HOST: 'host'
76
+ };
77
+
78
+ export const EXTENSIONS_INCOMPATIBILITY_DATA = {
79
+ UI: {
80
+ type: EXTENSIONS_INCOMPATIBILITY_TYPES.UI,
81
+ cardMessageKey: 'plugins.incompatibleRancherVersion',
82
+ tooltipKey: 'plugins.info.requiresRancherVersion',
83
+ },
84
+ EXTENSIONS_API: {
85
+ type: EXTENSIONS_INCOMPATIBILITY_TYPES.EXTENSIONS_API,
86
+ cardMessageKey: 'plugins.incompatibleUiExtensionsApiVersion',
87
+ tooltipKey: 'plugins.info.requiresExtensionApiVersion',
88
+ },
89
+ KUBE: {
90
+ type: EXTENSIONS_INCOMPATIBILITY_TYPES.KUBE,
91
+ cardMessageKey: 'plugins.incompatibleKubeVersion',
92
+ tooltipKey: 'plugins.info.requiresKubeVersion',
93
+ },
94
+ HOST: {
95
+ type: EXTENSIONS_INCOMPATIBILITY_TYPES.HOST,
96
+ cardMessageKey: 'plugins.incompatibleHost',
97
+ tooltipKey: 'plugins.info.requiresHost',
98
+ }
99
+ };
100
+
71
101
  export function isUIPlugin(chart) {
72
102
  return !!chart?.versions.find((v) => {
73
103
  return v.annotations && v.annotations[UI_PLUGIN_ANNOTATION_NAME] === UI_PLUGIN_ANNOTATION_VALUE;
@@ -91,130 +121,169 @@ export function uiPluginAnnotation(chart, name) {
91
121
  return undefined;
92
122
  }
93
123
 
124
+ /**
125
+ * Parse the rancher version string
126
+ */
127
+ function parseRancherVersion(v) {
128
+ let parsedRancherVersion = semver.coerce(v)?.version;
129
+ const splitArr = parsedRancherVersion.split('.');
130
+
131
+ // this is a scenario where we are on a "head" version of some sort... we can't infer the patch version from it
132
+ // so we apply a big patch version number to make sure we follow through with the minor
133
+ if (v.includes('-') && splitArr?.length === 3) {
134
+ parsedRancherVersion = `${ splitArr[0] }.${ splitArr[1] }.999`;
135
+ }
136
+
137
+ return parsedRancherVersion;
138
+ }
139
+
94
140
  // i18n-uses plugins.error.generic, plugins.error.api, plugins.error.host
95
141
 
96
- // Should we load a plugin, based on the metadata returned by the backend?
97
- // Returns error key string or false
98
- export function shouldNotLoadPlugin(plugin, rancherVersion, loadedPlugins) {
99
- if (!plugin.name || !plugin.version || !plugin.endpoint) {
142
+ /**
143
+ * Whether an extension should be loaded based on the metadata returned by the backend in the UIPlugins resource instance
144
+ * @returns String || Boolean
145
+ */
146
+ export function shouldNotLoadPlugin(UIPluginResource, rancherVersion, loadedPlugins) {
147
+ if (!UIPluginResource.name || !UIPluginResource.version || !UIPluginResource.endpoint) {
100
148
  return 'plugins.error.generic';
101
149
  }
102
150
 
103
- // Plugin specified a required extension API version
151
+ // Extension chart specified a required extension API version
104
152
  // we are propagating the annotations in pkg/package.json for any extension
105
153
  // inside the "spec.plugin.metadata" property of UIPlugin resource
106
- const requiredAPI = plugin.spec?.plugin?.metadata?.[UI_PLUGIN_CHART_ANNOTATIONS.EXTENSIONS_VERSION];
154
+ const requiredUiExtensionsVersion = UIPluginResource.spec?.plugin?.metadata?.[UI_PLUGIN_CHART_ANNOTATIONS.EXTENSIONS_VERSION];
155
+ // semver.coerce will get rid of any suffix on the version numbering (-rc, -head, etc)
156
+ const parsedUiExtensionsApiVersion = semver.coerce(UI_EXTENSIONS_API_VERSION)?.version || UI_EXTENSIONS_API_VERSION;
157
+ const parsedRancherVersion = rancherVersion ? parseRancherVersion(rancherVersion) : '';
107
158
 
108
- if (requiredAPI && !semver.satisfies(UI_EXTENSIONS_API_VERSION, requiredAPI)) {
159
+ if (requiredUiExtensionsVersion && !semver.satisfies(parsedUiExtensionsApiVersion, requiredUiExtensionsVersion)) {
109
160
  return 'plugins.error.api';
110
161
  }
111
162
 
112
163
  // Host application
113
- const requiredHost = plugin.metadata?.[UI_PLUGIN_METADATA.EXTENSIONS_HOST];
164
+ const requiredHost = UIPluginResource.metadata?.[UI_PLUGIN_METADATA.EXTENSIONS_HOST];
114
165
 
115
166
  if (requiredHost && requiredHost !== UI_PLUGIN_HOST_APP) {
116
167
  return 'plugins.error.host';
117
168
  }
118
169
 
119
170
  // Rancher version
120
- if (rancherVersion) {
121
- const requiredRancherVersion = plugin.metadata?.[UI_PLUGIN_METADATA.RANCHER_VERSION];
171
+ if (parsedRancherVersion) {
172
+ const requiredRancherVersion = UIPluginResource.metadata?.[UI_PLUGIN_METADATA.RANCHER_VERSION];
122
173
 
123
- if (requiredRancherVersion && !semver.satisfies(rancherVersion, requiredRancherVersion)) {
174
+ if (requiredRancherVersion && !semver.satisfies(parsedRancherVersion, requiredRancherVersion)) {
124
175
  return 'plugins.error.version';
125
176
  }
126
177
  }
127
178
 
128
179
  // check if a builtin extension has been loaded before - improve developer experience
129
- const checkLoaded = loadedPlugins.find((p) => p?.name === plugin?.name);
180
+ const checkLoaded = loadedPlugins.find((p) => p?.name === UIPluginResource?.name);
130
181
 
131
182
  if (checkLoaded && checkLoaded.builtin) {
132
183
  return 'plugins.error.developerPkg';
133
184
  }
134
185
 
135
- if (plugin.metadata?.[UI_PLUGIN_LABELS.CATALOG]) {
186
+ if (UIPluginResource.metadata?.[UI_PLUGIN_LABELS.CATALOG]) {
136
187
  return true;
137
188
  }
138
189
 
139
190
  return false;
140
191
  }
141
192
 
142
- // Can a chart version be used for this Rancher (based on the annotations on the chart)?
143
- export function isSupportedChartVersion(versionsData) {
144
- const { version, rancherVersion, kubeVersion } = versionsData;
193
+ /**
194
+ * Wether an extension version is available to be installed, based on the annotations present in the Helm chart version
195
+ * backend may not automatically "limit" a particular version but dashboard will disable that version for install with this check
196
+ * @returns Boolean || Object
197
+ */
198
+ export function isSupportedChartVersion(versionData, returnObj = false) {
199
+ const { version, rancherVersion, kubeVersion } = versionData;
145
200
 
146
- // Plugin specified a required extension API version
147
- const requiredAPI = version.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.EXTENSIONS_VERSION];
201
+ // semver.coerce will get rid of any suffix on the version numbering (-rc, -head, etc)
202
+ const parsedRancherVersion = rancherVersion ? parseRancherVersion(rancherVersion) : '';
203
+ const requiredUiVersion = version.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.UI_VERSION];
204
+ const requiredKubeVersion = version.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.KUBE_VERSION];
205
+ const versionObj = { ...version };
148
206
 
149
- if (requiredAPI && !semver.satisfies(UI_EXTENSIONS_API_VERSION, requiredAPI)) {
150
- return false;
151
- }
207
+ // reset compatibility property
208
+ versionObj.isVersionCompatible = true;
209
+ versionObj.versionIncompatibilityData = {};
152
210
 
153
- // Host application
154
- const requiredHost = version.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.EXTENSIONS_HOST];
211
+ // check "catalog.cattle.io/kube-version" annotation
212
+ // we keep it as first check since there is a card notification to be displayed
213
+ // in case an extension version installed has an incompatibility with the kube version and is not loaded
214
+ if (kubeVersion && requiredKubeVersion && !semver.satisfies(kubeVersion, requiredKubeVersion)) {
215
+ if (!returnObj) {
216
+ return false;
217
+ }
155
218
 
156
- if (requiredHost && requiredHost !== UI_PLUGIN_HOST_APP) {
157
- return false;
219
+ versionObj.isVersionCompatible = false;
220
+ versionObj.versionIncompatibilityData = Object.assign({}, EXTENSIONS_INCOMPATIBILITY_DATA.KUBE);
221
+ versionObj.versionIncompatibilityData.required = requiredKubeVersion;
222
+
223
+ return versionObj;
158
224
  }
159
225
 
160
- // Rancher version
161
- if (rancherVersion) {
226
+ // we aren't on a "published" version of Rancher and therefore in a "-head" or similar
227
+ // Backend will NOT block an extension version from being available IF we are on HEAD versions!!
228
+ // we need to enforce that check if we are on a HEAD world
229
+ if (rancherVersion && rancherVersion.includes('-')) {
162
230
  const requiredRancherVersion = version.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.RANCHER_VERSION];
163
231
 
164
- if (requiredRancherVersion && !semver.satisfies(rancherVersion, requiredRancherVersion)) {
165
- return false;
232
+ if (parsedRancherVersion && !semver.satisfies(parsedRancherVersion, requiredRancherVersion)) {
233
+ if (!returnObj) {
234
+ return false;
235
+ }
236
+
237
+ versionObj.isVersionCompatible = false;
238
+ versionObj.versionIncompatibilityData = Object.assign({}, EXTENSIONS_INCOMPATIBILITY_DATA.UI);
239
+ versionObj.versionIncompatibilityData.required = requiredRancherVersion;
240
+
241
+ return versionObj;
166
242
  }
167
243
  }
168
244
 
169
- // Kube version
170
- if (kubeVersion) {
171
- const requiredKubeVersion = version.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.KUBE_VERSION];
245
+ // check host application
246
+ const requiredHost = version.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.EXTENSIONS_HOST];
172
247
 
173
- if (requiredKubeVersion && !semver.satisfies(kubeVersion, requiredKubeVersion)) {
248
+ if (requiredHost && requiredHost !== UI_PLUGIN_HOST_APP) {
249
+ if (!returnObj) {
174
250
  return false;
175
251
  }
176
- }
177
252
 
178
- return true;
179
- }
180
-
181
- export function isChartVersionAvailableForInstall(versionsData, returnObj = false) {
182
- const { version, rancherVersion, kubeVersion } = versionsData;
253
+ versionObj.isVersionCompatible = false;
254
+ versionObj.versionIncompatibilityData = Object.assign({}, EXTENSIONS_INCOMPATIBILITY_DATA.HOST);
255
+ versionObj.versionIncompatibilityData.required = requiredHost;
183
256
 
184
- const parsedRancherVersion = rancherVersion.split('-')?.[0];
185
- const regexHashString = new RegExp('^[A-Za-z0-9]{9}$');
186
- const isRancherVersionHashString = regexHashString.test(rancherVersion);
187
- const requiredUiVersion = version.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.UI_VERSION];
188
- const requiredKubeVersion = version.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.KUBE_VERSION];
189
- const versionObj = { ...version };
257
+ return versionObj;
258
+ }
190
259
 
191
- versionObj.isCompatibleWithUi = true;
192
- versionObj.isCompatibleWithKubeVersion = true;
260
+ // check "catalog.cattle.io/ui-extensions-version" annotation
261
+ const requiredUiExtensionsApiVersion = version.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.EXTENSIONS_VERSION];
262
+ const parsedUiExtensionsApiVersion = semver.coerce(UI_EXTENSIONS_API_VERSION)?.version || UI_EXTENSIONS_API_VERSION;
193
263
 
194
- // if it's a head version of Rancher, then we skip the validation and enable them all
195
- if (!isRancherVersionHashString && requiredUiVersion && !semver.satisfies(parsedRancherVersion, requiredUiVersion)) {
264
+ if (requiredUiExtensionsApiVersion && parsedUiExtensionsApiVersion && !semver.satisfies(parsedUiExtensionsApiVersion, requiredUiExtensionsApiVersion)) {
196
265
  if (!returnObj) {
197
266
  return false;
198
267
  }
199
- versionObj.isCompatibleWithUi = false;
200
- versionObj.requiredUiVersion = requiredUiVersion;
201
268
 
202
- if (returnObj) {
203
- return versionObj;
204
- }
269
+ versionObj.isVersionCompatible = false;
270
+ versionObj.versionIncompatibilityData = Object.assign({}, EXTENSIONS_INCOMPATIBILITY_DATA.EXTENSIONS_API);
271
+ versionObj.versionIncompatibilityData.required = requiredUiExtensionsApiVersion;
272
+
273
+ return versionObj;
205
274
  }
206
275
 
207
- // check kube version
208
- if (kubeVersion && requiredKubeVersion && !semver.satisfies(kubeVersion, requiredKubeVersion)) {
276
+ // check "catalog.cattle.io/ui-version" annotation
277
+ if (requiredUiVersion && parsedRancherVersion && !semver.satisfies(parsedRancherVersion, requiredUiVersion)) {
209
278
  if (!returnObj) {
210
279
  return false;
211
280
  }
212
- versionObj.isCompatibleWithKubeVersion = false;
213
- versionObj.requiredKubeVersion = requiredKubeVersion;
214
281
 
215
- if (returnObj) {
216
- return versionObj;
217
- }
282
+ versionObj.isVersionCompatible = false;
283
+ versionObj.versionIncompatibilityData = Object.assign({}, EXTENSIONS_INCOMPATIBILITY_DATA.UI);
284
+ versionObj.versionIncompatibilityData.required = requiredUiVersion;
285
+
286
+ return versionObj;
218
287
  }
219
288
 
220
289
  if (returnObj) {
package/core/plugin.ts CHANGED
@@ -132,6 +132,11 @@ export class Plugin implements IPlugin {
132
132
  console.warn(`Layouts have been deprecated. We still have parent routes which use the same name and styling as the previous layouts. You should specify a parent, we're currently setting the parent to 'default'`); // eslint-disable-line no-console
133
133
  parentOverride = 'default';
134
134
  }
135
+
136
+ // Fix for Home page components with wrong layout - need to ensure the parentOverride is set
137
+ if (typelessRoute.component && typelessRoute.name === 'home' && typelessRoute.path === '/home') {
138
+ parentOverride = 'home_layout';
139
+ }
135
140
  }
136
141
 
137
142
  route.meta = {
package/core/plugins.js CHANGED
@@ -90,6 +90,10 @@ export default function(context, inject, vueApp) {
90
90
  delete window[oldPlugin.id];
91
91
 
92
92
  delete plugins[oldPlugin.id];
93
+
94
+ const oldElement = document.getElementById(oldPlugin.id);
95
+
96
+ oldElement.parentElement.removeChild(oldElement);
93
97
  });
94
98
  }
95
99
 
@@ -124,7 +128,9 @@ export default function(context, inject, vueApp) {
124
128
  };
125
129
 
126
130
  element.onerror = (e) => {
127
- // Massage the error into something useful
131
+ element.parentElement.removeChild(element);
132
+
133
+ // Massage the error into something useful
128
134
  const errorMessage = `Failed to load script from '${ e.target.src }'`;
129
135
 
130
136
  console.error(errorMessage, e); // eslint-disable-line no-console
@@ -9,7 +9,8 @@ import RelatedResources from '@shell/components/RelatedResources';
9
9
  import jsyaml from 'js-yaml';
10
10
  import merge from 'lodash/merge';
11
11
  import { CATALOG } from '@shell/config/types';
12
- import { sortBy } from '~shell/utils/sort';
12
+ import { sortBy } from '@shell/utils/sort';
13
+ import { allHash } from '@shell/utils/promise';
13
14
 
14
15
  export default {
15
16
  name: 'DetailRelease',
@@ -30,9 +31,15 @@ export default {
30
31
  },
31
32
 
32
33
  async fetch() {
33
- await this.$store.dispatch('catalog/load');
34
+ const promises = {
35
+ catalog: this.$store.dispatch('catalog/load'),
36
+ allOperations: this.$store.dispatch('cluster/findAll', { type: CATALOG.OPERATION }),
37
+ secrets: this.value.fetchValues(true),
38
+ };
34
39
 
35
- this.allOperations = await this.$store.dispatch('cluster/findAll', { type: CATALOG.OPERATION });
40
+ const res = await allHash(promises);
41
+
42
+ this.allOperations = res.allOperations;
36
43
  },
37
44
 
38
45
  computed: {
@@ -45,7 +52,7 @@ export default {
45
52
  },
46
53
 
47
54
  valuesYaml() {
48
- const combined = merge(merge({}, this.value?.spec?.chart?.values || {}), this.value?.spec?.values || {});
55
+ const combined = merge(merge({}, this.value?.chartValues || {}), this.value?.values || {});
49
56
 
50
57
  return jsyaml.dump(combined);
51
58
  },
@@ -95,6 +102,12 @@ export default {
95
102
  }
96
103
  },
97
104
  },
105
+
106
+ watch: {
107
+ 'value.secretId'(neu, old) {
108
+ this.value.fetchValues(true);
109
+ }
110
+ },
98
111
  };
99
112
  </script>
100
113
 
@@ -6,6 +6,7 @@ import ResourceTabs from '@shell/components/form/ResourceTabs';
6
6
  import Tab from '@shell/components/Tabbed/Tab';
7
7
  import { MANAGEMENT, FLEET } from '@shell/config/types';
8
8
  import { FLEET as FLEET_LABELS } from '@shell/config/labels-annotations';
9
+ import { allHash } from 'utils/promise';
9
10
 
10
11
  export default {
11
12
  name: 'FleetDetailCluster',
@@ -27,17 +28,18 @@ export default {
27
28
 
28
29
  async fetch() {
29
30
  const clusterId = this.value?.metadata?.labels[FLEET_LABELS.CLUSTER_NAME];
30
-
31
- this.rancherCluster = await this.$store.dispatch('management/find', {
32
- type: MANAGEMENT.CLUSTER,
33
- id: clusterId
31
+ const hash = await allHash({
32
+ rancherCluster: this.$store.dispatch('management/find', {
33
+ type: MANAGEMENT.CLUSTER,
34
+ id: clusterId
35
+ }),
36
+ repos: this.$store.dispatch('management/findAll', { type: FLEET.GIT_REPO }),
37
+ workspaces: this.$store.dispatch('management/findAll', { type: FLEET.WORKSPACE }),
38
+ bundleDeployments: this.$store.dispatch('management/findAll', { type: FLEET.BUNDLE_DEPLOYMENT })
34
39
  });
35
40
 
36
- this.allRepos = await this.$store.dispatch('management/findAll', { type: FLEET.GIT_REPO });
37
-
38
- await this.$store.dispatch('management/findAll', { type: FLEET.WORKSPACE });
39
-
40
- await this.$store.dispatch('management/findAll', { type: FLEET.BUNDLE_DEPLOYMENT });
41
+ this.rancherCluster = hash.rancherCluster;
42
+ this.allRepos = hash.repos;
41
43
  },
42
44
 
43
45
  data() {
@@ -40,7 +40,7 @@ export default {
40
40
  },
41
41
  computed: {
42
42
  gitRepoHasClusters() {
43
- return this.value?.clusterResourceStatus?.length;
43
+ return this.value.status.desiredReadyClusters;
44
44
  },
45
45
  clusterSchema() {
46
46
  return this.$store.getters['management/schemaFor'](FLEET.CLUSTER);
@@ -27,19 +27,36 @@ const defaultComputed = {
27
27
  const mockAgentArgs = { 'cloud-provider-name': { options: [], profile: { options: [{ anything: 'yes' }] } } };
28
28
  const mockServerArgs = { disable: {}, cni: { options: [] } };
29
29
 
30
- const rke2Versions =
31
- [
32
- {
33
- id: 'v1.25.0+rke2r1', value: 'v1.25.0+rke2r1', serverArgs: mockServerArgs, agentArgs: mockAgentArgs, charts: {}
34
- },
35
- {
36
- id: 'v1.24.0+rke2r1', value: 'v1.24.0+rke2r1', serverArgs: mockServerArgs, agentArgs: mockAgentArgs, charts: {}
37
- },
38
- {
39
- id: 'v1.23.0+rke2r1', value: 'v1.23.0+rke2r1', serverArgs: mockServerArgs, agentArgs: mockAgentArgs, charts: {}
40
- }
41
- ];
30
+ const rke2Versions = [
31
+ {
32
+ id: 'v1.31.0+rke2r1', value: 'v1.31.0+rke2r1', serverArgs: mockServerArgs, agentArgs: mockAgentArgs, charts: {}
33
+ },
34
+ {
35
+ id: 'v1.30.0+rke2r1', value: 'v1.30.0+rke2r1', serverArgs: mockServerArgs, agentArgs: mockAgentArgs, charts: {}
36
+ },
37
+ {
38
+ id: 'v1.29.1+rke2r1', value: 'v1.29.1+rke2r1', serverArgs: mockServerArgs, agentArgs: mockAgentArgs, charts: {}
39
+ },
40
+ {
41
+ id: 'v1.25.0+rke2r1', value: 'v1.25.0+rke2r1', serverArgs: mockServerArgs, agentArgs: mockAgentArgs, charts: {}
42
+ },
43
+ {
44
+ id: 'v1.24.0+rke2r1', value: 'v1.24.0+rke2r1', serverArgs: mockServerArgs, agentArgs: mockAgentArgs, charts: {}
45
+ },
46
+ {
47
+ id: 'v1.23.0+rke2r1', value: 'v1.23.0+rke2r1', serverArgs: mockServerArgs, agentArgs: mockAgentArgs, charts: {}
48
+ }
49
+ ];
42
50
  const k3sVersions = [
51
+ {
52
+ id: 'v1.31.0+k3s1', value: 'v1.31.0+k3s1', serverArgs: mockServerArgs, agentArgs: mockAgentArgs, charts: {}
53
+ },
54
+ {
55
+ id: 'v1.30.0+k3s1', value: 'v1.30.0+k3s1', serverArgs: mockServerArgs, agentArgs: mockAgentArgs, charts: {}
56
+ },
57
+ {
58
+ id: 'v1.29.1+k3s1', value: 'v1.29.1+k3s1', serverArgs: mockServerArgs, agentArgs: mockAgentArgs, charts: {}
59
+ },
43
60
  {
44
61
  id: 'v1.25.0+k3s1', value: 'v1.25.0+k3s1', serverArgs: mockServerArgs, agentArgs: mockAgentArgs, charts: {}
45
62
  },
@@ -88,7 +105,7 @@ const newOffValue = { ipv6: { enabled: false } };
88
105
  const bmOnValue = { bandwidthManager: { enabled: true } };
89
106
  const bmOffValue = { bandwidthManager: { enabled: false } };
90
107
 
91
- function createBasicsTab(version : string, userChartValues: any) {
108
+ function createBasicsTab(version : string, userChartValues: any, options = {}) {
92
109
  const k8s = mockVersionOptions.find((v) => v.id === version) || mockVersionOptions[0];
93
110
  const label = 'whatever';
94
111
  const wrapper = mount(Basics, {
@@ -122,6 +139,7 @@ function createBasicsTab(version : string, userChartValues: any) {
122
139
  showCloudProvider: false,
123
140
  unsupportedCloudProvider: false,
124
141
  cloudProviderOptions: [{ label: 'Default - RKE2 Embedded', value: '' }],
142
+ ...options
125
143
  },
126
144
  computed: defaultComputed,
127
145
  mocks: {
@@ -470,4 +488,59 @@ describe('component: Basics', () => {
470
488
 
471
489
  expect(JSON.stringify(latest)).toStrictEqual(expected);
472
490
  });
491
+
492
+ it.each([
493
+ ['create', true, true, '%cluster.banner.cloudProviderUnsupportedAzure%'],
494
+ ['create', false, true, undefined],
495
+ ['create', true, false, undefined],
496
+ ['edit', true, true, undefined],
497
+ ['view', true, true, undefined],
498
+ ])('should display Unsupported Azure provider warning message', (mode, showCloudProvider, isAzureProviderUnsupported, warningMessage) => {
499
+ const wrapper = createBasicsTab('v1.31.0+rke2r1', {}, {
500
+ mode,
501
+ showCloudProvider,
502
+ isAzureProviderUnsupported,
503
+ canAzureMigrateOnEdit: true
504
+ });
505
+
506
+ const cloudProviderUnsupportedAzureWarningMessage = wrapper.find('[data-testid="clusterBasics__showCloudProviderUnsupportedAzureWarning"]')?.element?.textContent;
507
+
508
+ expect(cloudProviderUnsupportedAzureWarningMessage).toBe(warningMessage);
509
+ });
510
+
511
+ it.each([
512
+ ['edit', true, true, '%cluster.banner.cloudProviderMigrateAzure%'],
513
+ ['edit', false, true, undefined],
514
+ ['edit', true, false, undefined],
515
+ ['create', true, true, undefined],
516
+ ['view', true, true, undefined],
517
+ ])('should display Azure Migration warning message', (mode, showCloudProvider, canAzureMigrateOnEdit, warningMessage) => {
518
+ const wrapper = createBasicsTab('v1.31.0+rke2r1', {}, {
519
+ mode,
520
+ showCloudProvider,
521
+ canAzureMigrateOnEdit,
522
+ isAzureProviderUnsupported: true,
523
+ });
524
+
525
+ const cloudProviderMigrateAzureWarningMessage = wrapper.find('[data-testid="clusterBasics__showCloudProviderMigrateAzureWarning"]')?.element?.textContent;
526
+
527
+ expect(cloudProviderMigrateAzureWarningMessage).toBe(warningMessage);
528
+ });
529
+
530
+ it.each([
531
+ ['create', true, false],
532
+ ['edit', false, true],
533
+ ['edit', true, false],
534
+ ['view', true, false],
535
+ ])('should disable Cloud Provider', (mode, canAzureMigrateOnEdit, disabled) => {
536
+ const wrapper = createBasicsTab('v1.31.0+rke2r1', {}, {
537
+ mode,
538
+ showCloudProvider: true,
539
+ canAzureMigrateOnEdit,
540
+ });
541
+
542
+ const cloudProvider = wrapper.find('[data-testid="clusterBasics__cloudProvider"]');
543
+
544
+ expect(cloudProvider.props().disabled).toBe(disabled);
545
+ });
473
546
  });