Skip to content

Commit

Permalink
apply some fixes for extensions logic in light of Vue3 migration
Browse files Browse the repository at this point in the history
  • Loading branch information
aalves08 committed Sep 30, 2024
1 parent d3e3fd3 commit c396d5b
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 132 deletions.
7 changes: 4 additions & 3 deletions shell/assets/translations/en-us.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4285,9 +4285,10 @@ plugins:
title: Error loading extension
message: Could not load extension code
generic: Extension error
api: This Extension is not compatible with the current Extensions API version
host: This Extension is not compatible with this application
version: This Extension is not compatible with this version of Rancher
api: Loading error! The version installed is not compatible with the current Extensions API version
host: Loading error! The version installed is not compatible with this application host
version: Loading error! The version installed is not compatible with the current version of Rancher
kubeVersion: Loading error! The version installed is not compatible with the current Rancher kubernetes version
load: An error occurred loading the code for this Extension
developerPkg: This Extension has been loaded internally, so we won't load the external version
success:
Expand Down
16 changes: 13 additions & 3 deletions shell/config/uiplugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,18 +147,19 @@ function parseRancherVersion(v) {
* Whether an extension should be loaded based on the metadata returned by the backend in the UIPlugins resource instance
* @returns String || Boolean
*/
export function shouldNotLoadPlugin(UIPluginResource, rancherVersion, loadedPlugins) {
export function shouldNotLoadPlugin(UIPluginResource, { rancherVersion, kubeVersion }, loadedPlugins) {
if (!UIPluginResource.name || !UIPluginResource.version || !UIPluginResource.endpoint) {
return 'plugins.error.generic';
}

// Extension chart specified a required extension API version
// we are propagating the annotations in pkg/package.json for any extension
// inside the "spec.plugin.metadata" property of UIPlugin resource
const requiredUiExtensionsVersion = UIPluginResource.spec?.plugin?.metadata?.[UI_PLUGIN_CHART_ANNOTATIONS.EXTENSIONS_VERSION];
const requiredUiExtensionsVersion = UIPluginResource.metadata?.[UI_PLUGIN_CHART_ANNOTATIONS.EXTENSIONS_VERSION] || '>= 3.0.0';
// semver.coerce will get rid of any suffix on the version numbering (-rc, -head, etc)
const parsedUiExtensionsApiVersion = semver.coerce(UI_EXTENSIONS_API_VERSION)?.version || UI_EXTENSIONS_API_VERSION;
const parsedRancherVersion = rancherVersion ? parseRancherVersion(rancherVersion) : '';
const parsedKubeVersion = kubeVersion ? semver.coerce(kubeVersion)?.version : '';

if (requiredUiExtensionsVersion && !semver.satisfies(parsedUiExtensionsApiVersion, requiredUiExtensionsVersion)) {
return 'plugins.error.api';
Expand All @@ -171,6 +172,15 @@ export function shouldNotLoadPlugin(UIPluginResource, rancherVersion, loadedPlug
return 'plugins.error.host';
}

// Kube version
if (parsedKubeVersion) {
const requiredKubeVersion = UIPluginResource.metadata?.[UI_PLUGIN_CHART_ANNOTATIONS.KUBE_VERSION];

if (requiredKubeVersion && !semver.satisfies(parsedRancherVersion, requiredKubeVersion)) {
return 'plugins.error.kubeVersion';
}
}

// Rancher version
if (parsedRancherVersion) {
const requiredRancherVersion = UIPluginResource.metadata?.[UI_PLUGIN_METADATA.RANCHER_VERSION];
Expand Down Expand Up @@ -262,7 +272,7 @@ export function isSupportedChartVersion(versionData, returnObj = false) {
}

// check "catalog.cattle.io/ui-extensions-version" annotation
const requiredUiExtensionsApiVersion = version.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.EXTENSIONS_VERSION];
const requiredUiExtensionsApiVersion = version.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.EXTENSIONS_VERSION] || '>= 3.0.0';
const parsedUiExtensionsApiVersion = semver.coerce(UI_EXTENSIONS_API_VERSION)?.version || UI_EXTENSIONS_API_VERSION;

if (requiredUiExtensionsApiVersion && parsedUiExtensionsApiVersion && !semver.satisfies(parsedUiExtensionsApiVersion, requiredUiExtensionsApiVersion)) {
Expand Down
10 changes: 10 additions & 0 deletions shell/config/version.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Store version data retrieved from the backend /rancherversion API
*/
let _versionData = { RancherPrime: 'false' };
let _kubeVersionData = {};

export function isRancherPrime() {
return _versionData.RancherPrime?.toLowerCase() === 'true';
Expand All @@ -15,3 +16,12 @@ export function setVersionData(v) {
// Remove any properties on 'v' we don't want
_versionData = JSON.parse(JSON.stringify(v));
}

export function getKubeVersionData() {
return _kubeVersionData;
}

export function setKubeVersionData(v) {
// Remove any properties on 'v' we don't want
_kubeVersionData = JSON.parse(JSON.stringify(v));
}
3 changes: 1 addition & 2 deletions shell/initialize/install-plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import plugins from '@shell/core/plugins.js';
import pluginsLoader from '@shell/core/plugins-loader.js';
import replaceAll from '@shell/plugins/replaceall';
import steveCreateWorker from '@shell/plugins/steve-create-worker';
import version from '@shell/plugins/version';
import emberCookie from '@shell/plugins/ember-cookie';
import ShortKey from '@shell/plugins/shortkey';

Expand All @@ -43,7 +42,7 @@ export async function installPlugins(vueApp) {
}

export async function installInjectedPlugins(app, vueApp) {
const pluginDefinitions = [config, cookieUniversal, axios, plugins, pluginsLoader, axiosShell, intNumber, codeMirror, nuxtClientInit, replaceAll, backButton, plugin, version, steveCreateWorker, emberCookie];
const pluginDefinitions = [config, cookieUniversal, axios, plugins, pluginsLoader, axiosShell, intNumber, codeMirror, nuxtClientInit, replaceAll, backButton, plugin, steveCreateWorker, emberCookie];

const installations = pluginDefinitions.map(async(pluginDefinition) => {
if (typeof pluginDefinition === 'function') {
Expand Down
6 changes: 0 additions & 6 deletions shell/pages/c/_cluster/uiplugins/PluginInfoPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -181,12 +181,6 @@ export default {
</div>
</div>
<div>
<Banner
v-if="info.error"
color="error"
:label="info.error"
class="mt-10"
/>
<Banner
v-if="info.builtin"
color="warning"
Expand Down
131 changes: 61 additions & 70 deletions shell/pages/c/_cluster/uiplugins/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -379,9 +379,9 @@ export default {
const error = this.uiErrors[e];
if (error && typeof error === 'string') {
chart.error = this.t(this.uiErrors[e]);
chart.installedError = this.t(this.uiErrors[e]);
} else {
chart.error = false;
chart.installedError = '';
}
}
});
Expand All @@ -400,21 +400,6 @@ export default {
if (plugin.description && plugin.description.length > MAX_DESCRIPTION_LENGTH) {
plugin.description = `${ plugin.description.substr(0, MAX_DESCRIPTION_LENGTH) } ...`;
}
// check if kube version compatibility is met for installed extension
if (plugin.uiplugin) {
const versionInstalled = plugin.uiplugin.spec?.plugin?.version;
const versionInstalledData = plugin.versions.find((v) => v.version === versionInstalled);
if (versionInstalledData) {
const kubeVersionToCheck = versionInstalledData.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.KUBE_VERSION];
const versionSupportedData = isSupportedChartVersion({ version: versionInstalledData, kubeVersion: this.kubeVersion });
if (this.kubeVersion && !versionSupportedData.isVersionCompatible && versionSupportedData.versionIncompatibilityData?.type === EXTENSIONS_INCOMPATIBILITY_TYPES.KUBE) {
plugin.installedError = this.t('plugins.currentInstalledVersionBlockedByKubeVersion', { kubeVersion: this.kubeVersion, kubeVersionToCheck }, true);
}
}
}
});
// Sort by name
Expand All @@ -423,66 +408,72 @@ export default {
},
watch: {
helmOps(neu) {
helmOps: {
handler(neu) {
// Get Helm operations for UI plugins and order by date
let pluginOps = neu.filter((op) => {
return op.namespace === UI_PLUGIN_NAMESPACE;
});
let pluginOps = neu.filter((op) => {
return op.namespace === UI_PLUGIN_NAMESPACE;
});
pluginOps = sortBy(pluginOps, 'metadata.creationTimestamp', true);
pluginOps = sortBy(pluginOps, 'metadata.creationTimestamp', true);
// Go through the installed plugins
(this.available || []).forEach((plugin) => {
const op = pluginOps.find((o) => o.status?.releaseName === plugin.name);
// Go through the installed plugins
(this.available || []).forEach((plugin) => {
const op = pluginOps.find((o) => o.status?.releaseName === plugin.name);
if (op) {
const active = op.metadata.state?.transitioning;
const error = op.metadata.state?.error;
if (op) {
const active = op.metadata.state?.transitioning;
const error = op.metadata.state?.error;
this.errors[plugin.name] = error;
this.errors[plugin.name] = error;
if (active) {
if (active) {
// Can use the status directly, apart from upgrade, which maps to install
const status = op.status.action === 'upgrade' ? 'install' : op.status.action;
const status = op.status.action === 'upgrade' ? 'install' : op.status.action;
this.updatePluginInstallStatus(plugin.name, status);
} else if (op.status.action === 'uninstall') {
this.updatePluginInstallStatus(plugin.name, status);
} else if (op.status.action === 'uninstall') {
// Uninstall has finished
this.updatePluginInstallStatus(plugin.name, false);
} else if (error) {
this.updatePluginInstallStatus(plugin.name, false);
} else if (error) {
this.updatePluginInstallStatus(plugin.name, false);
}
} else {
this.updatePluginInstallStatus(plugin.name, false);
}
} else {
this.updatePluginInstallStatus(plugin.name, false);
}
});
});
},
deep: true
},
plugins(neu, old) {
const installed = this.$store.getters['uiplugins/plugins'];
const shouldHaveLoaded = (installed || []).filter((p) => !this.uiErrors[p.name] && !p.builtin);
let changes = 0;
plugins: {
handler(neu) {
const installed = this.$store.getters['uiplugins/plugins'];
const shouldHaveLoaded = (installed || []).filter((p) => !this.uiErrors[p.name] && !p.builtin);
let changes = 0;
// Did the user remove an extension
if (neu?.length < shouldHaveLoaded.length) {
changes++;
}
// Did the user remove an extension
if (neu?.length < shouldHaveLoaded.length) {
changes++;
}
neu.forEach((plugin) => {
const existing = installed.find((p) => !p.removed && p.name === plugin.name && p.version === plugin.version);
neu.forEach((plugin) => {
const existing = installed.find((p) => !p.removed && p.name === plugin.name && p.version === plugin.version);
if (!existing && plugin.isInitialized) {
if (!this.uiErrors[plugin.name]) {
changes++;
if (!existing && plugin.isInitialized) {
if (!this.uiErrors[plugin.name]) {
changes++;
}
this.updatePluginInstallStatus(plugin.name, false);
}
});
this.updatePluginInstallStatus(plugin.name, false);
if (changes > 0) {
this['reloadRequired'] = true;
}
});
if (changes > 0) {
this['reloadRequired'] = true;
}
},
deep: true
}
},
Expand Down Expand Up @@ -852,9 +843,9 @@ export default {
> -> {{ plugin.upgrade }}</span>
<p
v-if="plugin.installedError"
class="incompatible"
class="install-error"
>
<i class="icon icon-warning icon-lg text-warning" />
<i class="icon icon-warning icon-lg" />
<span>{{ plugin.installedError }}</span>
</p>
<p
Expand Down Expand Up @@ -892,14 +883,6 @@ export default {
<div class="plugin-spacer" />
<!-- plugin badges -->
<div class="plugin-actions">
<template v-if="plugin.error">
<div
v-clean-tooltip="plugin.error"
class="plugin-error"
>
<i class="icon icon-warning" />
</div>
</template>
<!-- plugin status -->
<div
v-if="plugin.helmError"
Expand Down Expand Up @@ -1185,9 +1168,17 @@ export default {
width: 16px;
}
.incompatible {
margin: 10px 0;
.install-error {
margin: 10px 10px 5px 0;
font-weight: bold;
$error-icon-size: 22px;
> i {
color: var(--error);
height: $error-icon-size;
font-size: $error-icon-size;
width: $error-icon-size;
}
}
}
Expand Down
Loading

0 comments on commit c396d5b

Please sign in to comment.