@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.
- package/assets/translations/en-us.yaml +73 -34
- package/assets/translations/zh-hans.yaml +1 -0
- package/components/AssignTo.vue +2 -0
- package/components/PromptRemove.vue +8 -3
- package/components/Questions/index.vue +2 -2
- package/components/ResourceDetail/Masthead.vue +1 -0
- package/components/auth/RoleDetailEdit.vue +5 -4
- package/components/fleet/FleetClusters.vue +0 -3
- package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
- package/components/form/ProjectMemberEditor.vue +1 -1
- package/components/form/ResourceLabeledSelect.vue +11 -3
- package/components/form/labeled-select-utils/labeled-select.utils.ts +1 -1
- package/components/formatter/CloudCredExpired.vue +69 -0
- package/components/formatter/Date.vue +1 -1
- package/components/nav/Header.vue +9 -5
- package/components/nav/TopLevelMenu.vue +115 -51
- package/components/nav/__tests__/TopLevelMenu.test.ts +53 -27
- package/config/labels-annotations.js +2 -0
- package/config/pagination-table-headers.js +5 -4
- package/config/roles.ts +34 -19
- package/config/router/navigation-guards/attempt-first-login.js +1 -1
- package/config/router/navigation-guards/authentication.js +1 -1
- package/config/router/navigation-guards/i18n.js +1 -1
- package/config/router/navigation-guards/index.js +2 -1
- package/config/router/navigation-guards/load-initial-settings.js +1 -1
- package/config/router/navigation-guards/runtime-extension-route.js +31 -0
- package/config/router/routes.js +10 -1
- package/config/uiplugins.js +130 -61
- package/core/plugin.ts +5 -0
- package/core/plugins.js +7 -1
- package/detail/catalog.cattle.io.app.vue +17 -4
- package/detail/fleet.cattle.io.cluster.vue +11 -9
- package/detail/fleet.cattle.io.gitrepo.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +86 -13
- package/edit/provisioning.cattle.io.cluster/__tests__/DirectoryConfig.test.ts +3 -134
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +209 -0
- package/edit/provisioning.cattle.io.cluster/index.vue +8 -4
- package/edit/provisioning.cattle.io.cluster/rke2.vue +128 -17
- package/edit/provisioning.cattle.io.cluster/tabs/AddOnAdditionalManifest.vue +50 -0
- package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +29 -64
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +42 -3
- package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +22 -86
- package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +8 -2
- package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +61 -0
- package/initialize/entry-helpers.js +4 -21
- package/list/provisioning.cattle.io.cluster.vue +56 -5
- package/mixins/__tests__/chart.test.ts +4 -1
- package/mixins/chart.js +36 -16
- package/models/__tests__/apps.deployment.test.ts +93 -0
- package/models/apps.deployment.js +18 -4
- package/models/catalog.cattle.io.app.js +108 -21
- package/models/cloudcredential.js +159 -2
- package/models/fleet.cattle.io.gitrepo.js +4 -13
- package/models/management.cattle.io.cluster.js +15 -4
- package/models/management.cattle.io.user.js +3 -3
- package/models/nodedriver.js +5 -0
- package/models/provisioning.cattle.io.cluster.js +41 -3
- package/package.json +1 -1
- package/pages/404.vue +15 -0
- package/pages/auth/login.vue +4 -1
- package/pages/auth/setup.vue +4 -1
- package/pages/c/_cluster/apps/charts/install.vue +2 -1
- package/pages/c/_cluster/explorer/__tests__/index.test.ts +1 -1
- package/pages/c/_cluster/explorer/index.vue +6 -2
- package/pages/c/_cluster/fleet/index.vue +11 -5
- package/pages/c/_cluster/manager/cloudCredential/index.vue +68 -4
- package/pages/c/_cluster/manager/jwt.authentication/index.vue +10 -4
- package/pages/c/_cluster/settings/performance.vue +2 -2
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +7 -10
- package/pages/c/_cluster/uiplugins/index.vue +28 -18
- package/pages/home.vue +2 -13
- package/plugins/dashboard-store/actions.js +1 -1
- package/plugins/dashboard-store/getters.js +1 -1
- package/plugins/steve/__tests__/getters.test.ts +5 -5
- package/plugins/steve/getters.js +6 -4
- package/plugins/steve/hybrid-class.js +1 -5
- package/scripts/extension/bundle +1 -1
- package/scripts/extension/helm/charts/ui-plugin-server/Chart.yaml +1 -1
- package/scripts/publish-shell.sh +56 -59
- package/scripts/test-plugins-build.sh +45 -39
- package/scripts/typegen.sh +26 -23
- package/store/type-map.js +4 -2
- package/types/shell/index.d.ts +10 -0
- package/types/store/pagination.types.ts +1 -1
- package/utils/cluster.js +9 -0
- package/utils/settings.ts +3 -1
- package/utils/string.js +9 -0
- package/utils/v-sphere.ts +251 -0
- package/creators/app/app.package.json +0 -14
- package/creators/app/files/.eslintignore +0 -16
- package/creators/app/files/.eslintrc.js +0 -173
- package/creators/app/files/.gitignore +0 -70
- package/creators/app/files/.gitlab-ci.yml +0 -14
- package/creators/app/files/.vscode/settings.json +0 -21
- package/creators/app/files/babel.config.js +0 -1
- package/creators/app/files/tsconfig.json +0 -42
- package/creators/app/files/vue.config.js +0 -6
- package/creators/app/init +0 -120
- package/creators/app/package.json +0 -25
- package/creators/pkg/files/.github/workflows/build-extension-catalog.yml +0 -24
- package/creators/pkg/files/.github/workflows/build-extension-charts.yml +0 -22
- package/creators/pkg/files/babel.config.js +0 -1
- package/creators/pkg/files/index.ts +0 -14
- package/creators/pkg/files/tsconfig.json +0 -53
- package/creators/pkg/files/vue.config.js +0 -1
- package/creators/pkg/init +0 -286
- package/creators/pkg/package.json +0 -19
- package/creators/pkg/pkg.package.json +0 -21
- package/creators/pkg/vue-shim.ts +0 -4
- package/creators/update/init +0 -56
- package/creators/update/package.json +0 -20
- 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
|
+
}
|
package/config/router/routes.js
CHANGED
|
@@ -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
|
+
];
|
package/config/uiplugins.js
CHANGED
|
@@ -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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
//
|
|
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
|
|
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 (
|
|
159
|
+
if (requiredUiExtensionsVersion && !semver.satisfies(parsedUiExtensionsApiVersion, requiredUiExtensionsVersion)) {
|
|
109
160
|
return 'plugins.error.api';
|
|
110
161
|
}
|
|
111
162
|
|
|
112
163
|
// Host application
|
|
113
|
-
const requiredHost =
|
|
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 (
|
|
121
|
-
const requiredRancherVersion =
|
|
171
|
+
if (parsedRancherVersion) {
|
|
172
|
+
const requiredRancherVersion = UIPluginResource.metadata?.[UI_PLUGIN_METADATA.RANCHER_VERSION];
|
|
122
173
|
|
|
123
|
-
if (requiredRancherVersion && !semver.satisfies(
|
|
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 ===
|
|
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 (
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
//
|
|
147
|
-
const
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
207
|
+
// reset compatibility property
|
|
208
|
+
versionObj.isVersionCompatible = true;
|
|
209
|
+
versionObj.versionIncompatibilityData = {};
|
|
152
210
|
|
|
153
|
-
//
|
|
154
|
-
|
|
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
|
-
|
|
157
|
-
|
|
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
|
|
161
|
-
|
|
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 (
|
|
165
|
-
|
|
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
|
-
//
|
|
170
|
-
|
|
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
|
-
|
|
248
|
+
if (requiredHost && requiredHost !== UI_PLUGIN_HOST_APP) {
|
|
249
|
+
if (!returnObj) {
|
|
174
250
|
return false;
|
|
175
251
|
}
|
|
176
|
-
}
|
|
177
252
|
|
|
178
|
-
|
|
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
|
-
|
|
185
|
-
|
|
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
|
-
|
|
192
|
-
|
|
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
|
-
|
|
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
|
-
|
|
203
|
-
|
|
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
|
|
208
|
-
if (
|
|
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
|
-
|
|
216
|
-
|
|
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
|
-
|
|
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 '
|
|
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
|
-
|
|
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
|
-
|
|
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?.
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
{
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
{
|
|
39
|
-
|
|
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
|
});
|