Plugins: Ensure only updateable plugins can be updated via the UI (#93205)

* only updateable plugins can be updated via the UI

* add missing state

* Apply levi brains

Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com>

* apply same style throughout

---------

Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com>
This commit is contained in:
Will Browne 2024-09-12 13:51:50 +01:00 committed by GitHub
parent 3d9ae801cb
commit 3f8d127410
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 48 additions and 22 deletions

View File

@ -1,6 +1,6 @@
import { PluginType } from '@grafana/data';
import { PluginSignatureBadge, Stack } from '@grafana/ui';
import { isPluginUpdateable } from '../helpers';
import { CatalogPlugin } from '../types';
import {
@ -18,15 +18,13 @@ type PluginBadgeType = {
export function PluginListItemBadges({ plugin }: PluginBadgeType) {
// Currently renderer plugins are not supported by the catalog due to complications related to installation / update / uninstall.
const hasUpdate = plugin.hasUpdate && !plugin.isCore && plugin.type !== PluginType.renderer;
const canUpdate = isPluginUpdateable(plugin);
if (plugin.isEnterprise) {
return (
<Stack height="auto" wrap="wrap">
<PluginEnterpriseBadge plugin={plugin} />
{plugin.isDisabled && <PluginDisabledBadge error={plugin.error} />}
{hasUpdate && !plugin.isManaged && !plugin.isPreinstalled.withVersion && (
<PluginUpdateAvailableBadge plugin={plugin} />
)}
{canUpdate && <PluginUpdateAvailableBadge plugin={plugin} />}
{plugin.angularDetected && <PluginAngularBadge />}
</Stack>
);
@ -38,9 +36,7 @@ export function PluginListItemBadges({ plugin }: PluginBadgeType) {
{plugin.isDisabled && <PluginDisabledBadge error={plugin.error} />}
{plugin.isDeprecated && <PluginDeprecatedBadge />}
{plugin.isInstalled && <PluginInstalledBadge />}
{hasUpdate && !plugin.isManaged && !plugin.isPreinstalled.withVersion && (
<PluginUpdateAvailableBadge plugin={plugin} />
)}
{canUpdate && <PluginUpdateAvailableBadge plugin={plugin} />}
{plugin.angularDetected && <PluginAngularBadge />}
</Stack>
);

View File

@ -425,3 +425,42 @@ export function filterByKeyword(plugins: CatalogPlugin[], query: string) {
}
return idxs.map((id) => getId(dataArray[id]));
}
export function isPluginUpdateable(plugin: CatalogPlugin) {
// If there is no update available, the plugin cannot be updated
if (!plugin.hasUpdate) {
return false;
}
// Provisioned plugins cannot be updated
if (plugin.isProvisioned) {
return false;
}
// Core plugins cannot be updated
if (plugin.isCore) {
return false;
}
// Currently renderer plugins are not supported by the catalog due to complications related to installation / update / uninstall.
if (plugin.type === PluginType.renderer) {
return false;
}
// Preinstalled plugins (with specified version) cannot be updated
if (plugin.isPreinstalled.withVersion) {
return false;
}
// If the plugin is currently being updated, it should not be updated
if (plugin.isUpdatingFromInstance) {
return false;
}
// Managed plugins cannot be updated
if (plugin.isManaged) {
return false;
}
return true;
}

View File

@ -3,7 +3,7 @@ import { useEffect, useMemo } from 'react';
import { PluginError, PluginType } from '@grafana/data';
import { useDispatch, useSelector } from 'app/types';
import { sortPlugins, Sorters } from '../helpers';
import { sortPlugins, Sorters, isPluginUpdateable } from '../helpers';
import { CatalogPlugin } from '../types';
import { fetchAll, fetchDetails, fetchRemotePlugins, install, uninstall, fetchAllLocal, unsetInstall } from './actions';
@ -36,7 +36,8 @@ export const useGetAll = (filters: PluginFilters, sortBy: Sorters = Sorters.name
export const useGetUpdatable = () => {
const { isLoading } = useFetchStatus();
const updatablePlugins = useSelector(selectPlugins({ isInstalled: true, hasUpdate: true }));
const { plugins: installed } = useGetAll({ isInstalled: true });
const updatablePlugins = installed.filter(isPluginUpdateable);
return {
isLoading,
updatablePlugins,

View File

@ -3,7 +3,7 @@ import { createSelector } from '@reduxjs/toolkit';
import { PluginError, PluginType, unEscapeStringFromRegex } from '@grafana/data';
import { reportInteraction } from '@grafana/runtime';
import { filterByKeyword } from '../helpers';
import { filterByKeyword, isPluginUpdateable } from '../helpers';
import { RequestStatus, PluginCatalogStoreState } from '../types';
import { pluginsAdapter } from './reducer';
@ -61,17 +61,7 @@ export const selectPlugins = (filters: PluginFilters) =>
return false;
}
if (filters.hasUpdate !== undefined && plugin.hasUpdate !== filters.hasUpdate) {
return false;
}
// plugins not controlled by the user should not be shown as updatable
if (
filters.hasUpdate !== undefined &&
filters.hasUpdate &&
plugin.hasUpdate &&
(plugin.isCore || plugin.isManaged || plugin.isProvisioned || plugin.isUpdatingFromInstance)
) {
if (filters.hasUpdate !== undefined && (plugin.hasUpdate !== filters.hasUpdate || !isPluginUpdateable(plugin))) {
return false;
}