diff --git a/.chloggen/remove_ReportFatalError.yaml b/.chloggen/remove_ReportFatalError.yaml new file mode 100755 index 00000000000..54f69253e42 --- /dev/null +++ b/.chloggen/remove_ReportFatalError.yaml @@ -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: breaking + +# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver) +component: component + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Remove `host.ReportFatalError` + +# One or more tracking issues or pull requests related to the change +issues: [6344] + +# (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] \ No newline at end of file diff --git a/component/componenttest/nop_host.go b/component/componenttest/nop_host.go index 4accfab0d8c..f0ae4788dfe 100644 --- a/component/componenttest/nop_host.go +++ b/component/componenttest/nop_host.go @@ -15,8 +15,6 @@ func NewNopHost() component.Host { return &nopHost{} } -func (nh *nopHost) ReportFatalError(_ error) {} - func (nh *nopHost) GetFactory(_ component.Kind, _ component.Type) component.Factory { return nil } diff --git a/component/componenttest/nop_host_test.go b/component/componenttest/nop_host_test.go index 99d323681ca..4aada24b1be 100644 --- a/component/componenttest/nop_host_test.go +++ b/component/componenttest/nop_host_test.go @@ -4,7 +4,6 @@ package componenttest import ( - "errors" "testing" "github.com/stretchr/testify/assert" @@ -18,7 +17,6 @@ func TestNewNopHost(t *testing.T) { require.NotNil(t, nh) require.IsType(t, &nopHost{}, nh) - nh.ReportFatalError(errors.New("TestError")) assert.Nil(t, nh.GetExporters()) // nolint: staticcheck assert.Nil(t, nh.GetExtensions()) assert.Nil(t, nh.GetFactory(component.KindReceiver, component.MustNewType("test"))) diff --git a/component/host.go b/component/host.go index 732e37c8c44..4b6933baaef 100644 --- a/component/host.go +++ b/component/host.go @@ -6,16 +6,6 @@ package component // import "go.opentelemetry.io/collector/component" // Host represents the entity that is hosting a Component. It is used to allow communication // between the Component and its host (normally the service.Collector is the host). type Host interface { - // ReportFatalError is used to report to the host that the component - // encountered a fatal error (i.e.: an error that the instance can't recover - // from) after its start function had already returned. - // - // ReportFatalError should be called by the component anytime after Component.Start() ends and - // before Component.Shutdown() begins. - // Deprecated: [0.87.0] Use TelemetrySettings.ReportComponentStatus instead (with an event - // component.StatusFatalError) - ReportFatalError(err error) - // GetFactory of the specified kind. Returns the factory for a component type. // This allows components to create other components. For example: // func (r MyReceiver) Start(host component.Host) error { diff --git a/exporter/otlpexporter/config.go b/exporter/otlpexporter/config.go index a657fa684c9..61440c3a9e3 100644 --- a/exporter/otlpexporter/config.go +++ b/exporter/otlpexporter/config.go @@ -4,6 +4,8 @@ package otlpexporter // import "go.opentelemetry.io/collector/exporter/otlpexporter" import ( + "errors" + "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/config/configgrpc" "go.opentelemetry.io/collector/config/configretry" @@ -19,4 +21,11 @@ type Config struct { configgrpc.ClientConfig `mapstructure:",squash"` // squash ensures fields are correctly decoded in embedded struct. } +func (c *Config) Validate() error { + if c.SanitizedEndpoint() == "" { + return errors.New(`requires a non-empty "endpoint"`) + } + return nil +} + var _ component.Config = (*Config)(nil) diff --git a/exporter/otlpexporter/config_test.go b/exporter/otlpexporter/config_test.go index ae2fac324d8..f65ce61c27e 100644 --- a/exporter/otlpexporter/config_test.go +++ b/exporter/otlpexporter/config_test.go @@ -78,3 +78,39 @@ func TestUnmarshalConfig(t *testing.T) { }, }, cfg) } + +func TestUnmarshalInvalidConfig(t *testing.T) { + cm, err := confmaptest.LoadConf(filepath.Join("testdata", "invalid_configs.yaml")) + require.NoError(t, err) + factory := NewFactory() + for _, test := range []struct { + name string + errorMsg string + }{ + { + name: "no_endpoint", + errorMsg: `requires a non-empty "endpoint"`, + }, + { + name: "http_endpoint", + errorMsg: `requires a non-empty "endpoint"`, + }, + { + name: "invalid_timeout", + errorMsg: `'timeout' must be non-negative`, + }, + { + name: "invalid_retry", + errorMsg: `'randomization_factor' must be within [0, 1]`, + }, + } { + t.Run(test.name, func(t *testing.T) { + cfg := factory.CreateDefaultConfig() + sub, err := cm.Sub(test.name) + require.NoError(t, err) + assert.NoError(t, component.UnmarshalConfig(sub, cfg)) + assert.ErrorContains(t, component.ValidateConfig(cfg), test.errorMsg) + }) + } + +} diff --git a/exporter/otlpexporter/factory.go b/exporter/otlpexporter/factory.go index dfb06073d93..8a72aab8cdc 100644 --- a/exporter/otlpexporter/factory.go +++ b/exporter/otlpexporter/factory.go @@ -48,10 +48,7 @@ func createTracesExporter( set exporter.CreateSettings, cfg component.Config, ) (exporter.Traces, error) { - oce, err := newExporter(cfg, set) - if err != nil { - return nil, err - } + oce := newExporter(cfg, set) oCfg := cfg.(*Config) return exporterhelper.NewTracesExporter(ctx, set, cfg, oce.pushTraces, @@ -68,10 +65,7 @@ func createMetricsExporter( set exporter.CreateSettings, cfg component.Config, ) (exporter.Metrics, error) { - oce, err := newExporter(cfg, set) - if err != nil { - return nil, err - } + oce := newExporter(cfg, set) oCfg := cfg.(*Config) return exporterhelper.NewMetricsExporter(ctx, set, cfg, oce.pushMetrics, @@ -89,10 +83,7 @@ func createLogsExporter( set exporter.CreateSettings, cfg component.Config, ) (exporter.Logs, error) { - oce, err := newExporter(cfg, set) - if err != nil { - return nil, err - } + oce := newExporter(cfg, set) oCfg := cfg.(*Config) return exporterhelper.NewLogsExporter(ctx, set, cfg, oce.pushLogs, diff --git a/exporter/otlpexporter/factory_test.go b/exporter/otlpexporter/factory_test.go index a3232a4094b..e601361c0c1 100644 --- a/exporter/otlpexporter/factory_test.go +++ b/exporter/otlpexporter/factory_test.go @@ -50,20 +50,10 @@ func TestCreateMetricsExporter(t *testing.T) { func TestCreateTracesExporter(t *testing.T) { endpoint := testutil.GetAvailableLocalAddress(t) tests := []struct { - name string - config *Config - mustFailOnCreate bool - mustFailOnStart bool + name string + config *Config + mustFailOnStart bool }{ - { - name: "NoEndpoint", - config: &Config{ - ClientConfig: configgrpc.ClientConfig{ - Endpoint: "", - }, - }, - mustFailOnCreate: true, - }, { name: "UseSecure", config: &Config{ @@ -178,10 +168,6 @@ func TestCreateTracesExporter(t *testing.T) { factory := NewFactory() set := exportertest.NewNopCreateSettings() consumer, err := factory.CreateTracesExporter(context.Background(), set, tt.config) - if tt.mustFailOnCreate { - assert.NotNil(t, err) - return - } assert.NoError(t, err) assert.NotNil(t, consumer) err = consumer.Start(context.Background(), componenttest.NewNopHost()) diff --git a/exporter/otlpexporter/otlp.go b/exporter/otlpexporter/otlp.go index 495213825b5..21864d7723a 100644 --- a/exporter/otlpexporter/otlp.go +++ b/exporter/otlpexporter/otlp.go @@ -5,7 +5,6 @@ package otlpexporter // import "go.opentelemetry.io/collector/exporter/otlpexpor import ( "context" - "errors" "fmt" "runtime" "time" @@ -47,17 +46,13 @@ type baseExporter struct { userAgent string } -func newExporter(cfg component.Config, set exporter.CreateSettings) (*baseExporter, error) { +func newExporter(cfg component.Config, set exporter.CreateSettings) *baseExporter { oCfg := cfg.(*Config) - if oCfg.Endpoint == "" { - return nil, errors.New("OTLP exporter config requires an Endpoint") - } - userAgent := fmt.Sprintf("%s/%s (%s/%s)", set.BuildInfo.Description, set.BuildInfo.Version, runtime.GOOS, runtime.GOARCH) - return &baseExporter{config: oCfg, settings: set.TelemetrySettings, userAgent: userAgent}, nil + return &baseExporter{config: oCfg, settings: set.TelemetrySettings, userAgent: userAgent} } // start actually creates the gRPC connection. The client construction is deferred till this point as this diff --git a/exporter/otlpexporter/testdata/invalid_configs.yaml b/exporter/otlpexporter/testdata/invalid_configs.yaml new file mode 100644 index 00000000000..73c24e3f13e --- /dev/null +++ b/exporter/otlpexporter/testdata/invalid_configs.yaml @@ -0,0 +1,55 @@ +no_endpoint: + timeout: 10s + sending_queue: + enabled: true + num_consumers: 2 + queue_size: 10 + retry_on_failure: + enabled: true + initial_interval: 10s + randomization_factor: 0.7 + multiplier: 1.3 + max_interval: 60s + max_elapsed_time: 10m +http_endpoint: + endpoint: http:// + timeout: 10s + sending_queue: + enabled: true + num_consumers: 2 + queue_size: 10 + retry_on_failure: + enabled: true + initial_interval: 10s + randomization_factor: 0.7 + multiplier: 1.3 + max_interval: 60s + max_elapsed_time: 10m +invalid_timeout: + endpoint: example.com:443 + timeout: -5s + sending_queue: + enabled: true + num_consumers: 2 + queue_size: 10 + retry_on_failure: + enabled: true + initial_interval: 10s + randomization_factor: 0.7 + multiplier: 1.3 + max_interval: 60s + max_elapsed_time: 10m +invalid_retry: + endpoint: example.com:443 + timeout: 30s + sending_queue: + enabled: true + num_consumers: 2 + queue_size: 10 + retry_on_failure: + enabled: true + initial_interval: 10s + randomization_factor: -5 + multiplier: 1.3 + max_interval: 60s + max_elapsed_time: 10m diff --git a/extension/auth/client.go b/extension/auth/client.go index cf1bdb55fb8..d1855d8aece 100644 --- a/extension/auth/client.go +++ b/extension/auth/client.go @@ -25,7 +25,7 @@ type Client interface { PerRPCCredentials() (credentials.PerRPCCredentials, error) } -// ClientOption represents the possible options for NewServerAuthenticator. +// ClientOption represents the possible options for NewClient. type ClientOption func(*defaultClient) // ClientRoundTripperFunc specifies the function that returns a RoundTripper that can be used to authenticate HTTP requests. diff --git a/extension/extensiontest/nop_extension.go b/extension/extensiontest/nop_extension.go index fcb9ab001f7..f92ff388d94 100644 --- a/extension/extensiontest/nop_extension.go +++ b/extension/extensiontest/nop_extension.go @@ -39,13 +39,13 @@ type nopConfig struct{} var nopInstance = &nopExtension{} -// nopExtension stores consumed traces and metrics for testing purposes. +// nopExtension acts as an extension for testing purposes. type nopExtension struct { component.StartFunc component.ShutdownFunc } -// NewNopBuilder returns a extension.Builder that constructs nop receivers. +// NewNopBuilder returns a extension.Builder that constructs nop extension. func NewNopBuilder() *extension.Builder { nopFactory := NewNopFactory() return extension.NewBuilder( diff --git a/processor/processortest/nop_processor.go b/processor/processortest/nop_processor.go index dad0a9f0566..0651bc5b6ba 100644 --- a/processor/processortest/nop_processor.go +++ b/processor/processortest/nop_processor.go @@ -15,7 +15,7 @@ import ( var nopType = component.MustNewType("nop") -// NewNopCreateSettings returns a new nop settings for Create* functions. +// NewNopCreateSettings returns a new nop settings for Create*Processor functions. func NewNopCreateSettings() processor.CreateSettings { return processor.CreateSettings{ ID: component.NewID(nopType), @@ -53,14 +53,14 @@ var nopInstance = &nopProcessor{ Consumer: consumertest.NewNop(), } -// nopProcessor stores consumed traces and metrics for testing purposes. +// nopProcessor acts as a processor for testing purposes. type nopProcessor struct { component.StartFunc component.ShutdownFunc consumertest.Consumer } -// NewNopBuilder returns a processor.Builder that constructs nop receivers. +// NewNopBuilder returns a processor.Builder that constructs nop processors. func NewNopBuilder() *processor.Builder { nopFactory := NewNopFactory() return processor.NewBuilder( diff --git a/receiver/receivertest/nop_receiver.go b/receiver/receivertest/nop_receiver.go index 4c5565e6019..0c7cee87cf3 100644 --- a/receiver/receivertest/nop_receiver.go +++ b/receiver/receivertest/nop_receiver.go @@ -14,7 +14,7 @@ import ( var componentType = component.MustNewType("nop") -// NewNopCreateSettings returns a new nop settings for Create* functions. +// NewNopCreateSettings returns a new nop settings for Create*Receiver functions. func NewNopCreateSettings() receiver.CreateSettings { return receiver.CreateSettings{ ID: component.NewID(componentType), @@ -49,7 +49,7 @@ type nopConfig struct{} var nopInstance = &nopReceiver{} -// nopReceiver stores consumed traces and metrics for testing purposes. +// nopReceiver acts as a receiver for testing purposes. type nopReceiver struct { component.StartFunc component.ShutdownFunc diff --git a/service/host.go b/service/host.go index b749564b0dd..870e0bfc446 100644 --- a/service/host.go +++ b/service/host.go @@ -30,13 +30,6 @@ type serviceHost struct { serviceExtensions *extensions.Extensions } -// ReportFatalError is used to report to the host that the receiver encountered -// a fatal error (i.e.: an error that the instance can't recover from) after -// its start function has already returned. -// Deprecated: [0.87.0] Replaced by servicetelemetry.Settings.ReportComponentStatus -func (host *serviceHost) ReportFatalError(err error) { - host.asyncErrorChannel <- err -} func (host *serviceHost) GetFactory(kind component.Kind, componentType component.Type) component.Factory { switch kind { case component.KindReceiver: