Plugins: Remove managedPluginsInstall feature toggle (#100416)
* Plugins: Remove managedPluginsInstall feature toggle Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
This commit is contained in:
co-authored by
Will Browne
parent
39b424b8a7
commit
29afe7d2cc
@@ -49,7 +49,6 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general-
|
|||||||
| `kubernetesPlaylists` | Use the kubernetes API in the frontend for playlists, and route /api/playlist requests to k8s | Yes |
|
| `kubernetesPlaylists` | Use the kubernetes API in the frontend for playlists, and route /api/playlist requests to k8s | Yes |
|
||||||
| `recoveryThreshold` | Enables feature recovery threshold (aka hysteresis) for threshold server-side expression | Yes |
|
| `recoveryThreshold` | Enables feature recovery threshold (aka hysteresis) for threshold server-side expression | Yes |
|
||||||
| `lokiStructuredMetadata` | Enables the loki data source to request structured metadata from the Loki server | Yes |
|
| `lokiStructuredMetadata` | Enables the loki data source to request structured metadata from the Loki server | Yes |
|
||||||
| `managedPluginsInstall` | Install managed plugins directly from plugins catalog | Yes |
|
|
||||||
| `addFieldFromCalculationStatFunctions` | Add cumulative and window functions to the add field from calculation transformation | Yes |
|
| `addFieldFromCalculationStatFunctions` | Add cumulative and window functions to the add field from calculation transformation | Yes |
|
||||||
| `annotationPermissionUpdate` | Change the way annotation permissions work by scoping them to folders and dashboards. | Yes |
|
| `annotationPermissionUpdate` | Change the way annotation permissions work by scoping them to folders and dashboards. | Yes |
|
||||||
| `dashboardSceneForViewers` | Enables dashboard rendering using Scenes for viewer roles | Yes |
|
| `dashboardSceneForViewers` | Enables dashboard rendering using Scenes for viewer roles | Yes |
|
||||||
|
|||||||
@@ -120,7 +120,6 @@ export interface FeatureToggles {
|
|||||||
lokiStructuredMetadata?: boolean;
|
lokiStructuredMetadata?: boolean;
|
||||||
teamHttpHeaders?: boolean;
|
teamHttpHeaders?: boolean;
|
||||||
cachingOptimizeSerializationMemoryUsage?: boolean;
|
cachingOptimizeSerializationMemoryUsage?: boolean;
|
||||||
managedPluginsInstall?: boolean;
|
|
||||||
prometheusPromQAIL?: boolean;
|
prometheusPromQAIL?: boolean;
|
||||||
prometheusCodeModeMetricNamesSearch?: boolean;
|
prometheusCodeModeMetricNamesSearch?: boolean;
|
||||||
addFieldFromCalculationStatFunctions?: boolean;
|
addFieldFromCalculationStatFunctions?: boolean;
|
||||||
|
|||||||
+1
-1
@@ -424,7 +424,7 @@ func (hs *HTTPServer) registerRoutes() {
|
|||||||
apiRoute.Any("/plugin-proxy/:pluginId/*", requestmeta.SetSLOGroup(requestmeta.SLOGroupHighSlow), authorize(ac.EvalPermission(pluginaccesscontrol.ActionAppAccess, pluginIDScope)), checkAppEnabled(hs.pluginStore, hs.PluginSettings), hs.ProxyPluginRequest)
|
apiRoute.Any("/plugin-proxy/:pluginId/*", requestmeta.SetSLOGroup(requestmeta.SLOGroupHighSlow), authorize(ac.EvalPermission(pluginaccesscontrol.ActionAppAccess, pluginIDScope)), checkAppEnabled(hs.pluginStore, hs.PluginSettings), hs.ProxyPluginRequest)
|
||||||
apiRoute.Any("/plugin-proxy/:pluginId", requestmeta.SetSLOGroup(requestmeta.SLOGroupHighSlow), authorize(ac.EvalPermission(pluginaccesscontrol.ActionAppAccess, pluginIDScope)), checkAppEnabled(hs.pluginStore, hs.PluginSettings), hs.ProxyPluginRequest)
|
apiRoute.Any("/plugin-proxy/:pluginId", requestmeta.SetSLOGroup(requestmeta.SLOGroupHighSlow), authorize(ac.EvalPermission(pluginaccesscontrol.ActionAppAccess, pluginIDScope)), checkAppEnabled(hs.pluginStore, hs.PluginSettings), hs.ProxyPluginRequest)
|
||||||
|
|
||||||
if hs.Cfg.PluginAdminEnabled && (hs.Features.IsEnabledGlobally(featuremgmt.FlagManagedPluginsInstall) || !hs.Cfg.PluginAdminExternalManageEnabled) {
|
if hs.Cfg.PluginAdminEnabled {
|
||||||
apiRoute.Group("/plugins", func(pluginRoute routing.RouteRegister) {
|
apiRoute.Group("/plugins", func(pluginRoute routing.RouteRegister) {
|
||||||
pluginRoute.Post("/:pluginId/install", authorizeInOrg(ac.UseGlobalOrSingleOrg(hs.Cfg), ac.EvalPermission(pluginaccesscontrol.ActionInstall)), routing.Wrap(hs.InstallPlugin))
|
pluginRoute.Post("/:pluginId/install", authorizeInOrg(ac.UseGlobalOrSingleOrg(hs.Cfg), ac.EvalPermission(pluginaccesscontrol.ActionInstall)), routing.Wrap(hs.InstallPlugin))
|
||||||
pluginRoute.Post("/:pluginId/uninstall", authorizeInOrg(ac.UseGlobalOrSingleOrg(hs.Cfg), ac.EvalPermission(pluginaccesscontrol.ActionInstall)), routing.Wrap(hs.UninstallPlugin))
|
pluginRoute.Post("/:pluginId/uninstall", authorizeInOrg(ac.UseGlobalOrSingleOrg(hs.Cfg), ac.EvalPermission(pluginaccesscontrol.ActionInstall)), routing.Wrap(hs.UninstallPlugin))
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ func Test_PluginsInstallAndUninstall(t *testing.T) {
|
|||||||
preInstalledPlugin bool
|
preInstalledPlugin bool
|
||||||
}
|
}
|
||||||
tcs := []testCase{
|
tcs := []testCase{
|
||||||
{expectedCode: http.StatusNotFound, permissionOrg: globalOrg, permissions: canInstall, pluginAdminEnabled: true, pluginAdminExternalManageEnabled: true},
|
{expectedCode: http.StatusOK, permissionOrg: globalOrg, permissions: canInstall, pluginAdminEnabled: true, pluginAdminExternalManageEnabled: true},
|
||||||
{expectedCode: http.StatusNotFound, permissionOrg: globalOrg, permissions: canInstall, pluginAdminEnabled: false, pluginAdminExternalManageEnabled: true},
|
{expectedCode: http.StatusNotFound, permissionOrg: globalOrg, permissions: canInstall, pluginAdminEnabled: false, pluginAdminExternalManageEnabled: true},
|
||||||
{expectedCode: http.StatusNotFound, permissionOrg: globalOrg, permissions: canInstall, pluginAdminEnabled: false, pluginAdminExternalManageEnabled: false},
|
{expectedCode: http.StatusNotFound, permissionOrg: globalOrg, permissions: canInstall, pluginAdminEnabled: false, pluginAdminExternalManageEnabled: false},
|
||||||
{expectedCode: http.StatusForbidden, permissionOrg: globalOrg, permissions: cannotInstall, pluginAdminEnabled: true, pluginAdminExternalManageEnabled: false},
|
{expectedCode: http.StatusForbidden, permissionOrg: globalOrg, permissions: cannotInstall, pluginAdminEnabled: true, pluginAdminExternalManageEnabled: false},
|
||||||
|
|||||||
@@ -765,14 +765,6 @@ var (
|
|||||||
Owner: grafanaOperatorExperienceSquad,
|
Owner: grafanaOperatorExperienceSquad,
|
||||||
FrontendOnly: false,
|
FrontendOnly: false,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Name: "managedPluginsInstall",
|
|
||||||
Description: "Install managed plugins directly from plugins catalog",
|
|
||||||
Stage: FeatureStageGeneralAvailability,
|
|
||||||
RequiresDevMode: false,
|
|
||||||
Owner: grafanaPluginsPlatformSquad,
|
|
||||||
Expression: "true", // enabled by default
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Name: "prometheusPromQAIL",
|
Name: "prometheusPromQAIL",
|
||||||
Description: "Prometheus and AI/ML to assist users in creating a query",
|
Description: "Prometheus and AI/ML to assist users in creating a query",
|
||||||
|
|||||||
@@ -101,7 +101,6 @@ recoveryThreshold,GA,@grafana/alerting-squad,false,true,false
|
|||||||
lokiStructuredMetadata,GA,@grafana/observability-logs,false,false,false
|
lokiStructuredMetadata,GA,@grafana/observability-logs,false,false,false
|
||||||
teamHttpHeaders,preview,@grafana/identity-access-team,false,false,false
|
teamHttpHeaders,preview,@grafana/identity-access-team,false,false,false
|
||||||
cachingOptimizeSerializationMemoryUsage,experimental,@grafana/grafana-operator-experience-squad,false,false,false
|
cachingOptimizeSerializationMemoryUsage,experimental,@grafana/grafana-operator-experience-squad,false,false,false
|
||||||
managedPluginsInstall,GA,@grafana/plugins-platform-backend,false,false,false
|
|
||||||
prometheusPromQAIL,experimental,@grafana/oss-big-tent,false,false,true
|
prometheusPromQAIL,experimental,@grafana/oss-big-tent,false,false,true
|
||||||
prometheusCodeModeMetricNamesSearch,experimental,@grafana/oss-big-tent,false,false,true
|
prometheusCodeModeMetricNamesSearch,experimental,@grafana/oss-big-tent,false,false,true
|
||||||
addFieldFromCalculationStatFunctions,GA,@grafana/dataviz-squad,false,false,true
|
addFieldFromCalculationStatFunctions,GA,@grafana/dataviz-squad,false,false,true
|
||||||
|
|||||||
|
@@ -415,10 +415,6 @@ const (
|
|||||||
// If enabled, the caching backend gradually serializes query responses for the cache, comparing against the configured `[caching]max_value_mb` value as it goes. This can can help prevent Grafana from running out of memory while attempting to cache very large query responses.
|
// If enabled, the caching backend gradually serializes query responses for the cache, comparing against the configured `[caching]max_value_mb` value as it goes. This can can help prevent Grafana from running out of memory while attempting to cache very large query responses.
|
||||||
FlagCachingOptimizeSerializationMemoryUsage = "cachingOptimizeSerializationMemoryUsage"
|
FlagCachingOptimizeSerializationMemoryUsage = "cachingOptimizeSerializationMemoryUsage"
|
||||||
|
|
||||||
// FlagManagedPluginsInstall
|
|
||||||
// Install managed plugins directly from plugins catalog
|
|
||||||
FlagManagedPluginsInstall = "managedPluginsInstall"
|
|
||||||
|
|
||||||
// FlagPrometheusPromQAIL
|
// FlagPrometheusPromQAIL
|
||||||
// Prometheus and AI/ML to assist users in creating a query
|
// Prometheus and AI/ML to assist users in creating a query
|
||||||
FlagPrometheusPromQAIL = "prometheusPromQAIL"
|
FlagPrometheusPromQAIL = "prometheusPromQAIL"
|
||||||
|
|||||||
@@ -2651,6 +2651,7 @@
|
|||||||
"name": "managedPluginsInstall",
|
"name": "managedPluginsInstall",
|
||||||
"resourceVersion": "1720021873452",
|
"resourceVersion": "1720021873452",
|
||||||
"creationTimestamp": "2023-10-18T13:17:03Z",
|
"creationTimestamp": "2023-10-18T13:17:03Z",
|
||||||
|
"deletionTimestamp": "2025-02-11T11:13:57Z",
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
|
"grafana.app/updatedTimestamp": "2024-07-03 15:51:13.452477 +0000 UTC"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,8 +82,7 @@ func DeclareRBACRoles(service ac.Service, cfg *setting.Cfg, features featuremgmt
|
|||||||
Grants: []string{ac.RoleGrafanaAdmin},
|
Grants: []string{ac.RoleGrafanaAdmin},
|
||||||
}
|
}
|
||||||
|
|
||||||
if !cfg.PluginAdminEnabled ||
|
if !cfg.PluginAdminEnabled {
|
||||||
(cfg.PluginAdminExternalManageEnabled && !features.IsEnabledGlobally(featuremgmt.FlagManagedPluginsInstall)) {
|
|
||||||
PluginsMaintainer.Grants = []string{}
|
PluginsMaintainer.Grants = []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-5
@@ -37,10 +37,8 @@ const plugin: CatalogPlugin = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
describe('GetStartedWithDataSource', () => {
|
describe('GetStartedWithDataSource', () => {
|
||||||
const oldFeatureTogglesManagedPluginsInstall = config.featureToggles.managedPluginsInstall;
|
|
||||||
const oldPluginAdminExternalManageEnabled = config.pluginAdminExternalManageEnabled;
|
const oldPluginAdminExternalManageEnabled = config.pluginAdminExternalManageEnabled;
|
||||||
|
|
||||||
config.featureToggles.managedPluginsInstall = true;
|
|
||||||
config.pluginAdminExternalManageEnabled = true;
|
config.pluginAdminExternalManageEnabled = true;
|
||||||
|
|
||||||
const contextSrv = new ContextSrv();
|
const contextSrv = new ContextSrv();
|
||||||
@@ -51,11 +49,10 @@ describe('GetStartedWithDataSource', () => {
|
|||||||
setContextSrv(contextSrv);
|
setContextSrv(contextSrv);
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
config.featureToggles.managedPluginsInstall = oldFeatureTogglesManagedPluginsInstall;
|
|
||||||
config.pluginAdminExternalManageEnabled = oldPluginAdminExternalManageEnabled;
|
config.pluginAdminExternalManageEnabled = oldPluginAdminExternalManageEnabled;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should disable button when managedPluginsInstall and pluginAdminExternalManaged are enabled, but plugin.isFullyInstalled is false', () => {
|
it('should disable button when pluginAdminExternalManaged is enabled, but plugin.isFullyInstalled is false', () => {
|
||||||
render(
|
render(
|
||||||
<TestProvider>
|
<TestProvider>
|
||||||
<GetStartedWithDataSource plugin={{ ...plugin, isFullyInstalled: false }} />
|
<GetStartedWithDataSource plugin={{ ...plugin, isFullyInstalled: false }} />
|
||||||
@@ -67,7 +64,7 @@ describe('GetStartedWithDataSource', () => {
|
|||||||
expect(el).toBeDisabled();
|
expect(el).toBeDisabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should disable button when managedPluginsInstall and pluginAdminExternalManaged are enabled, but plugin.isFullyInstalled is true', () => {
|
it('should disable button when pluginAdminExternalManaged enabled, but plugin.isFullyInstalled is true', () => {
|
||||||
render(
|
render(
|
||||||
<TestProvider>
|
<TestProvider>
|
||||||
<GetStartedWithDataSource plugin={{ ...plugin, isFullyInstalled: true }} />
|
<GetStartedWithDataSource plugin={{ ...plugin, isFullyInstalled: true }} />
|
||||||
|
|||||||
+1
-2
@@ -30,8 +30,7 @@ export function GetStartedWithDataSource({ plugin }: Props): React.ReactElement
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const disabledButton =
|
const disabledButton = config.pluginAdminExternalManageEnabled && !plugin.isFullyInstalled;
|
||||||
config.featureToggles.managedPluginsInstall && config.pluginAdminExternalManageEnabled && !plugin.isFullyInstalled;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
+1
-4
@@ -13,10 +13,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function GetStartedWithPlugin({ plugin }: Props): ReactElement | null {
|
export function GetStartedWithPlugin({ plugin }: Props): ReactElement | null {
|
||||||
const isInstalled =
|
const isInstalled = config.pluginAdminExternalManageEnabled ? plugin.isFullyInstalled : plugin.isInstalled;
|
||||||
config.featureToggles.managedPluginsInstall && config.pluginAdminExternalManageEnabled
|
|
||||||
? plugin.isFullyInstalled
|
|
||||||
: plugin.isInstalled;
|
|
||||||
|
|
||||||
if (!isInstalled || plugin.isDisabled) {
|
if (!isInstalled || plugin.isDisabled) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
-47
@@ -1,47 +0,0 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
|
||||||
|
|
||||||
import { config } from '@grafana/runtime';
|
|
||||||
|
|
||||||
import { PluginStatus } from '../../types';
|
|
||||||
|
|
||||||
import { ExternallyManagedButton } from './ExternallyManagedButton';
|
|
||||||
|
|
||||||
function setup(opts: { angularSupportEnabled: boolean; angularDetected: boolean }) {
|
|
||||||
config.angularSupportEnabled = opts.angularSupportEnabled;
|
|
||||||
render(
|
|
||||||
<ExternallyManagedButton
|
|
||||||
pluginId={'some-plugin-id'}
|
|
||||||
angularDetected={opts.angularDetected}
|
|
||||||
pluginStatus={PluginStatus.INSTALL}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('ExternallyManagedButton', () => {
|
|
||||||
let oldAngularSupportEnabled = config.angularSupportEnabled;
|
|
||||||
afterAll(() => {
|
|
||||||
config.angularSupportEnabled = oldAngularSupportEnabled;
|
|
||||||
});
|
|
||||||
|
|
||||||
describe.each([{ angularSupportEnabled: true }, { angularSupportEnabled: false }])(
|
|
||||||
'angular support is $angularSupportEnabled',
|
|
||||||
({ angularSupportEnabled }) => {
|
|
||||||
it.each([
|
|
||||||
{ angularDetected: true, expectEnabled: angularSupportEnabled },
|
|
||||||
{ angularDetected: false, expectEnabled: true },
|
|
||||||
])('angular detected is $angularDetected', ({ angularDetected, expectEnabled }) => {
|
|
||||||
setup({ angularSupportEnabled, angularDetected });
|
|
||||||
|
|
||||||
const el = screen.getByRole('link');
|
|
||||||
expect(el).toHaveTextContent(/install/i);
|
|
||||||
expect(el).toBeVisible();
|
|
||||||
const linkDisabledStyle = 'pointer-events: none';
|
|
||||||
if (expectEnabled) {
|
|
||||||
expect(el).not.toHaveStyle(linkDisabledStyle);
|
|
||||||
} else {
|
|
||||||
expect(el).toHaveStyle(linkDisabledStyle);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
-47
@@ -1,47 +0,0 @@
|
|||||||
import { config } from '@grafana/runtime';
|
|
||||||
import { LinkButton, Stack } from '@grafana/ui';
|
|
||||||
|
|
||||||
import { getExternalManageLink } from '../../helpers';
|
|
||||||
import { PluginStatus } from '../../types';
|
|
||||||
|
|
||||||
type ExternallyManagedButtonProps = {
|
|
||||||
pluginId: string;
|
|
||||||
pluginStatus: PluginStatus;
|
|
||||||
angularDetected?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function ExternallyManagedButton({ pluginId, pluginStatus, angularDetected }: ExternallyManagedButtonProps) {
|
|
||||||
const externalManageLink = `${getExternalManageLink(pluginId)}/?tab=installation`;
|
|
||||||
|
|
||||||
if (pluginStatus === PluginStatus.UPDATE) {
|
|
||||||
return (
|
|
||||||
<Stack height="auto">
|
|
||||||
<LinkButton href={externalManageLink} target="_blank" rel="noopener noreferrer">
|
|
||||||
Update via grafana.com
|
|
||||||
</LinkButton>
|
|
||||||
<LinkButton variant="destructive" href={externalManageLink} target="_blank" rel="noopener noreferrer">
|
|
||||||
Uninstall via grafana.com
|
|
||||||
</LinkButton>
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pluginStatus === PluginStatus.UNINSTALL) {
|
|
||||||
return (
|
|
||||||
<LinkButton variant="destructive" href={externalManageLink} target="_blank" rel="noopener noreferrer">
|
|
||||||
Uninstall via grafana.com
|
|
||||||
</LinkButton>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<LinkButton
|
|
||||||
disabled={!config.angularSupportEnabled && angularDetected}
|
|
||||||
href={externalManageLink}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Install via grafana.com
|
|
||||||
</LinkButton>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
-6
@@ -124,16 +124,13 @@ describe('InstallControlsButton', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('update button on managed instance', () => {
|
describe('update button on managed instance', () => {
|
||||||
const oldFeatureTogglesManagedPluginsInstall = config.featureToggles.managedPluginsInstall;
|
|
||||||
const oldPluginAdminExternalManageEnabled = config.pluginAdminExternalManageEnabled;
|
const oldPluginAdminExternalManageEnabled = config.pluginAdminExternalManageEnabled;
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
config.featureToggles.managedPluginsInstall = true;
|
|
||||||
config.pluginAdminExternalManageEnabled = true;
|
config.pluginAdminExternalManageEnabled = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
config.featureToggles.managedPluginsInstall = oldFeatureTogglesManagedPluginsInstall;
|
|
||||||
config.pluginAdminExternalManageEnabled = oldPluginAdminExternalManageEnabled;
|
config.pluginAdminExternalManageEnabled = oldPluginAdminExternalManageEnabled;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -199,16 +196,13 @@ describe('InstallControlsButton', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('uninstall button on managed instance', () => {
|
describe('uninstall button on managed instance', () => {
|
||||||
const oldFeatureTogglesManagedPluginsInstall = config.featureToggles.managedPluginsInstall;
|
|
||||||
const oldPluginAdminExternalManageEnabled = config.pluginAdminExternalManageEnabled;
|
const oldPluginAdminExternalManageEnabled = config.pluginAdminExternalManageEnabled;
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
config.featureToggles.managedPluginsInstall = true;
|
|
||||||
config.pluginAdminExternalManageEnabled = true;
|
config.pluginAdminExternalManageEnabled = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
config.featureToggles.managedPluginsInstall = oldFeatureTogglesManagedPluginsInstall;
|
|
||||||
config.pluginAdminExternalManageEnabled = oldPluginAdminExternalManageEnabled;
|
config.pluginAdminExternalManageEnabled = oldPluginAdminExternalManageEnabled;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+4
-8
@@ -5,7 +5,6 @@ import { AppEvents } from '@grafana/data';
|
|||||||
import { config, locationService, reportInteraction } from '@grafana/runtime';
|
import { config, locationService, reportInteraction } from '@grafana/runtime';
|
||||||
import { Button, ConfirmModal, Stack } from '@grafana/ui';
|
import { Button, ConfirmModal, Stack } from '@grafana/ui';
|
||||||
import appEvents from 'app/core/app_events';
|
import appEvents from 'app/core/app_events';
|
||||||
import configCore from 'app/core/config';
|
|
||||||
import { useQueryParams } from 'app/core/hooks/useQueryParams';
|
import { useQueryParams } from 'app/core/hooks/useQueryParams';
|
||||||
import { removePluginFromNavTree } from 'app/core/reducers/navBarTree';
|
import { removePluginFromNavTree } from 'app/core/reducers/navBarTree';
|
||||||
import { useDispatch } from 'app/types';
|
import { useDispatch } from 'app/types';
|
||||||
@@ -72,7 +71,7 @@ export function InstallControlsButton({
|
|||||||
const result = await install(plugin.id, latestCompatibleVersion?.version);
|
const result = await install(plugin.id, latestCompatibleVersion?.version);
|
||||||
if (!errorInstalling && !('error' in result)) {
|
if (!errorInstalling && !('error' in result)) {
|
||||||
let successMessage = `Installed ${plugin.name}`;
|
let successMessage = `Installed ${plugin.name}`;
|
||||||
if (config.pluginAdminExternalManageEnabled && configCore.featureToggles.managedPluginsInstall) {
|
if (config.pluginAdminExternalManageEnabled) {
|
||||||
successMessage = 'Install requested, this may take a few minutes.';
|
successMessage = 'Install requested, this may take a few minutes.';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +97,7 @@ export function InstallControlsButton({
|
|||||||
}
|
}
|
||||||
|
|
||||||
let successMessage = `Uninstalled ${plugin.name}`;
|
let successMessage = `Uninstalled ${plugin.name}`;
|
||||||
if (config.pluginAdminExternalManageEnabled && configCore.featureToggles.managedPluginsInstall) {
|
if (config.pluginAdminExternalManageEnabled) {
|
||||||
successMessage = 'Uninstall requested, this may take a few minutes.';
|
successMessage = 'Uninstall requested, this may take a few minutes.';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,10 +153,7 @@ export function InstallControlsButton({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (pluginStatus === PluginStatus.UPDATE) {
|
if (pluginStatus === PluginStatus.UPDATE) {
|
||||||
const disableUpdate =
|
const disableUpdate = config.pluginAdminExternalManageEnabled ? plugin.isUpdatingFromInstance : isInstalling;
|
||||||
config.pluginAdminExternalManageEnabled && configCore.featureToggles.managedPluginsInstall
|
|
||||||
? plugin.isUpdatingFromInstance
|
|
||||||
: isInstalling;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack alignItems="flex-start" width="auto" height="auto">
|
<Stack alignItems="flex-start" width="auto" height="auto">
|
||||||
@@ -181,7 +177,7 @@ export function InstallControlsButton({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function shouldDisableUninstall(isUninstalling: boolean, plugin: CatalogPlugin) {
|
function shouldDisableUninstall(isUninstalling: boolean, plugin: CatalogPlugin) {
|
||||||
if (config.pluginAdminExternalManageEnabled && config.featureToggles.managedPluginsInstall) {
|
if (config.pluginAdminExternalManageEnabled) {
|
||||||
return plugin.isUninstallingFromInstance || !plugin.isFullyInstalled || plugin.isUpdatingFromInstance;
|
return plugin.isUninstallingFromInstance || !plugin.isFullyInstalled || plugin.isUpdatingFromInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,10 @@ import { css } from '@emotion/css';
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { config } from '@grafana/runtime';
|
|
||||||
import { Icon, Stack, useStyles2 } from '@grafana/ui';
|
import { Icon, Stack, useStyles2 } from '@grafana/ui';
|
||||||
import configCore from 'app/core/config';
|
|
||||||
|
|
||||||
import { GetStartedWithPlugin } from '../components/GetStartedWithPlugin';
|
import { GetStartedWithPlugin } from '../components/GetStartedWithPlugin';
|
||||||
import { InstallControlsButton } from '../components/InstallControls';
|
import { InstallControlsButton } from '../components/InstallControls';
|
||||||
import { ExternallyManagedButton } from '../components/InstallControls/ExternallyManagedButton';
|
|
||||||
import { getLatestCompatibleVersion, hasInstallControlWarning, isInstallControlsEnabled } from '../helpers';
|
import { getLatestCompatibleVersion, hasInstallControlWarning, isInstallControlsEnabled } from '../helpers';
|
||||||
import { useIsRemotePluginsAvailable } from '../state/hooks';
|
import { useIsRemotePluginsAvailable } from '../state/hooks';
|
||||||
import { CatalogPlugin, PluginStatus } from '../types';
|
import { CatalogPlugin, PluginStatus } from '../types';
|
||||||
@@ -28,7 +25,6 @@ export const PluginActions = ({ plugin }: Props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hasInstallWarning = hasInstallControlWarning(plugin, isRemotePluginsAvailable, latestCompatibleVersion);
|
const hasInstallWarning = hasInstallControlWarning(plugin, isRemotePluginsAvailable, latestCompatibleVersion);
|
||||||
const isExternallyManaged = config.pluginAdminExternalManageEnabled;
|
|
||||||
const pluginStatus = plugin.isInstalled
|
const pluginStatus = plugin.isInstalled
|
||||||
? plugin.hasUpdate
|
? plugin.hasUpdate
|
||||||
? PluginStatus.UPDATE
|
? PluginStatus.UPDATE
|
||||||
@@ -41,23 +37,13 @@ export const PluginActions = ({ plugin }: Props) => {
|
|||||||
<Stack direction="column">
|
<Stack direction="column">
|
||||||
<Stack alignItems="center">
|
<Stack alignItems="center">
|
||||||
{!isInstallControlsDisabled && (
|
{!isInstallControlsDisabled && (
|
||||||
<>
|
<InstallControlsButton
|
||||||
{isExternallyManaged && !hasInstallWarning && !configCore.featureToggles.managedPluginsInstall ? (
|
plugin={plugin}
|
||||||
<ExternallyManagedButton
|
latestCompatibleVersion={latestCompatibleVersion}
|
||||||
pluginId={plugin.id}
|
pluginStatus={pluginStatus}
|
||||||
pluginStatus={pluginStatus}
|
setNeedReload={setNeedReload}
|
||||||
angularDetected={plugin.angularDetected}
|
hasInstallWarning={hasInstallWarning}
|
||||||
/>
|
/>
|
||||||
) : (
|
|
||||||
<InstallControlsButton
|
|
||||||
plugin={plugin}
|
|
||||||
latestCompatibleVersion={latestCompatibleVersion}
|
|
||||||
pluginStatus={pluginStatus}
|
|
||||||
setNeedReload={setNeedReload}
|
|
||||||
hasInstallWarning={hasInstallWarning}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
<GetStartedWithPlugin plugin={plugin} />
|
<GetStartedWithPlugin plugin={plugin} />
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ export const UpdateAllModal = ({ isOpen, onDismiss, isLoading, plugins }: Props)
|
|||||||
setInProgress(true);
|
setInProgress(true);
|
||||||
|
|
||||||
// in cloud the requests need to be sync
|
// in cloud the requests need to be sync
|
||||||
if (config.pluginAdminExternalManageEnabled && config.featureToggles.managedPluginsInstall) {
|
if (config.pluginAdminExternalManageEnabled) {
|
||||||
for (let plugin of plugins) {
|
for (let plugin of plugins) {
|
||||||
if (selectedPlugins?.has(plugin.id)) {
|
if (selectedPlugins?.has(plugin.id)) {
|
||||||
await install(plugin.id, plugin.latestVersion, true);
|
await install(plugin.id, plugin.latestVersion, true);
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ export const UpdateModalBody = ({
|
|||||||
{`${errorMap.size} ${t('plugins.catalog.update-all.error-status-text', 'failed - see error messages')}`}
|
{`${errorMap.size} ${t('plugins.catalog.update-all.error-status-text', 'failed - see error messages')}`}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{config.pluginAdminExternalManageEnabled && config.featureToggles.managedPluginsInstall && (
|
{config.pluginAdminExternalManageEnabled && (
|
||||||
<footer className={styles.footer}>
|
<footer className={styles.footer}>
|
||||||
<Trans i18nKey="plugins.catalog.update-all.cloud-update-message">
|
<Trans i18nKey="plugins.catalog.update-all.cloud-update-message">
|
||||||
* It may take a few minutes for the plugins to be available for usage.
|
* It may take a few minutes for the plugins to be available for usage.
|
||||||
|
|||||||
@@ -92,10 +92,8 @@ describe('Plugins/Helpers', () => {
|
|||||||
test('core plugins should be fullyInstalled in cloud', () => {
|
test('core plugins should be fullyInstalled in cloud', () => {
|
||||||
const corePluginId = 'plugin-core';
|
const corePluginId = 'plugin-core';
|
||||||
|
|
||||||
const oldFeatureTogglesManagedPluginsInstall = config.featureToggles.managedPluginsInstall;
|
|
||||||
const oldPluginAdminExternalManageEnabled = config.pluginAdminExternalManageEnabled;
|
const oldPluginAdminExternalManageEnabled = config.pluginAdminExternalManageEnabled;
|
||||||
|
|
||||||
config.featureToggles.managedPluginsInstall = true;
|
|
||||||
config.pluginAdminExternalManageEnabled = true;
|
config.pluginAdminExternalManageEnabled = true;
|
||||||
|
|
||||||
const merged = mergeLocalsAndRemotes({
|
const merged = mergeLocalsAndRemotes({
|
||||||
@@ -109,17 +107,14 @@ describe('Plugins/Helpers', () => {
|
|||||||
expect(findMerged(corePluginId)?.isCore).toBe(true);
|
expect(findMerged(corePluginId)?.isCore).toBe(true);
|
||||||
expect(findMerged(corePluginId)?.isFullyInstalled).toBe(true);
|
expect(findMerged(corePluginId)?.isFullyInstalled).toBe(true);
|
||||||
|
|
||||||
config.featureToggles.managedPluginsInstall = oldFeatureTogglesManagedPluginsInstall;
|
|
||||||
config.pluginAdminExternalManageEnabled = oldPluginAdminExternalManageEnabled;
|
config.pluginAdminExternalManageEnabled = oldPluginAdminExternalManageEnabled;
|
||||||
});
|
});
|
||||||
|
|
||||||
test('plugins should be fully installed if they are installed and it is provisioned', () => {
|
test('plugins should be fully installed if they are installed and it is provisioned', () => {
|
||||||
const pluginId = 'plugin-1';
|
const pluginId = 'plugin-1';
|
||||||
|
|
||||||
const oldFeatureTogglesManagedPluginsInstall = config.featureToggles.managedPluginsInstall;
|
|
||||||
const oldPluginAdminExternalManageEnabled = config.pluginAdminExternalManageEnabled;
|
const oldPluginAdminExternalManageEnabled = config.pluginAdminExternalManageEnabled;
|
||||||
|
|
||||||
config.featureToggles.managedPluginsInstall = true;
|
|
||||||
config.pluginAdminExternalManageEnabled = true;
|
config.pluginAdminExternalManageEnabled = true;
|
||||||
|
|
||||||
const merged = mergeLocalsAndRemotes({
|
const merged = mergeLocalsAndRemotes({
|
||||||
@@ -133,15 +128,12 @@ describe('Plugins/Helpers', () => {
|
|||||||
expect(findMerged(pluginId)).not.toBeUndefined();
|
expect(findMerged(pluginId)).not.toBeUndefined();
|
||||||
expect(findMerged(pluginId)?.isFullyInstalled).toBe(true);
|
expect(findMerged(pluginId)?.isFullyInstalled).toBe(true);
|
||||||
|
|
||||||
config.featureToggles.managedPluginsInstall = oldFeatureTogglesManagedPluginsInstall;
|
|
||||||
config.pluginAdminExternalManageEnabled = oldPluginAdminExternalManageEnabled;
|
config.pluginAdminExternalManageEnabled = oldPluginAdminExternalManageEnabled;
|
||||||
});
|
});
|
||||||
|
|
||||||
test('plugins should have update when instance version is different from remote version', () => {
|
test('plugins should have update when instance version is different from remote version', () => {
|
||||||
const oldFeatureTogglesManagedPluginsInstall = config.featureToggles.managedPluginsInstall;
|
|
||||||
const oldPluginAdminExternalManageEnabled = config.pluginAdminExternalManageEnabled;
|
const oldPluginAdminExternalManageEnabled = config.pluginAdminExternalManageEnabled;
|
||||||
|
|
||||||
config.featureToggles.managedPluginsInstall = true;
|
|
||||||
config.pluginAdminExternalManageEnabled = true;
|
config.pluginAdminExternalManageEnabled = true;
|
||||||
|
|
||||||
const pluginId = 'plugin-1';
|
const pluginId = 'plugin-1';
|
||||||
@@ -162,7 +154,6 @@ describe('Plugins/Helpers', () => {
|
|||||||
expect(findMerged(pluginId)).not.toBeUndefined();
|
expect(findMerged(pluginId)).not.toBeUndefined();
|
||||||
expect(findMerged(pluginId)?.hasUpdate).toBe(true);
|
expect(findMerged(pluginId)?.hasUpdate).toBe(true);
|
||||||
|
|
||||||
config.featureToggles.managedPluginsInstall = oldFeatureTogglesManagedPluginsInstall;
|
|
||||||
config.pluginAdminExternalManageEnabled = oldPluginAdminExternalManageEnabled;
|
config.pluginAdminExternalManageEnabled = oldPluginAdminExternalManageEnabled;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import uFuzzy from '@leeoniya/ufuzzy';
|
|||||||
|
|
||||||
import { PluginSignatureStatus, dateTimeParse, PluginError, PluginType, PluginErrorCode } from '@grafana/data';
|
import { PluginSignatureStatus, dateTimeParse, PluginError, PluginType, PluginErrorCode } from '@grafana/data';
|
||||||
import { config, featureEnabled } from '@grafana/runtime';
|
import { config, featureEnabled } from '@grafana/runtime';
|
||||||
import configCore, { Settings } from 'app/core/config';
|
import { Settings } from 'app/core/config';
|
||||||
import { contextSrv } from 'app/core/core';
|
import { contextSrv } from 'app/core/core';
|
||||||
import { getBackendSrv } from 'app/core/services/backend_srv';
|
import { getBackendSrv } from 'app/core/services/backend_srv';
|
||||||
import { AccessControlAction } from 'app/types';
|
import { AccessControlAction } from 'app/types';
|
||||||
@@ -63,7 +63,7 @@ export function mergeLocalsAndRemotes({
|
|||||||
const catalogPlugin = mergeLocalAndRemote(localCounterpart, remotePlugin, error);
|
const catalogPlugin = mergeLocalAndRemote(localCounterpart, remotePlugin, error);
|
||||||
|
|
||||||
// for managed instances, check if plugin is installed, but not yet present in the current instance
|
// for managed instances, check if plugin is installed, but not yet present in the current instance
|
||||||
if (configCore.featureToggles.managedPluginsInstall && config.pluginAdminExternalManageEnabled) {
|
if (config.pluginAdminExternalManageEnabled) {
|
||||||
catalogPlugin.isFullyInstalled = catalogPlugin.isCore
|
catalogPlugin.isFullyInstalled = catalogPlugin.isCore
|
||||||
? true
|
? true
|
||||||
: (instancesMap.has(remotePlugin.slug) || provisionedSet.has(remotePlugin.slug)) && catalogPlugin.isInstalled;
|
: (instancesMap.has(remotePlugin.slug) || provisionedSet.has(remotePlugin.slug)) && catalogPlugin.isInstalled;
|
||||||
|
|||||||
@@ -11,10 +11,8 @@ export const usePluginConfig = (plugin?: CatalogPlugin) => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isPluginInstalled =
|
const isPluginInstalled = config.pluginAdminExternalManageEnabled ? plugin.isFullyInstalled : plugin.isInstalled;
|
||||||
config.pluginAdminExternalManageEnabled && config.featureToggles.managedPluginsInstall
|
|
||||||
? plugin.isFullyInstalled
|
|
||||||
: plugin.isInstalled;
|
|
||||||
if (isPluginInstalled && !plugin.isDisabled) {
|
if (isPluginInstalled && !plugin.isDisabled) {
|
||||||
return loadPlugin(plugin.id);
|
return loadPlugin(plugin.id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -391,31 +391,6 @@ describe('Plugin details page', () => {
|
|||||||
expect(await queryByRole('button', { name: /(un)?install/i })).not.toBeInTheDocument();
|
expect(await queryByRole('button', { name: /(un)?install/i })).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display install link with `config.pluginAdminExternalManageEnabled` set to true', async () => {
|
|
||||||
config.pluginAdminExternalManageEnabled = true;
|
|
||||||
|
|
||||||
const { queryByRole } = renderPluginDetails({ id, isInstalled: false });
|
|
||||||
|
|
||||||
expect(await queryByRole('link', { name: /install via grafana.com/i })).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display uninstall link for an installed plugin with `config.pluginAdminExternalManageEnabled` set to true', async () => {
|
|
||||||
config.pluginAdminExternalManageEnabled = true;
|
|
||||||
|
|
||||||
const { queryByRole } = renderPluginDetails({ id, isInstalled: true });
|
|
||||||
|
|
||||||
expect(await queryByRole('link', { name: /uninstall via grafana.com/i })).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display update and uninstall links for a plugin with an available update and `config.pluginAdminExternalManageEnabled` set to true', async () => {
|
|
||||||
config.pluginAdminExternalManageEnabled = true;
|
|
||||||
|
|
||||||
const { queryByRole } = renderPluginDetails({ id, isInstalled: true, hasUpdate: true });
|
|
||||||
|
|
||||||
expect(await queryByRole('link', { name: /update via grafana.com/i })).toBeInTheDocument();
|
|
||||||
expect(queryByRole('link', { name: /uninstall via grafana.com/i })).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display alert with information about why the plugin is disabled', async () => {
|
it('should display alert with information about why the plugin is disabled', async () => {
|
||||||
const { queryByTestId } = renderPluginDetails({
|
const { queryByTestId } = renderPluginDetails({
|
||||||
id,
|
id,
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { from, forkJoin, timeout, lastValueFrom, catchError, of } from 'rxjs';
|
|||||||
|
|
||||||
import { PanelPlugin, PluginError } from '@grafana/data';
|
import { PanelPlugin, PluginError } from '@grafana/data';
|
||||||
import { config, getBackendSrv, isFetchError } from '@grafana/runtime';
|
import { config, getBackendSrv, isFetchError } from '@grafana/runtime';
|
||||||
import configCore from 'app/core/config';
|
|
||||||
import { importPanelPlugin } from 'app/features/plugins/importPanelPlugin';
|
import { importPanelPlugin } from 'app/features/plugins/importPanelPlugin';
|
||||||
import { StoreState, ThunkResult } from 'app/types';
|
import { StoreState, ThunkResult } from 'app/types';
|
||||||
|
|
||||||
@@ -28,14 +27,8 @@ export const fetchAll = createAsyncThunk(`${STATE_PREFIX}/fetchAll`, async (_, t
|
|||||||
thunkApi.dispatch({ type: `${STATE_PREFIX}/fetchLocal/pending` });
|
thunkApi.dispatch({ type: `${STATE_PREFIX}/fetchLocal/pending` });
|
||||||
thunkApi.dispatch({ type: `${STATE_PREFIX}/fetchRemote/pending` });
|
thunkApi.dispatch({ type: `${STATE_PREFIX}/fetchRemote/pending` });
|
||||||
|
|
||||||
const instance$ =
|
const instance$ = config.pluginAdminExternalManageEnabled ? from(getInstancePlugins()) : of(undefined);
|
||||||
config.pluginAdminExternalManageEnabled && configCore.featureToggles.managedPluginsInstall
|
const provisioned$ = config.pluginAdminExternalManageEnabled ? from(getProvisionedPlugins()) : of(undefined);
|
||||||
? from(getInstancePlugins())
|
|
||||||
: of(undefined);
|
|
||||||
const provisioned$ =
|
|
||||||
config.pluginAdminExternalManageEnabled && configCore.featureToggles.managedPluginsInstall
|
|
||||||
? from(getProvisionedPlugins())
|
|
||||||
: of(undefined);
|
|
||||||
const TIMEOUT = 500;
|
const TIMEOUT = 500;
|
||||||
const pluginErrors$ = from(getPluginErrors());
|
const pluginErrors$ = from(getPluginErrors());
|
||||||
const local$ = from(getLocalPlugins());
|
const local$ = from(getLocalPlugins());
|
||||||
|
|||||||
Reference in New Issue
Block a user