mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Disable selecting enterprise plugins with no license (#28758)
* Add unlicensed property to plugins * Disable selecting unlicensed plugin * Add customizable plugin market place url * License: workaround enabled only in enterprise * linter * Move licensing info to front end * Update pkg/services/licensing/oss.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update pkg/services/licensing/oss.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update pkg/setting/setting.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update pkg/setting/setting.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update pkg/api/frontendsettings.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update sample.ini * Update docs * Update packages/grafana-runtime/src/config.ts Co-authored-by: Torkel Ödegaard <torkel@grafana.org> * Update public/app/features/datasources/state/buildCategories.ts Co-authored-by: Torkel Ödegaard <torkel@grafana.org> * Update pkg/api/frontendsettings.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update pkg/setting/setting.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix spelling Co-authored-by: Leonard Gram <leo@xlson.com> Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
This commit is contained in:
@@ -671,7 +671,7 @@ disable_total_stats = false
|
|||||||
basic_auth_username =
|
basic_auth_username =
|
||||||
basic_auth_password =
|
basic_auth_password =
|
||||||
|
|
||||||
# Metrics environment info adds dimensions to the `grafana_environment_info` metric, which
|
# Metrics environment info adds dimensions to the `grafana_environment_info` metric, which
|
||||||
# can expose more information about the Grafana instance.
|
# can expose more information about the Grafana instance.
|
||||||
[metrics.environment_info]
|
[metrics.environment_info]
|
||||||
#exampleLabel1 = exampleValue1
|
#exampleLabel1 = exampleValue1
|
||||||
@@ -769,6 +769,7 @@ enable_alpha = false
|
|||||||
app_tls_skip_verify_insecure = false
|
app_tls_skip_verify_insecure = false
|
||||||
# Enter a comma-separated list of plugin identifiers to identify plugins that are allowed to be loaded even if they lack a valid signature.
|
# Enter a comma-separated list of plugin identifiers to identify plugins that are allowed to be loaded even if they lack a valid signature.
|
||||||
allow_loading_unsigned_plugins =
|
allow_loading_unsigned_plugins =
|
||||||
|
marketplace_url = https://grafana.com/grafana/plugins/
|
||||||
|
|
||||||
#################################### Grafana Image Renderer Plugin ##########################
|
#################################### Grafana Image Renderer Plugin ##########################
|
||||||
[plugin.grafana-image-renderer]
|
[plugin.grafana-image-renderer]
|
||||||
|
|||||||
@@ -665,7 +665,7 @@
|
|||||||
; basic_auth_username =
|
; basic_auth_username =
|
||||||
; basic_auth_password =
|
; basic_auth_password =
|
||||||
|
|
||||||
# Metrics environment info adds dimensions to the `grafana_environment_info` metric, which
|
# Metrics environment info adds dimensions to the `grafana_environment_info` metric, which
|
||||||
# can expose more information about the Grafana instance.
|
# can expose more information about the Grafana instance.
|
||||||
[metrics.environment_info]
|
[metrics.environment_info]
|
||||||
#exampleLabel1 = exampleValue1
|
#exampleLabel1 = exampleValue1
|
||||||
@@ -757,6 +757,7 @@
|
|||||||
;app_tls_skip_verify_insecure = false
|
;app_tls_skip_verify_insecure = false
|
||||||
# Enter a comma-separated list of plugin identifiers to identify plugins that are allowed to be loaded even if they lack a valid signature.
|
# Enter a comma-separated list of plugin identifiers to identify plugins that are allowed to be loaded even if they lack a valid signature.
|
||||||
;allow_loading_unsigned_plugins =
|
;allow_loading_unsigned_plugins =
|
||||||
|
;marketplace_url = https://grafana.com/grafana/plugins/
|
||||||
|
|
||||||
#################################### Grafana Image Renderer Plugin ##########################
|
#################################### Grafana Image Renderer Plugin ##########################
|
||||||
[plugin.grafana-image-renderer]
|
[plugin.grafana-image-renderer]
|
||||||
|
|||||||
@@ -1336,6 +1336,10 @@ Set to `true` if you want to test alpha plugins that are not yet ready for gener
|
|||||||
|
|
||||||
Enter a comma-separated list of plugin identifiers to identify plugins that are allowed to be loaded even if they lack a valid signature.
|
Enter a comma-separated list of plugin identifiers to identify plugins that are allowed to be loaded even if they lack a valid signature.
|
||||||
|
|
||||||
|
### marketplace_url
|
||||||
|
|
||||||
|
Custom install/learn more url for enterprise plugins. Defaults to https://grafana.com/grafana/plugins/.
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
## [plugin.grafana-image-renderer]
|
## [plugin.grafana-image-renderer]
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ export interface LicenseInfo {
|
|||||||
expiry: number;
|
expiry: number;
|
||||||
licenseUrl: string;
|
licenseUrl: string;
|
||||||
stateInfo: string;
|
stateInfo: string;
|
||||||
|
hasValidLicense: boolean;
|
||||||
|
edition: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ export interface DataSourcePluginMeta<T extends KeyValue = {}> extends PluginMet
|
|||||||
queryOptions?: PluginMetaQueryOptions;
|
queryOptions?: PluginMetaQueryOptions;
|
||||||
sort?: number;
|
sort?: number;
|
||||||
streaming?: boolean;
|
streaming?: boolean;
|
||||||
|
unlicensed?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PluginMetaQueryOptions {
|
interface PluginMetaQueryOptions {
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ export class GrafanaBootConfig implements GrafanaConfig {
|
|||||||
rendererAvailable = false;
|
rendererAvailable = false;
|
||||||
http2Enabled = false;
|
http2Enabled = false;
|
||||||
dateFormats?: SystemDateFormatSettings;
|
dateFormats?: SystemDateFormatSettings;
|
||||||
|
marketplaceUrl?: string;
|
||||||
|
|
||||||
constructor(options: GrafanaBootConfig) {
|
constructor(options: GrafanaBootConfig) {
|
||||||
this.theme = options.bootData.user.lightTheme ? getTheme(GrafanaThemeType.Light) : getTheme(GrafanaThemeType.Dark);
|
this.theme = options.bootData.user.lightTheme ? getTheme(GrafanaThemeType.Light) : getTheme(GrafanaThemeType.Dark);
|
||||||
|
|||||||
@@ -230,14 +230,17 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
|
|||||||
"isEnterprise": hs.License.HasValidLicense(),
|
"isEnterprise": hs.License.HasValidLicense(),
|
||||||
},
|
},
|
||||||
"licenseInfo": map[string]interface{}{
|
"licenseInfo": map[string]interface{}{
|
||||||
"hasLicense": hs.License.HasLicense(),
|
"hasLicense": hs.License.HasLicense(),
|
||||||
"expiry": hs.License.Expiry(),
|
"hasValidLicense": hs.License.HasValidLicense(),
|
||||||
"stateInfo": hs.License.StateInfo(),
|
"expiry": hs.License.Expiry(),
|
||||||
"licenseUrl": hs.License.LicenseURL(c.SignedInUser),
|
"stateInfo": hs.License.StateInfo(),
|
||||||
|
"licenseUrl": hs.License.LicenseURL(c.SignedInUser),
|
||||||
|
"edition": hs.License.Edition(),
|
||||||
},
|
},
|
||||||
"featureToggles": hs.Cfg.FeatureToggles,
|
"featureToggles": hs.Cfg.FeatureToggles,
|
||||||
"rendererAvailable": hs.RenderService.IsAvailable(),
|
"rendererAvailable": hs.RenderService.IsAvailable(),
|
||||||
"http2Enabled": hs.Cfg.Protocol == setting.HTTP2Scheme,
|
"http2Enabled": hs.Cfg.Protocol == setting.HTTP2Scheme,
|
||||||
|
"marketplaceUrl": hs.Cfg.MarketplaceURL,
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsonObj, nil
|
return jsonObj, nil
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/dtos"
|
"github.com/grafana/grafana/pkg/api/dtos"
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
openSource = "Open Source"
|
||||||
|
)
|
||||||
|
|
||||||
type OSSLicensingService struct {
|
type OSSLicensingService struct {
|
||||||
Cfg *setting.Cfg `inject:""`
|
Cfg *setting.Cfg `inject:""`
|
||||||
HooksService *hooks.HooksService `inject:""`
|
HooksService *hooks.HooksService `inject:""`
|
||||||
@@ -21,7 +25,7 @@ func (*OSSLicensingService) Expiry() int64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (*OSSLicensingService) Edition() string {
|
func (*OSSLicensingService) Edition() string {
|
||||||
return "Open Source"
|
return openSource
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*OSSLicensingService) StateInfo() string {
|
func (*OSSLicensingService) StateInfo() string {
|
||||||
|
|||||||
@@ -271,6 +271,7 @@ type Cfg struct {
|
|||||||
PluginsAppsSkipVerifyTLS bool
|
PluginsAppsSkipVerifyTLS bool
|
||||||
PluginSettings PluginSettings
|
PluginSettings PluginSettings
|
||||||
PluginsAllowUnsigned []string
|
PluginsAllowUnsigned []string
|
||||||
|
MarketplaceURL string
|
||||||
DisableSanitizeHtml bool
|
DisableSanitizeHtml bool
|
||||||
EnterpriseLicensePath string
|
EnterpriseLicensePath string
|
||||||
|
|
||||||
@@ -794,6 +795,7 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
|
|||||||
plug = strings.TrimSpace(plug)
|
plug = strings.TrimSpace(plug)
|
||||||
cfg.PluginsAllowUnsigned = append(cfg.PluginsAllowUnsigned, plug)
|
cfg.PluginsAllowUnsigned = append(cfg.PluginsAllowUnsigned, plug)
|
||||||
}
|
}
|
||||||
|
cfg.MarketplaceURL = pluginsSection.Key("marketplace_url").MustString("https://grafana.com/grafana/plugins/")
|
||||||
cfg.Protocol = Protocol
|
cfg.Protocol = Protocol
|
||||||
|
|
||||||
// Read and populate feature toggles list
|
// Read and populate feature toggles list
|
||||||
|
|||||||
@@ -129,10 +129,9 @@ interface DataSourceTypeCardProps {
|
|||||||
const DataSourceTypeCard: FC<DataSourceTypeCardProps> = props => {
|
const DataSourceTypeCard: FC<DataSourceTypeCardProps> = props => {
|
||||||
const { plugin, onLearnMoreClick } = props;
|
const { plugin, onLearnMoreClick } = props;
|
||||||
const isPhantom = plugin.module === 'phantom';
|
const isPhantom = plugin.module === 'phantom';
|
||||||
const onClick = !isPhantom ? props.onClick : () => {};
|
const onClick = !isPhantom && !plugin.unlicensed ? props.onClick : () => {};
|
||||||
|
|
||||||
// find first plugin info link
|
// find first plugin info link
|
||||||
const learnMoreLink = plugin.info.links && plugin.info.links.length > 0 ? plugin.info.links[0] : null;
|
const learnMoreLink = plugin.info?.links?.length > 0 ? plugin.info.links[0] : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
@@ -154,7 +153,7 @@ const DataSourceTypeCard: FC<DataSourceTypeCardProps> = props => {
|
|||||||
{learnMoreLink.name}
|
{learnMoreLink.name}
|
||||||
</LinkButton>
|
</LinkButton>
|
||||||
)}
|
)}
|
||||||
{!isPhantom && <Button>Select</Button>}
|
{!isPhantom && <Button disabled={plugin.unlicensed}>Select</Button>}
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
labels={
|
labels={
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { DataSourcePluginMeta, PluginType } from '@grafana/data';
|
import { DataSourcePluginMeta, PluginType } from '@grafana/data';
|
||||||
import { DataSourcePluginCategory } from 'app/types';
|
import { DataSourcePluginCategory } from 'app/types';
|
||||||
|
import { config } from '../../../core/config';
|
||||||
|
|
||||||
export function buildCategories(plugins: DataSourcePluginMeta[]): DataSourcePluginCategory[] {
|
export function buildCategories(plugins: DataSourcePluginMeta[]): DataSourcePluginCategory[] {
|
||||||
const categories: DataSourcePluginCategory[] = [
|
const categories: DataSourcePluginCategory[] = [
|
||||||
@@ -22,10 +23,15 @@ export function buildCategories(plugins: DataSourcePluginMeta[]): DataSourcePlug
|
|||||||
categoryIndex[category.id] = category;
|
categoryIndex[category.id] = category;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { edition, hasValidLicense } = config.licenseInfo;
|
||||||
|
|
||||||
for (const plugin of plugins) {
|
for (const plugin of plugins) {
|
||||||
|
const enterprisePlugin = enterprisePlugins.find(item => item.id === plugin.id);
|
||||||
// Force category for enterprise plugins
|
// Force category for enterprise plugins
|
||||||
if (plugin.enterprise || enterprisePlugins.find(item => item.id === plugin.id)) {
|
if (plugin.enterprise || enterprisePlugin) {
|
||||||
plugin.category = 'enterprise';
|
plugin.category = 'enterprise';
|
||||||
|
plugin.unlicensed = edition !== 'Open Source' && !hasValidLicense;
|
||||||
|
plugin.info.links = enterprisePlugin?.info?.links || plugin.info.links;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix link name
|
// Fix link name
|
||||||
@@ -197,7 +203,7 @@ function getPhantomPlugin(options: GetPhantomPluginOptions): DataSourcePluginMet
|
|||||||
author: { name: 'Grafana Labs' },
|
author: { name: 'Grafana Labs' },
|
||||||
links: [
|
links: [
|
||||||
{
|
{
|
||||||
url: 'https://grafana.com/grafana/plugins/' + options.id,
|
url: config.marketplaceUrl + options.id,
|
||||||
name: 'Install now',
|
name: 'Install now',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user