ref: pass tracer to plugin factory func (#93701)

* ref: pass tracer to plugin factory func

* fix: add tracer to coreplugin

* test: fix test, generate wire

* test: ignore trace field in loader_test

* ref: pass tracer as dependency, don't store in plugin

* ref: wrap tracer with tracer provider to satisfy WithTracerProvider

* ref: use otel trace.Tracer type for tracer
This commit is contained in:
Syerikjan Kh
2024-10-10 20:30:56 -04:00
committed by GitHub
parent 27c44f4709
commit f8748f0724
13 changed files with 75 additions and 30 deletions

View File

@ -3,7 +3,8 @@ package backendplugin
import (
"github.com/grafana/grafana/pkg/plugins/log"
"go.opentelemetry.io/otel/trace"
)
// PluginFactoryFunc is a function type for creating a Plugin.
type PluginFactoryFunc func(pluginID string, logger log.Logger, env func() []string) (Plugin, error)
type PluginFactoryFunc func(pluginID string, logger log.Logger, tracer trace.Tracer, env func() []string) (Plugin, error)

View File

@ -4,6 +4,7 @@ import (
"context"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"go.opentelemetry.io/otel/trace"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin"
@ -24,7 +25,7 @@ type corePlugin struct {
// New returns a new backendplugin.PluginFactoryFunc for creating a core (built-in) backendplugin.Plugin.
func New(opts backend.ServeOpts) backendplugin.PluginFactoryFunc {
return func(pluginID string, logger log.Logger, _ func() []string) (backendplugin.Plugin, error) {
return func(pluginID string, logger log.Logger, _ trace.Tracer, _ func() []string) (backendplugin.Plugin, error) {
return &corePlugin{
pluginID: pluginID,
logger: logger,

View File

@ -8,13 +8,14 @@ import (
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
"github.com/grafana/grafana/pkg/plugins/log"
"github.com/grafana/grafana/pkg/plugins/manager/fakes"
"github.com/stretchr/testify/require"
)
func TestCorePlugin(t *testing.T) {
t.Run("New core plugin with empty opts should return expected values", func(t *testing.T) {
factory := coreplugin.New(backend.ServeOpts{})
p, err := factory("plugin", log.New("test"), nil)
p, err := factory("plugin", log.New("test"), fakes.InitializeNoopTracerForTest(), nil)
require.NoError(t, err)
require.NotNil(t, p)
require.NoError(t, p.Start(context.Background()))
@ -47,7 +48,7 @@ func TestCorePlugin(t *testing.T) {
return nil
}),
})
p, err := factory("plugin", log.New("test"), nil)
p, err := factory("plugin", log.New("test"), fakes.InitializeNoopTracerForTest(), nil)
require.NoError(t, err)
require.NotNil(t, p)
require.NoError(t, p.Start(context.Background()))

View File

@ -254,7 +254,7 @@ func NewPlugin(pluginID string, cfg *setting.Cfg, httpClientProvider *httpclient
if backendFactory == nil {
return nil, ErrCorePluginNotFound
}
bp, err := backendFactory(p.ID, p.Logger(), nil)
bp, err := backendFactory(p.ID, p.Logger(), tracer, nil)
if err != nil {
return nil, err
}

View File

@ -6,6 +6,8 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend/grpcplugin"
goplugin "github.com/hashicorp/go-plugin"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
trace "go.opentelemetry.io/otel/trace"
"go.opentelemetry.io/otel/trace/embedded"
"google.golang.org/grpc"
"github.com/grafana/grafana/pkg/plugins/backendplugin"
@ -40,7 +42,20 @@ var pluginSet = map[int]goplugin.PluginSet{
},
}
func newClientConfig(executablePath string, args []string, env []string, skipHostEnvVars bool, logger log.Logger,
type clientTracerProvider struct {
tracer trace.Tracer
embedded.TracerProvider
}
func (ctp *clientTracerProvider) Tracer(instrumentationName string, opts ...trace.TracerOption) trace.Tracer {
return ctp.tracer
}
func newClientTracerProvider(tracer trace.Tracer) trace.TracerProvider {
return &clientTracerProvider{tracer: tracer}
}
func newClientConfig(executablePath string, args []string, env []string, skipHostEnvVars bool, logger log.Logger, tracer trace.Tracer,
versionedPlugins map[int]goplugin.PluginSet) *goplugin.ClientConfig {
// We can ignore gosec G201 here, since the dynamic part of executablePath comes from the plugin definition
// nolint:gosec
@ -55,7 +70,13 @@ func newClientConfig(executablePath string, args []string, env []string, skipHos
Logger: logWrapper{Logger: logger},
AllowedProtocols: []goplugin.Protocol{goplugin.ProtocolGRPC},
GRPCDialOptions: []grpc.DialOption{
grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
// https://github.com/grafana/app-platform-wg/issues/140
// external plugins are loaded before k8s API server
// configures the tracing service thus failing to
// record trace span in the middleware.
// With code below we are passing the same tracer that k8s API server
// uses so that middleware is configured with tracer.
grpc.WithStatsHandler(otelgrpc.NewClientHandler(otelgrpc.WithTracerProvider(newClientTracerProvider(tracer)))),
},
}
}

View File

@ -4,6 +4,7 @@ import (
"context"
"errors"
trace "go.opentelemetry.io/otel/trace"
"google.golang.org/grpc"
"github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2"
@ -44,6 +45,7 @@ type ProtoClientOpts struct {
ExecutableArgs []string
Env []string
Logger log.Logger
Tracer trace.Tracer
}
func NewProtoClient(opts ProtoClientOpts) (ProtoClient, error) {
@ -56,6 +58,7 @@ func NewProtoClient(opts ProtoClientOpts) (ProtoClient, error) {
versionedPlugins: pluginSet,
},
opts.Logger,
opts.Tracer,
func() []string { return opts.Env },
)

View File

@ -7,6 +7,7 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/hashicorp/go-plugin"
trace "go.opentelemetry.io/otel/trace"
"github.com/grafana/grafana/pkg/infra/process"
"github.com/grafana/grafana/pkg/plugins"
@ -37,17 +38,17 @@ const (
// newPlugin allocates and returns a new gRPC (external) backendplugin.Plugin.
func newPlugin(descriptor PluginDescriptor) backendplugin.PluginFactoryFunc {
return func(pluginID string, logger log.Logger, env func() []string) (backendplugin.Plugin, error) {
return newGrpcPlugin(descriptor, logger, env), nil
return func(pluginID string, logger log.Logger, tracer trace.Tracer, env func() []string) (backendplugin.Plugin, error) {
return newGrpcPlugin(descriptor, logger, tracer, env), nil
}
}
func newGrpcPlugin(descriptor PluginDescriptor, logger log.Logger, env func() []string) *grpcPlugin {
func newGrpcPlugin(descriptor PluginDescriptor, logger log.Logger, tracer trace.Tracer, env func() []string) *grpcPlugin {
return &grpcPlugin{
descriptor: descriptor,
logger: logger,
clientFactory: func() *plugin.Client {
return plugin.NewClient(newClientConfig(descriptor.executablePath, descriptor.executableArgs, env(), descriptor.skipHostEnvVars, logger, descriptor.versionedPlugins))
return plugin.NewClient(newClientConfig(descriptor.executablePath, descriptor.executableArgs, env(), descriptor.skipHostEnvVars, logger, tracer, descriptor.versionedPlugins))
},
state: pluginStateNotStarted,
}

View File

@ -8,6 +8,8 @@ import (
"sync"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"go.opentelemetry.io/otel/trace"
"go.opentelemetry.io/otel/trace/noop"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/auth"
@ -267,6 +269,14 @@ func (r *FakePluginRepo) PluginVersion(ctx context.Context, pluginID, version st
return repo.VersionData{}, nil
}
type fakeTracerProvider struct {
noop.TracerProvider
}
func InitializeNoopTracerForTest() trace.Tracer {
return fakeTracerProvider{}.Tracer("test")
}
type FakePluginStorage struct {
ExtractFunc func(_ context.Context, pluginID string, dirNameFunc storage.DirNameGeneratorFunc, z *zip.ReadCloser) (*storage.ExtractedPluginArchive, error)
}
@ -340,7 +350,7 @@ func NewFakeBackendProcessProvider() *FakeBackendProcessProvider {
}
f.BackendFactoryFunc = func(ctx context.Context, p *plugins.Plugin) backendplugin.PluginFactoryFunc {
f.Requested[p.ID]++
return func(pluginID string, _ log.Logger, _ func() []string) (backendplugin.Plugin, error) {
return func(pluginID string, _ log.Logger, _ trace.Tracer, _ func() []string) (backendplugin.Plugin, error) {
f.Invoked[pluginID]++
return &FakePluginClient{}, nil
}

View File

@ -9,6 +9,7 @@ import (
"github.com/grafana/grafana/pkg/plugins/log"
"github.com/grafana/grafana/pkg/plugins/manager/process"
"github.com/grafana/grafana/pkg/plugins/manager/registry"
"go.opentelemetry.io/otel/trace"
)
// BackendClientInit implements an InitializeFunc for initializing a backend plugin process.
@ -21,20 +22,22 @@ type BackendClientInit struct {
envVarProvider envvars.Provider
backendProvider plugins.BackendFactoryProvider
log log.Logger
tracer trace.Tracer
}
// BackendClientInitStep returns a new InitializeFunc for registering a backend plugin process.
func BackendClientInitStep(envVarProvider envvars.Provider,
backendProvider plugins.BackendFactoryProvider) InitializeFunc {
return newBackendProcessRegistration(envVarProvider, backendProvider).Initialize
backendProvider plugins.BackendFactoryProvider, tracer trace.Tracer) InitializeFunc {
return newBackendProcessRegistration(envVarProvider, backendProvider, tracer).Initialize
}
func newBackendProcessRegistration(envVarProvider envvars.Provider,
backendProvider plugins.BackendFactoryProvider) *BackendClientInit {
backendProvider plugins.BackendFactoryProvider, tracer trace.Tracer) *BackendClientInit {
return &BackendClientInit{
backendProvider: backendProvider,
envVarProvider: envVarProvider,
log: log.New("plugins.backend.registration"),
tracer: tracer,
}
}
@ -49,7 +52,7 @@ func (b *BackendClientInit) Initialize(ctx context.Context, p *plugins.Plugin) (
// this will ensure that the env variables are calculated every time a plugin is started
envFunc := func() []string { return b.envVarProvider.PluginEnvVars(ctx, p) }
if backendClient, err := backendFactory(p.ID, p.Logger(), envFunc); err != nil {
if backendClient, err := backendFactory(p.ID, p.Logger(), b.tracer, envFunc); err != nil {
return nil, err
} else {
p.RegisterClient(backendClient)

View File

@ -4,11 +4,12 @@ import (
"context"
"testing"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin"
"github.com/grafana/grafana/pkg/plugins/log"
"github.com/grafana/grafana/pkg/plugins/manager/fakes"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/trace"
)
func TestInitializer_Initialize(t *testing.T) {
@ -28,7 +29,7 @@ func TestInitializer_Initialize(t *testing.T) {
Class: plugins.ClassCore,
}
stepFunc := BackendClientInitStep(&fakeEnvVarsProvider{}, &fakeBackendProvider{plugin: p})
stepFunc := BackendClientInitStep(&fakeEnvVarsProvider{}, &fakeBackendProvider{plugin: p}, fakes.InitializeNoopTracerForTest())
var err error
p, err = stepFunc(context.Background(), p)
@ -52,7 +53,7 @@ func TestInitializer_Initialize(t *testing.T) {
Class: plugins.ClassExternal,
}
stepFunc := BackendClientInitStep(&fakeEnvVarsProvider{}, &fakeBackendProvider{plugin: p})
stepFunc := BackendClientInitStep(&fakeEnvVarsProvider{}, &fakeBackendProvider{plugin: p}, fakes.InitializeNoopTracerForTest())
var err error
p, err = stepFunc(context.Background(), p)
@ -76,7 +77,7 @@ func TestInitializer_Initialize(t *testing.T) {
Class: plugins.ClassExternal,
}
stepFunc := BackendClientInitStep(&fakeEnvVarsProvider{}, &fakeBackendProvider{plugin: p})
stepFunc := BackendClientInitStep(&fakeEnvVarsProvider{}, &fakeBackendProvider{plugin: p}, fakes.InitializeNoopTracerForTest())
var err error
p, err = stepFunc(context.Background(), p)
@ -96,7 +97,7 @@ func TestInitializer_Initialize(t *testing.T) {
i := BackendClientInitStep(&fakeEnvVarsProvider{}, &fakeBackendProvider{
plugin: p,
})
}, fakes.InitializeNoopTracerForTest())
var err error
p, err = i(context.Background(), p)
@ -115,7 +116,7 @@ type fakeBackendProvider struct {
}
func (f *fakeBackendProvider) BackendFactory(_ context.Context, _ *plugins.Plugin) backendplugin.PluginFactoryFunc {
return func(_ string, _ log.Logger, _ func() []string) (backendplugin.Plugin, error) {
return func(_ string, _ log.Logger, _ trace.Tracer, _ func() []string) (backendplugin.Plugin, error) {
return f.plugin, nil
}
}

View File

@ -10,6 +10,7 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/trace"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/plugins"
@ -584,7 +585,7 @@ func TestLoader_Load_ExternalRegistration(t *testing.T) {
backendFactoryProvider := fakes.NewFakeBackendProcessProvider()
backendFactoryProvider.BackendFactoryFunc = func(ctx context.Context, plugin *plugins.Plugin) backendplugin.PluginFactoryFunc {
return func(pluginID string, logger log.Logger, env func() []string) (backendplugin.Plugin, error) {
return func(pluginID string, logger log.Logger, tracer trace.Tracer, env func() []string) (backendplugin.Plugin, error) {
require.Equal(t, "grafana-test-datasource", pluginID)
return &fakes.FakeBackendPlugin{}, nil
}
@ -1133,7 +1134,7 @@ func TestLoader_Load_SkipUninitializedPlugins(t *testing.T) {
procPrvdr := fakes.NewFakeBackendProcessProvider()
// Cause an initialization error
procPrvdr.BackendFactoryFunc = func(ctx context.Context, p *plugins.Plugin) backendplugin.PluginFactoryFunc {
return func(pluginID string, _ log.Logger, _ func() []string) (backendplugin.Plugin, error) {
return func(pluginID string, _ log.Logger, _ trace.Tracer, _ func() []string) (backendplugin.Plugin, error) {
if pluginID == "test-datasource" {
return nil, errors.New("failed to initialize")
}

View File

@ -68,7 +68,7 @@ func ProvideInitializationStage(cfg *config.PluginManagementCfg, pr registry.Ser
return initialization.New(cfg, initialization.Opts{
InitializeFuncs: []initialization.InitializeFunc{
ExternalServiceRegistrationStep(cfg, externalServiceRegistry, tracer),
initialization.BackendClientInitStep(pluginEnvProvider, bp),
initialization.BackendClientInitStep(pluginEnvProvider, bp, tracer),
initialization.BackendProcessStartStep(pm),
RegisterPluginRolesStep(roleRegistry),
RegisterActionSetsStep(actionSetRegistry),

View File

@ -5,6 +5,7 @@ import (
"errors"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2"
"github.com/grafana/grafana/pkg/plugins/backendplugin/provider"
@ -22,11 +23,12 @@ import (
"github.com/grafana/grafana/pkg/services/pluginsintegration/pipeline"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginerrs"
"github.com/grafana/grafana/pkg/services/rendering"
"go.opentelemetry.io/otel/trace"
)
func ProvideService(cfg *config.PluginManagementCfg, pluginEnvProvider envvars.Provider,
registry registry.Service) (*Manager, error) {
l, err := createLoader(cfg, pluginEnvProvider, registry)
registry registry.Service, tracer tracing.Tracer) (*Manager, error) {
l, err := createLoader(cfg, pluginEnvProvider, registry, tracer)
if err != nil {
return nil, err
}
@ -105,7 +107,7 @@ func (m *Manager) Renderer(ctx context.Context) (rendering.Plugin, bool) {
}
func createLoader(cfg *config.PluginManagementCfg, pluginEnvProvider envvars.Provider,
pr registry.Service) (loader.Service, error) {
pr registry.Service, tracer trace.Tracer) (loader.Service, error) {
d := discovery.New(cfg, discovery.Opts{
FindFilterFuncs: []discovery.FindFilterFunc{
discovery.NewPermittedPluginTypesFilterStep([]plugins.Type{plugins.TypeRenderer}),
@ -124,7 +126,7 @@ func createLoader(cfg *config.PluginManagementCfg, pluginEnvProvider envvars.Pro
})
i := initialization.New(cfg, initialization.Opts{
InitializeFuncs: []initialization.InitializeFunc{
initialization.BackendClientInitStep(pluginEnvProvider, provider.New(provider.RendererProvider)),
initialization.BackendClientInitStep(pluginEnvProvider, provider.New(provider.RendererProvider), tracer),
initialization.PluginRegistrationStep(pr),
},
})