[extension] Error out if passed extension.Settings has incorrect type (#12305)

<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue.
Ex. Adding a feature - Explain what this achieves.-->
#### Description

Explicitly error out if the passed `component.ID` does not have a
matching `component.Type`

<!-- Issue number if applicable -->
#### Link to tracking issue
Updates #12221
This commit is contained in:
Pablo Baeyens 2025-02-07 21:15:42 +01:00 committed by GitHub
parent 765e4a5417
commit 183d7edf9f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 139 additions and 31 deletions

View File

@ -0,0 +1,25 @@
# Use this changelog template to create an entry for release notes.
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: deprecation
# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
component: extensiontest
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Deprecate `extensiontest.NewNopSettings` in favor of `extensiontest.NewNopSettingsWithType`
# One or more tracking issues or pull requests related to the change
issues: [12305]
# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: [api]

View File

@ -0,0 +1,25 @@
# Use this changelog template to create an entry for release notes.
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: bug_fix
# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
component: extension
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Explicitly error out at extension creation time if there is a type mismatch.
# One or more tracking issues or pull requests related to the change
issues: [12305]
# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: [api]

View File

@ -22,8 +22,10 @@ import (
"go.opentelemetry.io/collector/processor/processortest"
)
var typ = component.MustNewType("sample")
func TestComponentFactoryType(t *testing.T) {
require.Equal(t, "sample", NewFactory().Type().String())
require.Equal(t, typ, NewFactory().Type())
}
func TestComponentConfigStruct(t *testing.T) {

View File

@ -17,8 +17,10 @@ import (
"go.opentelemetry.io/collector/receiver/receivertest"
)
var typ = component.MustNewType("sample")
func TestComponentFactoryType(t *testing.T) {
require.Equal(t, "sample", NewFactory().Type().String())
require.Equal(t, typ, NewFactory().Type())
}
func TestComponentConfigStruct(t *testing.T) {

View File

@ -16,8 +16,10 @@ import (
"go.opentelemetry.io/collector/scraper/scrapertest"
)
var typ = component.MustNewType("sample")
func TestComponentFactoryType(t *testing.T) {
require.Equal(t, "sample", NewFactory().Type().String())
require.Equal(t, typ, NewFactory().Type())
}
func TestComponentConfigStruct(t *testing.T) {

View File

@ -60,8 +60,10 @@ import (
{{- end }}
)
var typ = component.MustNewType("{{ .Type }}")
func TestComponentFactoryType(t *testing.T) {
require.Equal(t, "{{ .Type }}", NewFactory().Type().String())
require.Equal(t, typ, NewFactory().Type())
}
func TestComponentConfigStruct(t *testing.T) {
@ -412,7 +414,7 @@ func TestComponentLifecycle(t *testing.T) {
{{- if not .Tests.SkipShutdown }}
t.Run("shutdown", func(t *testing.T) {
e, err := factory.Create(context.Background(), extensiontest.NewNopSettings(), cfg)
e, err := factory.Create(context.Background(), extensiontest.NewNopSettingsWithType(typ), cfg)
require.NoError(t, err)
err = e.Shutdown(context.Background())
require.NoError(t, err)
@ -421,12 +423,12 @@ func TestComponentLifecycle(t *testing.T) {
{{- if not .Tests.SkipLifecycle }}
t.Run("lifecycle", func(t *testing.T) {
firstExt, err := factory.Create(context.Background(), extensiontest.NewNopSettings(), cfg)
firstExt, err := factory.Create(context.Background(), extensiontest.NewNopSettingsWithType(typ), cfg)
require.NoError(t, err)
require.NoError(t, firstExt.Start(context.Background(), {{ .Tests.Host }}))
require.NoError(t, firstExt.Shutdown(context.Background()))
secondExt, err := factory.Create(context.Background(), extensiontest.NewNopSettings(), cfg)
secondExt, err := factory.Create(context.Background(), extensiontest.NewNopSettingsWithType(typ), cfg)
require.NoError(t, err)
require.NoError(t, secondExt.Start(context.Background(), {{ .Tests.Host }}))
require.NoError(t, secondExt.Shutdown(context.Background()))

View File

@ -18,8 +18,10 @@ import (
"go.opentelemetry.io/collector/pipeline"
)
var typ = component.MustNewType("forward")
func TestComponentFactoryType(t *testing.T) {
require.Equal(t, "forward", NewFactory().Type().String())
require.Equal(t, typ, NewFactory().Type())
}
func TestComponentConfigStruct(t *testing.T) {

View File

@ -20,8 +20,10 @@ import (
"go.opentelemetry.io/collector/pdata/ptrace"
)
var typ = component.MustNewType("debug")
func TestComponentFactoryType(t *testing.T) {
require.Equal(t, "debug", NewFactory().Type().String())
require.Equal(t, typ, NewFactory().Type())
}
func TestComponentConfigStruct(t *testing.T) {

View File

@ -20,8 +20,10 @@ import (
"go.opentelemetry.io/collector/pdata/ptrace"
)
var typ = component.MustNewType("nop")
func TestComponentFactoryType(t *testing.T) {
require.Equal(t, "nop", NewFactory().Type().String())
require.Equal(t, typ, NewFactory().Type())
}
func TestComponentConfigStruct(t *testing.T) {

View File

@ -20,8 +20,10 @@ import (
"go.opentelemetry.io/collector/pdata/ptrace"
)
var typ = component.MustNewType("otlp")
func TestComponentFactoryType(t *testing.T) {
require.Equal(t, "otlp", NewFactory().Type().String())
require.Equal(t, typ, NewFactory().Type())
}
func TestComponentConfigStruct(t *testing.T) {

View File

@ -20,8 +20,10 @@ import (
"go.opentelemetry.io/collector/pdata/ptrace"
)
var typ = component.MustNewType("otlphttp")
func TestComponentFactoryType(t *testing.T) {
require.Equal(t, "otlphttp", NewFactory().Type().String())
require.Equal(t, typ, NewFactory().Type())
}
func TestComponentConfigStruct(t *testing.T) {

View File

@ -32,6 +32,7 @@ type Settings struct {
type CreateFunc func(context.Context, Settings, component.Config) (Extension, error)
// Create implements Factory.Create.
// Deprecated: [v0.120.0] No longer used, will be removed.
func (f CreateFunc) Create(ctx context.Context, set Settings, cfg component.Config) (Extension, error) {
return f(ctx, set, cfg)
}
@ -51,7 +52,7 @@ type Factory interface {
type factory struct {
cfgType component.Type
component.CreateDefaultConfigFunc
CreateFunc
createFunc CreateFunc
extensionStability component.StabilityLevel
}
@ -65,6 +66,14 @@ func (f *factory) Stability() component.StabilityLevel {
return f.extensionStability
}
func (f *factory) Create(ctx context.Context, set Settings, cfg component.Config) (Extension, error) {
if set.ID.Type() != f.cfgType {
return nil, fmt.Errorf("component type mismatch: component ID %q does not have type %q", set.ID, f.cfgType)
}
return f.createFunc(ctx, set, cfg)
}
// NewFactory returns a new Factory based on this configuration.
func NewFactory(
cfgType component.Type,
@ -75,7 +84,7 @@ func NewFactory(
return &factory{
cfgType: cfgType,
CreateDefaultConfigFunc: createDefaultConfig,
CreateFunc: createServiceExtension,
createFunc: createServiceExtension,
extensionStability: sl,
}
}

View File

@ -35,9 +35,12 @@ func TestNewFactory(t *testing.T) {
assert.EqualValues(t, &defaultCfg, factory.CreateDefaultConfig())
assert.Equal(t, component.StabilityLevelDevelopment, factory.Stability())
ext, err := factory.Create(context.Background(), Settings{}, &defaultCfg)
ext, err := factory.Create(context.Background(), Settings{ID: component.NewID(testType)}, &defaultCfg)
require.NoError(t, err)
assert.Same(t, nopExtensionInstance, ext)
_, err = factory.Create(context.Background(), Settings{ID: component.NewID(component.MustNewType("mismatch"))}, &defaultCfg)
require.Error(t, err)
}
func TestMakeFactoryMap(t *testing.T) {

View File

@ -13,12 +13,19 @@ import (
"go.opentelemetry.io/collector/extension"
)
var nopType = component.MustNewType("nop")
// NopType is the type of the nop extension.
var NopType = component.MustNewType("nop")
// NewNopSettings returns a new nop settings for extension.Factory Create* functions.
// Deprecated: [v0.120.0] Use NewNopSettingsWithType(NopType) instead.
func NewNopSettings() extension.Settings {
return NewNopSettingsWithType(NopType)
}
// NewNopSettings returns a new nop settings for extension.Factory Create* functions with the given type.
func NewNopSettingsWithType(ty component.Type) extension.Settings {
return extension.Settings{
ID: component.NewIDWithName(nopType, uuid.NewString()),
ID: component.NewIDWithName(ty, uuid.NewString()),
TelemetrySettings: componenttest.NewNopTelemetrySettings(),
BuildInfo: component.NewDefaultBuildInfo(),
}
@ -27,7 +34,7 @@ func NewNopSettings() extension.Settings {
// NewNopFactory returns an extension.Factory that constructs nop extensions.
func NewNopFactory() extension.Factory {
return extension.NewFactory(
nopType,
NopType,
func() component.Config {
return &nopConfig{}
},

View File

@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/component/componenttest"
"go.opentelemetry.io/collector/extension/extensiontest"
"go.opentelemetry.io/collector/internal/memorylimiter"
@ -37,7 +38,10 @@ func TestCreate(t *testing.T) {
pCfg.MemorySpikeLimitMiB = 1907
pCfg.CheckInterval = 100 * time.Millisecond
tp, err := factory.Create(context.Background(), extensiontest.NewNopSettings(), cfg)
set := extensiontest.NewNopSettings()
set.ID = component.NewID(factory.Type())
tp, err := factory.Create(context.Background(), set, cfg)
require.NoError(t, err)
assert.NotNil(t, tp)
// test if we can shutdown a monitoring routine that has not started

View File

@ -8,13 +8,16 @@ import (
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/component/componenttest"
"go.opentelemetry.io/collector/confmap/confmaptest"
"go.opentelemetry.io/collector/extension/extensiontest"
)
var typ = component.MustNewType("memory_limiter")
func TestComponentFactoryType(t *testing.T) {
require.Equal(t, "memory_limiter", NewFactory().Type().String())
require.Equal(t, typ, NewFactory().Type())
}
func TestComponentConfigStruct(t *testing.T) {
@ -31,12 +34,12 @@ func TestComponentLifecycle(t *testing.T) {
require.NoError(t, err)
require.NoError(t, sub.Unmarshal(&cfg))
t.Run("lifecycle", func(t *testing.T) {
firstExt, err := factory.Create(context.Background(), extensiontest.NewNopSettings(), cfg)
firstExt, err := factory.Create(context.Background(), extensiontest.NewNopSettingsWithType(typ), cfg)
require.NoError(t, err)
require.NoError(t, firstExt.Start(context.Background(), componenttest.NewNopHost()))
require.NoError(t, firstExt.Shutdown(context.Background()))
secondExt, err := factory.Create(context.Background(), extensiontest.NewNopSettings(), cfg)
secondExt, err := factory.Create(context.Background(), extensiontest.NewNopSettingsWithType(typ), cfg)
require.NoError(t, err)
require.NoError(t, secondExt.Start(context.Background(), componenttest.NewNopHost()))
require.NoError(t, secondExt.Shutdown(context.Background()))

View File

@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/component/componenttest"
"go.opentelemetry.io/collector/config/confighttp"
"go.opentelemetry.io/collector/extension/extensiontest"
@ -35,7 +36,9 @@ func TestFactoryCreate(t *testing.T) {
cfg := createDefaultConfig().(*Config)
cfg.ServerConfig.Endpoint = testutil.GetAvailableLocalAddress(t)
ext, err := create(context.Background(), extensiontest.NewNopSettings(), cfg)
set := extensiontest.NewNopSettings()
set.ID = component.NewID(NewFactory().Type())
ext, err := create(context.Background(), set, cfg)
require.NoError(t, err)
require.NotNil(t, ext)
}

View File

@ -8,13 +8,16 @@ import (
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/component/componenttest"
"go.opentelemetry.io/collector/confmap/confmaptest"
"go.opentelemetry.io/collector/extension/extensiontest"
)
var typ = component.MustNewType("zpages")
func TestComponentFactoryType(t *testing.T) {
require.Equal(t, "zpages", NewFactory().Type().String())
require.Equal(t, typ, NewFactory().Type())
}
func TestComponentConfigStruct(t *testing.T) {
@ -31,18 +34,18 @@ func TestComponentLifecycle(t *testing.T) {
require.NoError(t, err)
require.NoError(t, sub.Unmarshal(&cfg))
t.Run("shutdown", func(t *testing.T) {
e, err := factory.Create(context.Background(), extensiontest.NewNopSettings(), cfg)
e, err := factory.Create(context.Background(), extensiontest.NewNopSettingsWithType(typ), cfg)
require.NoError(t, err)
err = e.Shutdown(context.Background())
require.NoError(t, err)
})
t.Run("lifecycle", func(t *testing.T) {
firstExt, err := factory.Create(context.Background(), extensiontest.NewNopSettings(), cfg)
firstExt, err := factory.Create(context.Background(), extensiontest.NewNopSettingsWithType(typ), cfg)
require.NoError(t, err)
require.NoError(t, firstExt.Start(context.Background(), componenttest.NewNopHost()))
require.NoError(t, firstExt.Shutdown(context.Background()))
secondExt, err := factory.Create(context.Background(), extensiontest.NewNopSettings(), cfg)
secondExt, err := factory.Create(context.Background(), extensiontest.NewNopSettingsWithType(typ), cfg)
require.NoError(t, err)
require.NoError(t, secondExt.Start(context.Background(), componenttest.NewNopHost()))
require.NoError(t, secondExt.Shutdown(context.Background()))

View File

@ -21,8 +21,10 @@ import (
"go.opentelemetry.io/collector/processor/processortest"
)
var typ = component.MustNewType("batch")
func TestComponentFactoryType(t *testing.T) {
require.Equal(t, "batch", NewFactory().Type().String())
require.Equal(t, typ, NewFactory().Type())
}
func TestComponentConfigStruct(t *testing.T) {

View File

@ -21,8 +21,10 @@ import (
"go.opentelemetry.io/collector/processor/processortest"
)
var typ = component.MustNewType("memory_limiter")
func TestComponentFactoryType(t *testing.T) {
require.Equal(t, "memory_limiter", NewFactory().Type().String())
require.Equal(t, typ, NewFactory().Type())
}
func TestComponentConfigStruct(t *testing.T) {

View File

@ -16,8 +16,10 @@ import (
"go.opentelemetry.io/collector/receiver/receivertest"
)
var typ = component.MustNewType("nop")
func TestComponentFactoryType(t *testing.T) {
require.Equal(t, "nop", NewFactory().Type().String())
require.Equal(t, typ, NewFactory().Type())
}
func TestComponentConfigStruct(t *testing.T) {

View File

@ -16,8 +16,10 @@ import (
"go.opentelemetry.io/collector/receiver/receivertest"
)
var typ = component.MustNewType("otlp")
func TestComponentFactoryType(t *testing.T) {
require.Equal(t, "otlp", NewFactory().Type().String())
require.Equal(t, typ, NewFactory().Type())
}
func TestComponentConfigStruct(t *testing.T) {