The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/plugins/manager/pipeline/bootstrap/steps.go

147 lines
5.1 KiB

package bootstrap
import (
"context"
"path"
"slices"
"github.com/grafana/grafana/pkg/infra/slugify"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/config"
"github.com/grafana/grafana/pkg/plugins/log"
"github.com/grafana/grafana/pkg/plugins/manager/loader/assetpath"
)
// DefaultConstructor implements the default ConstructFunc used for the Construct step of the Bootstrap stage.
//
// It uses a pluginFactoryFunc to create plugins and the signatureCalculator to calculate the plugin's signature state.
type DefaultConstructor struct {
pluginFactoryFunc pluginFactoryFunc
signatureCalculator plugins.SignatureCalculator
log log.Logger
}
// DefaultConstructFunc is the default ConstructFunc used for the Construct step of the Bootstrap stage.
func DefaultConstructFunc(signatureCalculator plugins.SignatureCalculator, assetPath *assetpath.Service) ConstructFunc {
return NewDefaultConstructor(signatureCalculator, assetPath).Construct
}
// DefaultDecorateFuncs are the default DecorateFuncs used for the Decorate step of the Bootstrap stage.
func DefaultDecorateFuncs(cfg *config.PluginManagementCfg) []DecorateFunc {
return []DecorateFunc{
AppDefaultNavURLDecorateFunc,
TemplateDecorateFunc,
AppChildDecorateFunc(),
SkipHostEnvVarsDecorateFunc(cfg),
}
}
// NewDefaultConstructor returns a new DefaultConstructor.
func NewDefaultConstructor(signatureCalculator plugins.SignatureCalculator, assetPath *assetpath.Service) *DefaultConstructor {
return &DefaultConstructor{
pluginFactoryFunc: NewDefaultPluginFactory(assetPath).createPlugin,
signatureCalculator: signatureCalculator,
log: log.New("plugins.construct"),
}
}
// Construct will calculate the plugin's signature state and create the plugin using the pluginFactoryFunc.
func (c *DefaultConstructor) Construct(ctx context.Context, src plugins.PluginSource, bundle *plugins.FoundBundle) ([]*plugins.Plugin, error) {
sig, err := c.signatureCalculator.Calculate(ctx, src, bundle.Primary)
if err != nil {
c.log.Warn("Could not calculate plugin signature state", "pluginId", bundle.Primary.JSONData.ID, "error", err)
return nil, err
}
plugin, err := c.pluginFactoryFunc(bundle, src.PluginClass(ctx), sig)
if err != nil {
c.log.Error("Could not create primary plugin base", "pluginId", bundle.Primary.JSONData.ID, "error", err)
return nil, err
}
res := make([]*plugins.Plugin, 0, len(plugin.Children)+1)
res = append(res, plugin)
res = append(res, plugin.Children...)
return res, nil
}
// AppDefaultNavURLDecorateFunc is a DecorateFunc that sets the default nav URL for app plugins.
func AppDefaultNavURLDecorateFunc(_ context.Context, p *plugins.Plugin) (*plugins.Plugin, error) {
if p.IsApp() {
setDefaultNavURL(p)
}
return p, nil
}
// TemplateDecorateFunc is a DecorateFunc that removes the placeholder for the version and last_update fields.
func TemplateDecorateFunc(_ context.Context, p *plugins.Plugin) (*plugins.Plugin, error) {
// %VERSION% and %TODAY% are valid values, according to the plugin schema
// but it's meant to be replaced by the build system with the actual version and date.
// If not, it's the same than not having a version or a date.
if p.Info.Version == "%VERSION%" {
p.Info.Version = ""
}
if p.Info.Updated == "%TODAY%" {
p.Info.Updated = ""
}
return p, nil
}
func setDefaultNavURL(p *plugins.Plugin) {
// slugify pages
for _, include := range p.Includes {
if include.Slug == "" {
include.Slug = slugify.Slugify(include.Name)
}
if !include.DefaultNav {
continue
}
if include.Type == "page" {
p.DefaultNavURL = path.Join("/plugins/", p.ID, "/page/", include.Slug)
}
if include.Type == "dashboard" {
dboardURL := include.DashboardURLPath()
if dboardURL == "" {
p.Logger().Warn("Included dashboard is missing a UID field")
continue
}
p.DefaultNavURL = dboardURL
}
}
}
// AppChildDecorateFunc is a DecorateFunc that configures child plugins of app plugins.
func AppChildDecorateFunc() DecorateFunc {
return func(_ context.Context, p *plugins.Plugin) (*plugins.Plugin, error) {
if p.Parent != nil && p.Parent.IsApp() {
configureAppChildPlugin(p.Parent, p)
}
return p, nil
}
}
func configureAppChildPlugin(parent *plugins.Plugin, child *plugins.Plugin) {
if !parent.IsApp() {
return
}
child.IncludedInAppID = parent.ID
// If the child plugin does not have a version, it will inherit the version from the parent.
// This is to ensure that the frontend can appropriately cache the plugin assets.
if child.Info.Version == "" {
child.Info.Version = parent.Info.Version
}
}
// SkipHostEnvVarsDecorateFunc returns a DecorateFunc that configures the SkipHostEnvVars field of the plugin.
// It will be set to true if the FlagPluginsSkipHostEnvVars feature flag is set, and the plugin is not present in the
// ForwardHostEnvVars plugin ids list.
func SkipHostEnvVarsDecorateFunc(cfg *config.PluginManagementCfg) DecorateFunc {
return func(_ context.Context, p *plugins.Plugin) (*plugins.Plugin, error) {
p.SkipHostEnvVars = cfg.Features.SkipHostEnvVarsEnabled && !slices.Contains(cfg.ForwardHostEnvVars, p.ID)
return p, nil
}
}