diff --git a/.chloggen/component-remove-reporting-from-struct-2-2.yaml b/.chloggen/component-remove-reporting-from-struct-2-2.yaml new file mode 100644 index 00000000000..56479c00f18 --- /dev/null +++ b/.chloggen/component-remove-reporting-from-struct-2-2.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: extensions + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Remove `StatusWatcher` interface. Use `componentstatus.Watcher` instead. + +# One or more tracking issues or pull requests related to the change +issues: [10777] + +# (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] diff --git a/.chloggen/component-remove-reporting-from-struct-2-3.yaml b/.chloggen/component-remove-reporting-from-struct-2-3.yaml new file mode 100644 index 00000000000..4af073a52da --- /dev/null +++ b/.chloggen/component-remove-reporting-from-struct-2-3.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: Removed Status related types and functions. Use `componentstatus` instead. + +# One or more tracking issues or pull requests related to the change +issues: [10777] + +# (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] diff --git a/.chloggen/component-remove-reporting-from-struct-2.yaml b/.chloggen/component-remove-reporting-from-struct-2.yaml new file mode 100644 index 00000000000..7151d58f36f --- /dev/null +++ b/.chloggen/component-remove-reporting-from-struct-2.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 `ReportStatus` from `TelemetrySettings`. Use `componentstatus.ReportStatus` instead. + +# One or more tracking issues or pull requests related to the change +issues: [10777] + +# (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] diff --git a/cmd/builder/internal/builder/main_test.go b/cmd/builder/internal/builder/main_test.go index d2a5d47ca85..146c58b1d17 100644 --- a/cmd/builder/internal/builder/main_test.go +++ b/cmd/builder/internal/builder/main_test.go @@ -42,6 +42,7 @@ var ( "", "/component", "/component/componentprofiles", + "/component/componentstatus", "/client", "/config/configauth", "/config/configcompression", diff --git a/cmd/builder/test/core.builder.yaml b/cmd/builder/test/core.builder.yaml index c63aa468b78..3de6aca0f30 100644 --- a/cmd/builder/test/core.builder.yaml +++ b/cmd/builder/test/core.builder.yaml @@ -22,6 +22,7 @@ replaces: - go.opentelemetry.io/collector/client => ${WORKSPACE_DIR}/client - go.opentelemetry.io/collector/component => ${WORKSPACE_DIR}/component - go.opentelemetry.io/collector/component/componentprofiles => ${WORKSPACE_DIR}/component/componentprofiles + - go.opentelemetry.io/collector/component/componentstatus => ${WORKSPACE_DIR}/component/componentstatus - go.opentelemetry.io/collector/config/configauth => ${WORKSPACE_DIR}/config/configauth - go.opentelemetry.io/collector/config/configcompression => ${WORKSPACE_DIR}/config/configcompression - go.opentelemetry.io/collector/config/configgrpc => ${WORKSPACE_DIR}/config/configgrpc diff --git a/cmd/mdatagen/go.mod b/cmd/mdatagen/go.mod index 07cd23b4a57..0fa5a171c5c 100644 --- a/cmd/mdatagen/go.mod +++ b/cmd/mdatagen/go.mod @@ -100,3 +100,5 @@ replace go.opentelemetry.io/collector/internal/globalgates => ../../internal/glo replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/cmd/otelcorecol/builder-config.yaml b/cmd/otelcorecol/builder-config.yaml index ca58598d055..a1c8fa02157 100644 --- a/cmd/otelcorecol/builder-config.yaml +++ b/cmd/otelcorecol/builder-config.yaml @@ -46,6 +46,7 @@ replaces: - go.opentelemetry.io/collector/otelcol => ../../otelcol - go.opentelemetry.io/collector/component => ../../component - go.opentelemetry.io/collector/component/componentprofiles => ../../component/componentprofiles + - go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus - go.opentelemetry.io/collector/config/configauth => ../../config/configauth - go.opentelemetry.io/collector/config/configcompression => ../../config/configcompression - go.opentelemetry.io/collector/config/configgrpc => ../../config/configgrpc diff --git a/cmd/otelcorecol/go.mod b/cmd/otelcorecol/go.mod index 58b26110409..1241ea4c6dc 100644 --- a/cmd/otelcorecol/go.mod +++ b/cmd/otelcorecol/go.mod @@ -80,6 +80,7 @@ require ( go.opentelemetry.io/collector v0.107.0 // indirect go.opentelemetry.io/collector/client v1.13.0 // indirect go.opentelemetry.io/collector/component/componentprofiles v0.107.0 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.107.0 // indirect go.opentelemetry.io/collector/config/configauth v0.107.0 // indirect go.opentelemetry.io/collector/config/configcompression v1.13.0 // indirect go.opentelemetry.io/collector/config/configgrpc v0.107.0 // indirect @@ -147,6 +148,8 @@ replace go.opentelemetry.io/collector/component => ../../component replace go.opentelemetry.io/collector/component/componentprofiles => ../../component/componentprofiles +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus + replace go.opentelemetry.io/collector/config/configauth => ../../config/configauth replace go.opentelemetry.io/collector/config/configcompression => ../../config/configcompression diff --git a/component/component.go b/component/component.go index f5f68b57290..bd1ff210d4b 100644 --- a/component/component.go +++ b/component/component.go @@ -189,10 +189,3 @@ type CreateDefaultConfigFunc func() Config func (f CreateDefaultConfigFunc) CreateDefaultConfig() Config { return f() } - -// InstanceID uniquely identifies a component instance -type InstanceID struct { - ID ID - Kind Kind - PipelineIDs map[ID]struct{} -} diff --git a/component/componenttest/nop_telemetry.go b/component/componenttest/nop_telemetry.go index 171e9daa47e..0324f65c980 100644 --- a/component/componenttest/nop_telemetry.go +++ b/component/componenttest/nop_telemetry.go @@ -21,7 +21,5 @@ func NewNopTelemetrySettings() component.TelemetrySettings { MeterProvider: noopmetric.NewMeterProvider(), MetricsLevel: configtelemetry.LevelNone, Resource: pcommon.NewResource(), - ReportStatus: func(*component.StatusEvent) { - }, } } diff --git a/component/status.go b/component/status.go deleted file mode 100644 index 60894a217ca..00000000000 --- a/component/status.go +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package component // import "go.opentelemetry.io/collector/component" - -import ( - "time" -) - -type Status int32 - -// Enumeration of possible component statuses -const ( - // StatusNone indicates absence of component status. - StatusNone Status = iota - // StatusStarting indicates the component is starting. - StatusStarting - // StatusOK indicates the component is running without issues. - StatusOK - // StatusRecoverableError indicates that the component has experienced a transient error and may recover. - StatusRecoverableError - // StatusPermanentError indicates that the component has detected a condition at runtime that will need human intervention to fix. The collector will continue to run in a degraded mode. - StatusPermanentError - // StatusFatalError indicates that the collector has experienced a fatal runtime error and will shut down. - StatusFatalError - // StatusStopping indicates that the component is in the process of shutting down. - StatusStopping - // StatusStopped indicates that the component has completed shutdown. - StatusStopped -) - -// String returns a string representation of a Status -func (s Status) String() string { - switch s { - case StatusStarting: - return "StatusStarting" - case StatusOK: - return "StatusOK" - case StatusRecoverableError: - return "StatusRecoverableError" - case StatusPermanentError: - return "StatusPermanentError" - case StatusFatalError: - return "StatusFatalError" - case StatusStopping: - return "StatusStopping" - case StatusStopped: - return "StatusStopped" - } - return "StatusNone" -} - -// StatusEvent contains a status and timestamp, and can contain an error -type StatusEvent struct { - status Status - err error - timestamp time.Time -} - -// Status returns the Status (enum) associated with the StatusEvent -func (ev *StatusEvent) Status() Status { - return ev.status -} - -// Err returns the error associated with the StatusEvent. -func (ev *StatusEvent) Err() error { - return ev.err -} - -// Timestamp returns the timestamp associated with the StatusEvent -func (ev *StatusEvent) Timestamp() time.Time { - return ev.timestamp -} - -// NewStatusEvent creates and returns a StatusEvent with the specified status and sets the timestamp -// time.Now(). To set an error on the event for an error status use one of the dedicated -// constructors (e.g. NewRecoverableErrorEvent, NewPermanentErrorEvent, NewFatalErrorEvent) -func NewStatusEvent(status Status) *StatusEvent { - return &StatusEvent{ - status: status, - timestamp: time.Now(), - } -} - -// NewRecoverableErrorEvent wraps a transient error -// passed as argument as a StatusEvent with a status StatusRecoverableError -// and a timestamp set to time.Now(). -func NewRecoverableErrorEvent(err error) *StatusEvent { - ev := NewStatusEvent(StatusRecoverableError) - ev.err = err - return ev -} - -// NewPermanentErrorEvent wraps an error requiring human intervention to fix -// passed as argument as a StatusEvent with a status StatusPermanentError -// and a timestamp set to time.Now(). -func NewPermanentErrorEvent(err error) *StatusEvent { - ev := NewStatusEvent(StatusPermanentError) - ev.err = err - return ev -} - -// NewFatalErrorEvent wraps the fatal runtime error passed as argument as a StatusEvent -// with a status StatusFatalError and a timestamp set to time.Now(). -func NewFatalErrorEvent(err error) *StatusEvent { - ev := NewStatusEvent(StatusFatalError) - ev.err = err - return ev -} - -// AggregateStatus will derive a status for the given input using the following rules in order: -// 1. If all instances have the same status, there is nothing to aggregate, return it. -// 2. If any instance encounters a fatal error, the component is in a Fatal Error state. -// 3. If any instance is in a Permanent Error state, the component status is Permanent Error. -// 4. If any instance is Stopping, the component is in a Stopping state. -// 5. An instance is Stopped, but not all instances are Stopped, we must be in the process of Stopping the component. -// 6. If any instance is in a Recoverable Error state, the component status is Recoverable Error. -// 7. By process of elimination, the only remaining state is starting. -func AggregateStatus[K comparable](eventMap map[K]*StatusEvent) Status { - seen := make(map[Status]struct{}) - for _, ev := range eventMap { - seen[ev.Status()] = struct{}{} - } - - // All statuses are the same. Note, this will handle StatusOK and StatusStopped as these two - // cases require all components be in the same state. - if len(seen) == 1 { - for st := range seen { - return st - } - } - - // Handle mixed status cases - if _, isFatal := seen[StatusFatalError]; isFatal { - return StatusFatalError - } - - if _, isPermanent := seen[StatusPermanentError]; isPermanent { - return StatusPermanentError - } - - if _, isStopping := seen[StatusStopping]; isStopping { - return StatusStopping - } - - if _, isStopped := seen[StatusStopped]; isStopped { - return StatusStopping - } - - if _, isRecoverable := seen[StatusRecoverableError]; isRecoverable { - return StatusRecoverableError - } - - // By process of elimination, this is the last possible status; no check necessary. - return StatusStarting -} - -// StatusIsError returns true for error statuses (e.g. StatusRecoverableError, -// StatusPermanentError, or StatusFatalError) -func StatusIsError(status Status) bool { - return status == StatusRecoverableError || - status == StatusPermanentError || - status == StatusFatalError -} - -// AggregateStatusEvent returns a status event where: -// - The status is set to the aggregate status of the events in the eventMap -// - The timestamp is set to the latest timestamp of the events in the eventMap -// - For an error status, the event will have same error as the most current event of the same -// error type from the eventMap -func AggregateStatusEvent[K comparable](eventMap map[K]*StatusEvent) *StatusEvent { - var lastEvent, lastMatchingEvent *StatusEvent - aggregateStatus := AggregateStatus[K](eventMap) - - for _, ev := range eventMap { - if lastEvent == nil || lastEvent.timestamp.Before(ev.timestamp) { - lastEvent = ev - } - if aggregateStatus == ev.Status() && - (lastMatchingEvent == nil || lastMatchingEvent.timestamp.Before(ev.timestamp)) { - lastMatchingEvent = ev - } - } - - // the effective status matches an existing event - if lastEvent.Status() == aggregateStatus { - return lastEvent - } - - // the effective status requires a synthetic event - aggregateEvent := &StatusEvent{ - status: aggregateStatus, - timestamp: lastEvent.timestamp, - } - if StatusIsError(aggregateStatus) { - aggregateEvent.err = lastMatchingEvent.err - } - - return aggregateEvent -} diff --git a/component/status_test.go b/component/status_test.go deleted file mode 100644 index 13755d078a5..00000000000 --- a/component/status_test.go +++ /dev/null @@ -1,330 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 -package component - -import ( - "fmt" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestNewStatusEvent(t *testing.T) { - statuses := []Status{ - StatusStarting, - StatusOK, - StatusRecoverableError, - StatusPermanentError, - StatusFatalError, - StatusStopping, - StatusStopped, - } - - for _, status := range statuses { - t.Run(fmt.Sprintf("%s without error", status), func(t *testing.T) { - ev := NewStatusEvent(status) - require.Equal(t, status, ev.Status()) - require.Nil(t, ev.Err()) - require.False(t, ev.Timestamp().IsZero()) - }) - } -} - -func TestStatusEventsWithError(t *testing.T) { - statusConstructorMap := map[Status]func(error) *StatusEvent{ - StatusRecoverableError: NewRecoverableErrorEvent, - StatusPermanentError: NewPermanentErrorEvent, - StatusFatalError: NewFatalErrorEvent, - } - - for status, newEvent := range statusConstructorMap { - t.Run(fmt.Sprintf("error status constructor for: %s", status), func(t *testing.T) { - ev := newEvent(assert.AnError) - require.Equal(t, status, ev.Status()) - require.Equal(t, assert.AnError, ev.Err()) - require.False(t, ev.Timestamp().IsZero()) - }) - } -} - -func TestAggregateStatus(t *testing.T) { - for _, tc := range []struct { - name string - statusMap map[*InstanceID]*StatusEvent - expectedStatus Status - }{ - { - name: "aggregate status with fatal is FatalError", - statusMap: map[*InstanceID]*StatusEvent{ - {}: NewStatusEvent(StatusStarting), - {}: NewStatusEvent(StatusOK), - {}: NewStatusEvent(StatusFatalError), - {}: NewStatusEvent(StatusRecoverableError), - }, - expectedStatus: StatusFatalError, - }, - { - name: "aggregate status with permanent is PermanentError", - statusMap: map[*InstanceID]*StatusEvent{ - {}: NewStatusEvent(StatusStarting), - {}: NewStatusEvent(StatusOK), - {}: NewStatusEvent(StatusPermanentError), - {}: NewStatusEvent(StatusRecoverableError), - }, - expectedStatus: StatusPermanentError, - }, - { - name: "aggregate status with stopping is Stopping", - statusMap: map[*InstanceID]*StatusEvent{ - {}: NewStatusEvent(StatusStarting), - {}: NewStatusEvent(StatusOK), - {}: NewStatusEvent(StatusRecoverableError), - {}: NewStatusEvent(StatusStopping), - }, - expectedStatus: StatusStopping, - }, - { - name: "aggregate status with stopped and non-stopped is Stopping", - statusMap: map[*InstanceID]*StatusEvent{ - {}: NewStatusEvent(StatusStarting), - {}: NewStatusEvent(StatusOK), - {}: NewStatusEvent(StatusRecoverableError), - {}: NewStatusEvent(StatusStopped), - }, - expectedStatus: StatusStopping, - }, - { - name: "aggregate status with all stopped is Stopped", - statusMap: map[*InstanceID]*StatusEvent{ - {}: NewStatusEvent(StatusStopped), - {}: NewStatusEvent(StatusStopped), - {}: NewStatusEvent(StatusStopped), - }, - expectedStatus: StatusStopped, - }, - { - name: "aggregate status with recoverable is RecoverableError", - statusMap: map[*InstanceID]*StatusEvent{ - {}: NewStatusEvent(StatusStarting), - {}: NewStatusEvent(StatusOK), - {}: NewStatusEvent(StatusRecoverableError), - }, - expectedStatus: StatusRecoverableError, - }, - { - name: "aggregate status with starting is Starting", - statusMap: map[*InstanceID]*StatusEvent{ - {}: NewStatusEvent(StatusStarting), - {}: NewStatusEvent(StatusOK), - }, - expectedStatus: StatusStarting, - }, - { - name: "aggregate status with all ok is OK", - statusMap: map[*InstanceID]*StatusEvent{ - {}: NewStatusEvent(StatusOK), - {}: NewStatusEvent(StatusOK), - {}: NewStatusEvent(StatusOK), - }, - expectedStatus: StatusOK, - }, - } { - t.Run(tc.name, func(t *testing.T) { - assert.Equal(t, tc.expectedStatus, AggregateStatus(tc.statusMap)) - }) - } -} - -func TestStatusIsError(t *testing.T) { - for _, tc := range []struct { - status Status - isError bool - }{ - { - status: StatusStarting, - isError: false, - }, - { - status: StatusOK, - isError: false, - }, - { - status: StatusRecoverableError, - isError: true, - }, - { - status: StatusPermanentError, - isError: true, - }, - { - status: StatusFatalError, - isError: true, - }, - { - status: StatusStopping, - isError: false, - }, - { - status: StatusStopped, - isError: false, - }, - } { - name := fmt.Sprintf("StatusIsError(%s) is %t", tc.status, tc.isError) - t.Run(name, func(t *testing.T) { - assert.Equal(t, tc.isError, StatusIsError(tc.status)) - }) - } -} - -func TestAggregateStatusEvent(t *testing.T) { - // maxTime is used to make sure we select the event with the latest timestamp - maxTime := time.Unix(1<<63-62135596801, 999999999) - // latest sets the timestamp for an event to maxTime - latest := func(ev *StatusEvent) *StatusEvent { - ev.timestamp = maxTime - return ev - } - - for _, tc := range []struct { - name string - statusMap map[*InstanceID]*StatusEvent - expectedStatus *StatusEvent - }{ - { - name: "FatalError - existing event", - statusMap: map[*InstanceID]*StatusEvent{ - {}: NewStatusEvent(StatusStarting), - {}: NewStatusEvent(StatusOK), - {}: latest(NewFatalErrorEvent(assert.AnError)), - {}: NewStatusEvent(StatusRecoverableError), - }, - expectedStatus: &StatusEvent{ - status: StatusFatalError, - timestamp: maxTime, - err: assert.AnError, - }, - }, - { - name: "FatalError - synthetic event", - statusMap: map[*InstanceID]*StatusEvent{ - {}: NewStatusEvent(StatusStarting), - {}: NewStatusEvent(StatusOK), - {}: NewFatalErrorEvent(assert.AnError), - {}: latest(NewStatusEvent(StatusRecoverableError)), - }, - expectedStatus: &StatusEvent{ - status: StatusFatalError, - timestamp: maxTime, - err: assert.AnError, - }, - }, - { - name: "PermanentError - existing event", - statusMap: map[*InstanceID]*StatusEvent{ - {}: NewStatusEvent(StatusStarting), - {}: NewStatusEvent(StatusOK), - {}: latest(NewPermanentErrorEvent(assert.AnError)), - {}: NewStatusEvent(StatusRecoverableError), - }, - expectedStatus: &StatusEvent{ - status: StatusPermanentError, - timestamp: maxTime, - err: assert.AnError, - }, - }, - { - name: "PermanentError - synthetic event", - statusMap: map[*InstanceID]*StatusEvent{ - {}: NewStatusEvent(StatusStarting), - {}: NewStatusEvent(StatusOK), - {}: NewPermanentErrorEvent(assert.AnError), - {}: latest(NewStatusEvent(StatusRecoverableError)), - }, - expectedStatus: &StatusEvent{ - status: StatusPermanentError, - timestamp: maxTime, - err: assert.AnError, - }, - }, - { - name: "Stopping - existing event", - statusMap: map[*InstanceID]*StatusEvent{ - {}: NewStatusEvent(StatusStarting), - {}: NewStatusEvent(StatusOK), - {}: NewStatusEvent(StatusRecoverableError), - {}: latest(NewStatusEvent(StatusStopping)), - }, - expectedStatus: &StatusEvent{ - status: StatusStopping, - timestamp: maxTime, - }, - }, - { - name: "Stopping - synthetic event", - statusMap: map[*InstanceID]*StatusEvent{ - {}: NewStatusEvent(StatusStarting), - {}: NewStatusEvent(StatusOK), - {}: NewStatusEvent(StatusRecoverableError), - {}: latest(NewStatusEvent(StatusStopped)), - }, - expectedStatus: &StatusEvent{ - status: StatusStopping, - timestamp: maxTime, - }, - }, - { - name: "Stopped - existing event", - statusMap: map[*InstanceID]*StatusEvent{ - {}: NewStatusEvent(StatusStopped), - {}: latest(NewStatusEvent(StatusStopped)), - {}: NewStatusEvent(StatusStopped), - }, - expectedStatus: &StatusEvent{ - status: StatusStopped, - timestamp: maxTime, - }, - }, - { - name: "RecoverableError - existing event", - statusMap: map[*InstanceID]*StatusEvent{ - {}: NewStatusEvent(StatusStarting), - {}: NewStatusEvent(StatusOK), - {}: latest(NewRecoverableErrorEvent(assert.AnError)), - }, - expectedStatus: &StatusEvent{ - status: StatusRecoverableError, - timestamp: maxTime, - err: assert.AnError, - }, - }, - { - name: "Starting - synthetic event", - statusMap: map[*InstanceID]*StatusEvent{ - {}: NewStatusEvent(StatusStarting), - {}: latest(NewStatusEvent(StatusOK)), - }, - expectedStatus: &StatusEvent{ - status: StatusStarting, - timestamp: maxTime, - }, - }, - { - name: "OK - existing event", - statusMap: map[*InstanceID]*StatusEvent{ - {}: NewStatusEvent(StatusOK), - {}: latest(NewStatusEvent(StatusOK)), - {}: NewStatusEvent(StatusOK), - }, - expectedStatus: &StatusEvent{ - status: StatusOK, - timestamp: maxTime, - }, - }, - } { - t.Run(tc.name, func(t *testing.T) { - assert.Equal(t, tc.expectedStatus, AggregateStatusEvent(tc.statusMap)) - }) - } -} diff --git a/component/telemetry.go b/component/telemetry.go index febb6d6cbcd..a3d153c7050 100644 --- a/component/telemetry.go +++ b/component/telemetry.go @@ -30,10 +30,4 @@ type TelemetrySettings struct { // Resource contains the resource attributes for the collector's telemetry. Resource pcommon.Resource - - // ReportStatus allows a component to report runtime changes in status. The service - // will automatically report status for a component during startup and shutdown. Components can - // use this method to report status after start and before shutdown. For more details about - // component status reporting see: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-status.md - ReportStatus func(*StatusEvent) } diff --git a/config/configauth/go.mod b/config/configauth/go.mod index 67d49fed81e..ead90136226 100644 --- a/config/configauth/go.mod +++ b/config/configauth/go.mod @@ -55,3 +55,5 @@ replace go.opentelemetry.io/collector/extension/auth => ../../extension/auth replace go.opentelemetry.io/collector/featuregate => ../../featuregate replace go.opentelemetry.io/collector/internal/globalgates => ../../internal/globalgates + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/config/configgrpc/go.mod b/config/configgrpc/go.mod index c124e5eef00..061387f1c70 100644 --- a/config/configgrpc/go.mod +++ b/config/configgrpc/go.mod @@ -112,3 +112,5 @@ replace go.opentelemetry.io/collector/consumer => ../../consumer replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/config/confighttp/go.mod b/config/confighttp/go.mod index 9521f051d77..9f3e2c9b899 100644 --- a/config/confighttp/go.mod +++ b/config/confighttp/go.mod @@ -105,3 +105,5 @@ replace go.opentelemetry.io/collector/pdata/pprofile => ../../pdata/pprofile replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/config/internal/go.mod b/config/internal/go.mod index f0813bb1408..4568a8a7eb0 100644 --- a/config/internal/go.mod +++ b/config/internal/go.mod @@ -41,3 +41,5 @@ replace go.opentelemetry.io/collector/internal/globalgates => ../../internal/glo replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/connector/connectorprofiles/go.mod b/connector/connectorprofiles/go.mod index e162d93cc8b..86376480e5f 100644 --- a/connector/connectorprofiles/go.mod +++ b/connector/connectorprofiles/go.mod @@ -64,3 +64,5 @@ replace go.opentelemetry.io/collector/pdata => ../../pdata replace go.opentelemetry.io/collector/config/configtelemetry => ../../config/configtelemetry replace go.opentelemetry.io/collector/pdata/testdata => ../../pdata/testdata + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/connector/forwardconnector/go.mod b/connector/forwardconnector/go.mod index 343ee8ca2f9..685b37157c2 100644 --- a/connector/forwardconnector/go.mod +++ b/connector/forwardconnector/go.mod @@ -93,3 +93,5 @@ replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consume replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest replace go.opentelemetry.io/collector/component/componentprofiles => ../../component/componentprofiles + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/connector/go.mod b/connector/go.mod index f01bf11e517..e7631178a0f 100644 --- a/connector/go.mod +++ b/connector/go.mod @@ -76,3 +76,5 @@ replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../consumer/c replace go.opentelemetry.io/collector/consumer/consumertest => ../consumer/consumertest replace go.opentelemetry.io/collector/component/componentprofiles => ../component/componentprofiles + +replace go.opentelemetry.io/collector/component/componentstatus => ../component/componentstatus diff --git a/exporter/debugexporter/go.mod b/exporter/debugexporter/go.mod index 61b002adaf6..90b702a93dc 100644 --- a/exporter/debugexporter/go.mod +++ b/exporter/debugexporter/go.mod @@ -96,3 +96,5 @@ replace go.opentelemetry.io/collector/config/configretry => ../../config/configr replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/exporter/exporterprofiles/go.mod b/exporter/exporterprofiles/go.mod index aba55964d02..32e5846dc3a 100644 --- a/exporter/exporterprofiles/go.mod +++ b/exporter/exporterprofiles/go.mod @@ -66,3 +66,5 @@ replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/co replace go.opentelemetry.io/collector/consumer => ../../consumer replace go.opentelemetry.io/collector/exporter => ../ + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/exporter/go.mod b/exporter/go.mod index de344232074..489d6a2282c 100644 --- a/exporter/go.mod +++ b/exporter/go.mod @@ -8,7 +8,7 @@ require ( github.com/stretchr/testify v1.9.0 go.opentelemetry.io/collector v0.107.0 go.opentelemetry.io/collector/component v0.107.0 - go.opentelemetry.io/collector/config/configretry v1.13.0 + go.opentelemetry.io/collector/config/configretry v1.12.0 go.opentelemetry.io/collector/config/configtelemetry v0.107.0 go.opentelemetry.io/collector/consumer v0.107.0 go.opentelemetry.io/collector/consumer/consumerprofiles v0.107.0 @@ -25,7 +25,7 @@ require ( go.uber.org/goleak v1.3.0 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.27.0 - golang.org/x/sys v0.24.0 + golang.org/x/sys v0.23.0 google.golang.org/grpc v1.65.0 ) @@ -52,6 +52,7 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.107.0 // indirect go.opentelemetry.io/collector/confmap v0.107.0 // indirect go.opentelemetry.io/collector/featuregate v1.13.0 // indirect go.opentelemetry.io/collector/internal/globalgates v0.107.0 // indirect @@ -95,3 +96,5 @@ replace go.opentelemetry.io/collector/config/configtelemetry => ../config/config replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../component/componentstatus diff --git a/exporter/go.sum b/exporter/go.sum index bdac93ca734..704d099bce6 100644 --- a/exporter/go.sum +++ b/exporter/go.sum @@ -101,8 +101,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= diff --git a/exporter/loggingexporter/go.mod b/exporter/loggingexporter/go.mod index ed7f24f981f..e1827a7148b 100644 --- a/exporter/loggingexporter/go.mod +++ b/exporter/loggingexporter/go.mod @@ -101,3 +101,5 @@ replace go.opentelemetry.io/collector/pdata/pprofile => ../../pdata/pprofile replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/exporter/nopexporter/go.mod b/exporter/nopexporter/go.mod index 928bd92d178..cc903dd716c 100644 --- a/exporter/nopexporter/go.mod +++ b/exporter/nopexporter/go.mod @@ -91,3 +91,5 @@ replace go.opentelemetry.io/collector/pdata/pprofile => ../../pdata/pprofile replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/exporter/otlpexporter/go.mod b/exporter/otlpexporter/go.mod index 835331a455c..e8a14292ba7 100644 --- a/exporter/otlpexporter/go.mod +++ b/exporter/otlpexporter/go.mod @@ -147,3 +147,5 @@ replace go.opentelemetry.io/collector/internal/globalgates => ../../internal/glo replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/exporter/otlphttpexporter/go.mod b/exporter/otlphttpexporter/go.mod index 8441d280c91..10f2779e1e0 100644 --- a/exporter/otlphttpexporter/go.mod +++ b/exporter/otlphttpexporter/go.mod @@ -144,3 +144,5 @@ replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consume replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest replace go.opentelemetry.io/collector/client => ../../client + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/extension/auth/go.mod b/extension/auth/go.mod index 034caa4ad24..6742c989011 100644 --- a/extension/auth/go.mod +++ b/extension/auth/go.mod @@ -65,3 +65,5 @@ replace go.opentelemetry.io/collector/config/configtelemetry => ../../config/con replace go.opentelemetry.io/collector/featuregate => ../../featuregate replace go.opentelemetry.io/collector/internal/globalgates => ../../internal/globalgates + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/extension/ballastextension/go.mod b/extension/ballastextension/go.mod index d6671accaac..ab746997052 100644 --- a/extension/ballastextension/go.mod +++ b/extension/ballastextension/go.mod @@ -41,6 +41,7 @@ require ( github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.107.0 // indirect go.opentelemetry.io/collector/config/configtelemetry v0.107.0 // indirect go.opentelemetry.io/collector/featuregate v1.13.0 // indirect go.opentelemetry.io/collector/internal/globalgates v0.107.0 // indirect @@ -91,3 +92,5 @@ replace go.opentelemetry.io/collector/pdata/pprofile => ../../pdata/pprofile replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/extension/extension.go b/extension/extension.go index aaa235d5ae0..c351747274e 100644 --- a/extension/extension.go +++ b/extension/extension.go @@ -51,17 +51,6 @@ type ConfigWatcher interface { NotifyConfig(ctx context.Context, conf *confmap.Conf) error } -// StatusWatcher is an extra interface for Extension hosted by the OpenTelemetry -// Collector that is to be implemented by extensions interested in changes to component -// status. -type StatusWatcher interface { - // ComponentStatusChanged notifies about a change in the source component status. - // Extensions that implement this interface must be ready that the ComponentStatusChanged - // may be called before, after or concurrently with calls to Component.Start() and Component.Shutdown(). - // The function may be called concurrently with itself. - ComponentStatusChanged(source *component.InstanceID, event *component.StatusEvent) -} - // Settings is passed to Factory.Create(...) function. type Settings struct { // ID returns the ID of the component that will be created. diff --git a/extension/extensiontest/statuswatcher_extension.go b/extension/extensiontest/statuswatcher_extension.go index a4d78f9033e..8a14bd2e9a1 100644 --- a/extension/extensiontest/statuswatcher_extension.go +++ b/extension/extensiontest/statuswatcher_extension.go @@ -7,6 +7,7 @@ import ( "context" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/extension" ) @@ -21,7 +22,7 @@ func NewStatusWatcherExtensionCreateSettings() extension.Settings { // NewStatusWatcherExtensionFactory returns a component.ExtensionFactory to construct a status watcher extension. func NewStatusWatcherExtensionFactory( - onStatusChanged func(source *component.InstanceID, event *component.StatusEvent), + onStatusChanged func(source *componentstatus.InstanceID, event *componentstatus.Event), ) extension.Factory { return extension.NewFactory( component.MustNewType("statuswatcher"), @@ -39,9 +40,9 @@ func NewStatusWatcherExtensionFactory( type statusWatcherExtension struct { component.StartFunc component.ShutdownFunc - onStatusChanged func(source *component.InstanceID, event *component.StatusEvent) + onStatusChanged func(source *componentstatus.InstanceID, event *componentstatus.Event) } -func (e statusWatcherExtension) ComponentStatusChanged(source *component.InstanceID, event *component.StatusEvent) { +func (e statusWatcherExtension) ComponentStatusChanged(source *componentstatus.InstanceID, event *componentstatus.Event) { e.onStatusChanged(source, event) } diff --git a/extension/extensiontest/statuswatcher_extension_test.go b/extension/extensiontest/statuswatcher_extension_test.go index 14f9859e354..cee610795f1 100644 --- a/extension/extensiontest/statuswatcher_extension_test.go +++ b/extension/extensiontest/statuswatcher_extension_test.go @@ -11,14 +11,14 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/component/componenttest" - "go.opentelemetry.io/collector/extension" ) func TestStatusWatcherExtension(t *testing.T) { statusChanged := false factory := NewStatusWatcherExtensionFactory( - func(*component.InstanceID, *component.StatusEvent) { + func(*componentstatus.InstanceID, *componentstatus.Event) { statusChanged = true }, ) @@ -32,7 +32,7 @@ func TestStatusWatcherExtension(t *testing.T) { assert.NoError(t, ext.Start(context.Background(), componenttest.NewNopHost())) assert.False(t, statusChanged) - ext.(extension.StatusWatcher).ComponentStatusChanged(&component.InstanceID{}, &component.StatusEvent{}) + ext.(componentstatus.Watcher).ComponentStatusChanged(&componentstatus.InstanceID{}, &componentstatus.Event{}) assert.True(t, statusChanged) assert.NoError(t, ext.Shutdown(context.Background())) diff --git a/extension/go.mod b/extension/go.mod index bde73828a12..e171cac7d66 100644 --- a/extension/go.mod +++ b/extension/go.mod @@ -6,6 +6,7 @@ require ( github.com/google/uuid v1.6.0 github.com/stretchr/testify v1.9.0 go.opentelemetry.io/collector/component v0.107.0 + go.opentelemetry.io/collector/component/componentstatus v0.0.0-20240731165531-6d32c09daf9f go.opentelemetry.io/collector/confmap v0.107.0 go.uber.org/goleak v1.3.0 ) @@ -62,3 +63,5 @@ replace go.opentelemetry.io/collector/config/configtelemetry => ../config/config replace go.opentelemetry.io/collector/featuregate => ../featuregate replace go.opentelemetry.io/collector/internal/globalgates => ../internal/globalgates + +replace go.opentelemetry.io/collector/component/componentstatus => ../component/componentstatus diff --git a/extension/memorylimiterextension/go.mod b/extension/memorylimiterextension/go.mod index ed93bee5fea..d51af99aa8b 100644 --- a/extension/memorylimiterextension/go.mod +++ b/extension/memorylimiterextension/go.mod @@ -40,6 +40,7 @@ require ( github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.107.0 // indirect go.opentelemetry.io/collector/config/configtelemetry v0.107.0 // indirect go.opentelemetry.io/collector/featuregate v1.13.0 // indirect go.opentelemetry.io/collector/internal/globalgates v0.107.0 // indirect @@ -85,3 +86,5 @@ replace go.opentelemetry.io/collector/pdata/pprofile => ../../pdata/pprofile replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/extension/zpagesextension/go.mod b/extension/zpagesextension/go.mod index 6e8e654a2ef..50f3cbff3da 100644 --- a/extension/zpagesextension/go.mod +++ b/extension/zpagesextension/go.mod @@ -6,6 +6,7 @@ require ( github.com/stretchr/testify v1.9.0 go.opentelemetry.io/collector v0.107.0 go.opentelemetry.io/collector/component v0.107.0 + go.opentelemetry.io/collector/component/componentstatus v0.107.0 go.opentelemetry.io/collector/config/configauth v0.107.0 go.opentelemetry.io/collector/config/confighttp v0.107.0 go.opentelemetry.io/collector/confmap v0.107.0 @@ -129,3 +130,5 @@ replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consume replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest replace go.opentelemetry.io/collector/client => ../../client + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/extension/zpagesextension/zpagesextension.go b/extension/zpagesextension/zpagesextension.go index 16bb6c6635d..dc2cd3a7f27 100644 --- a/extension/zpagesextension/zpagesextension.go +++ b/extension/zpagesextension/zpagesextension.go @@ -14,6 +14,7 @@ import ( "go.uber.org/zap" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" ) const ( @@ -83,7 +84,7 @@ func (zpe *zpagesExtension) Start(ctx context.Context, host component.Host) erro defer close(zpe.stopCh) if errHTTP := zpe.server.Serve(ln); errHTTP != nil && !errors.Is(errHTTP, http.ErrServerClosed) { - zpe.telemetry.ReportStatus(component.NewFatalErrorEvent(errHTTP)) + componentstatus.ReportStatus(host, componentstatus.NewFatalErrorEvent(errHTTP)) } }() diff --git a/go.mod b/go.mod index 75bc59f8366..493e9eb7fc3 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/shirou/gopsutil/v4 v4.24.7 github.com/stretchr/testify v1.9.0 go.opentelemetry.io/collector/component v0.107.0 + go.opentelemetry.io/collector/component/componentstatus v0.107.0 go.opentelemetry.io/collector/confmap v0.107.0 go.opentelemetry.io/collector/consumer v0.107.0 go.opentelemetry.io/collector/consumer/consumertest v0.107.0 @@ -91,6 +92,8 @@ require ( replace go.opentelemetry.io/collector/component => ./component +replace go.opentelemetry.io/collector/component/componentstatus => ./component/componentstatus + replace go.opentelemetry.io/collector/confmap => ./confmap replace go.opentelemetry.io/collector/config/configtelemetry => ./config/configtelemetry diff --git a/internal/e2e/go.mod b/internal/e2e/go.mod index 0740dfbdac6..8b6e7fe0f1e 100644 --- a/internal/e2e/go.mod +++ b/internal/e2e/go.mod @@ -6,22 +6,29 @@ require ( github.com/stretchr/testify v1.9.0 go.opentelemetry.io/collector v0.107.0 go.opentelemetry.io/collector/component v0.107.0 + go.opentelemetry.io/collector/component/componentstatus v0.107.0 go.opentelemetry.io/collector/config/configgrpc v0.107.0 go.opentelemetry.io/collector/config/confighttp v0.107.0 go.opentelemetry.io/collector/config/configopaque v1.13.0 go.opentelemetry.io/collector/config/configretry v1.13.0 + go.opentelemetry.io/collector/config/configtelemetry v0.107.0 go.opentelemetry.io/collector/config/configtls v1.13.0 go.opentelemetry.io/collector/confmap v0.107.0 + go.opentelemetry.io/collector/connector v0.107.0 go.opentelemetry.io/collector/consumer v0.107.0 go.opentelemetry.io/collector/consumer/consumertest v0.107.0 go.opentelemetry.io/collector/exporter v0.107.0 go.opentelemetry.io/collector/exporter/otlpexporter v0.107.0 go.opentelemetry.io/collector/exporter/otlphttpexporter v0.107.0 + go.opentelemetry.io/collector/extension v0.107.0 go.opentelemetry.io/collector/pdata v1.13.0 go.opentelemetry.io/collector/pdata/testdata v0.107.0 + go.opentelemetry.io/collector/processor v0.107.0 go.opentelemetry.io/collector/receiver v0.107.0 go.opentelemetry.io/collector/receiver/otlpreceiver v0.107.0 + go.opentelemetry.io/collector/service v0.107.0 go.uber.org/goleak v1.3.0 + go.uber.org/zap v1.27.0 ) require ( @@ -33,6 +40,7 @@ require ( github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-viper/mapstructure/v2 v2.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/snappy v0.0.4 // indirect @@ -44,6 +52,7 @@ require ( github.com/knadh/koanf/maps v0.1.1 // indirect github.com/knadh/koanf/providers/confmap v0.1.0 // indirect github.com/knadh/koanf/v2 v2.1.1 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -51,26 +60,33 @@ require ( github.com/mostynb/go-grpc-compression v1.2.3 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/prometheus/client_golang v1.19.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/rs/cors v1.11.0 // indirect + github.com/shirou/gopsutil/v4 v4.24.7 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opentelemetry.io/collector/client v1.13.0 // indirect + go.opentelemetry.io/collector/component/componentprofiles v0.107.0 // indirect go.opentelemetry.io/collector/config/configauth v0.107.0 // indirect go.opentelemetry.io/collector/config/configcompression v1.13.0 // indirect go.opentelemetry.io/collector/config/confignet v0.107.0 // indirect - go.opentelemetry.io/collector/config/configtelemetry v0.107.0 // indirect go.opentelemetry.io/collector/config/internal v0.107.0 // indirect go.opentelemetry.io/collector/consumer/consumerprofiles v0.107.0 // indirect - go.opentelemetry.io/collector/extension v0.107.0 // indirect go.opentelemetry.io/collector/extension/auth v0.107.0 // indirect go.opentelemetry.io/collector/featuregate v1.13.0 // indirect go.opentelemetry.io/collector/internal/globalgates v0.107.0 // indirect go.opentelemetry.io/collector/pdata/pprofile v0.107.0 // indirect + go.opentelemetry.io/collector/semconv v0.107.0 // indirect go.opentelemetry.io/contrib/config v0.8.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.28.0 // indirect go.opentelemetry.io/otel v1.28.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.4.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 // indirect @@ -89,10 +105,10 @@ require ( go.opentelemetry.io/otel/trace v1.28.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect golang.org/x/net v0.28.0 // indirect golang.org/x/sys v0.24.0 // indirect golang.org/x/text v0.17.0 // indirect + gonum.org/v1/gonum v0.15.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/grpc v1.65.0 // indirect @@ -157,3 +173,17 @@ replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consume replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest replace go.opentelemetry.io/collector/client => ../../client + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus + +replace go.opentelemetry.io/collector/connector => ../../connector + +replace go.opentelemetry.io/collector/semconv => ../../semconv + +replace go.opentelemetry.io/collector/processor => ../../processor + +replace go.opentelemetry.io/collector/extension/zpagesextension => ../../extension/zpagesextension + +replace go.opentelemetry.io/collector/service => ../../service + +replace go.opentelemetry.io/collector/component/componentprofiles => ../../component/componentprofiles diff --git a/internal/e2e/go.sum b/internal/e2e/go.sum index c024517b811..4d430ac21d0 100644 --- a/internal/e2e/go.sum +++ b/internal/e2e/go.sum @@ -16,12 +16,15 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-viper/mapstructure/v2 v2.1.0 h1:gHnMa2Y/pIxElCH2GlZZ1lZSsn6XMtufpGyP1XxdC/w= github.com/go-viper/mapstructure/v2 v2.1.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -47,6 +50,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= @@ -62,6 +67,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= @@ -74,18 +81,34 @@ github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/shirou/gopsutil/v4 v4.24.7 h1:V9UGTK4gQ8HvcnPKf6Zt3XHyQq/peaekfxpJ2HSocJk= +github.com/shirou/gopsutil/v4 v4.24.7/go.mod h1:0uW/073rP7FYLOkvxolUQM5rMOLTNmRXnFKafpb71rw= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opentelemetry.io/contrib/config v0.8.0 h1:OD7aDMhL+2EpzdSHfkDmcdD/uUA+PgKM5faFyF9XFT0= go.opentelemetry.io/contrib/config v0.8.0/go.mod h1:dGeVZWE//3wrxYHHP0iCBYJU1QmOmPcbV+FNB7pjDYI= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= +go.opentelemetry.io/contrib/propagators/b3 v1.28.0 h1:XR6CFQrQ/ttAYmTBX2loUEFGdk1h17pxYI8828dk/1Y= +go.opentelemetry.io/contrib/propagators/b3 v1.28.0/go.mod h1:DWRkzJONLquRz7OJPh2rRbZ7MugQj62rk7g6HRnEqh0= +go.opentelemetry.io/contrib/zpages v0.53.0 h1:hGgaJ3nrescxEk383gOBHA5gNfoquHs8oV/XcKYxJkw= +go.opentelemetry.io/contrib/zpages v0.53.0/go.mod h1:iOo8fpUxMAu5+4x9DSEQeUOCeY19KaN6v2OPSeIggz4= go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.4.0 h1:zBPZAISA9NOc5cE8zydqDiS0itvg/P/0Hn9m72a5gvM= @@ -129,6 +152,8 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -142,7 +167,11 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -157,6 +186,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ= +gonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo= google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0= google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw= google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= diff --git a/internal/e2e/status_test.go b/internal/e2e/status_test.go new file mode 100644 index 00000000000..b49d7bea600 --- /dev/null +++ b/internal/e2e/status_test.go @@ -0,0 +1,267 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package e2e + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zapcore" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" + "go.opentelemetry.io/collector/config/configtelemetry" + "go.opentelemetry.io/collector/confmap" + "go.opentelemetry.io/collector/connector/connectortest" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/exporter/exportertest" + "go.opentelemetry.io/collector/extension" + "go.opentelemetry.io/collector/internal/sharedcomponent" + "go.opentelemetry.io/collector/processor/processortest" + "go.opentelemetry.io/collector/receiver" + "go.opentelemetry.io/collector/service" + "go.opentelemetry.io/collector/service/extensions" + "go.opentelemetry.io/collector/service/pipelines" + "go.opentelemetry.io/collector/service/telemetry" +) + +var nopType = component.MustNewType("nop") + +func Test_ComponentStatusReporting_SharedInstance(t *testing.T) { + eventsReceived := make(map[*componentstatus.InstanceID][]*componentstatus.Event) + + set := service.Settings{ + BuildInfo: component.NewDefaultBuildInfo(), + CollectorConf: confmap.New(), + Receivers: receiver.NewBuilder( + map[component.ID]component.Config{ + component.NewID(component.MustNewType("test")): &receiverConfig{}, + }, + map[component.Type]receiver.Factory{ + component.MustNewType("test"): newReceiverFactory(), + }), + Processors: processortest.NewNopBuilder(), + Exporters: exportertest.NewNopBuilder(), + Connectors: connectortest.NewNopBuilder(), + Extensions: extension.NewBuilder( + map[component.ID]component.Config{ + component.NewID(component.MustNewType("watcher")): &extensionConfig{eventsReceived}, + }, + map[component.Type]extension.Factory{ + component.MustNewType("watcher"): newExtensionFactory(), + }), + } + set.BuildInfo = component.BuildInfo{Version: "test version", Command: "otelcoltest"} + + cfg := service.Config{ + Telemetry: telemetry.Config{ + Logs: telemetry.LogsConfig{ + Level: zapcore.InfoLevel, + Development: false, + Encoding: "console", + Sampling: &telemetry.LogsSamplingConfig{ + Enabled: true, + Tick: 10 * time.Second, + Initial: 100, + Thereafter: 100, + }, + OutputPaths: []string{"stderr"}, + ErrorOutputPaths: []string{"stderr"}, + DisableCaller: false, + DisableStacktrace: false, + InitialFields: map[string]any(nil), + }, + Metrics: telemetry.MetricsConfig{ + Level: configtelemetry.LevelNone, + }, + }, + Pipelines: pipelines.Config{ + component.MustNewID("traces"): { + Receivers: []component.ID{component.NewID(component.MustNewType("test"))}, + Exporters: []component.ID{component.NewID(nopType)}, + }, + component.MustNewID("metrics"): { + Receivers: []component.ID{component.NewID(component.MustNewType("test"))}, + Exporters: []component.ID{component.NewID(nopType)}, + }, + }, + Extensions: extensions.Config{component.NewID(component.MustNewType("watcher"))}, + } + + s, err := service.New(context.Background(), set, cfg) + require.NoError(t, err) + + err = s.Start(context.Background()) + require.NoError(t, err) + time.Sleep(15 * time.Second) + err = s.Shutdown(context.Background()) + require.NoError(t, err) + + assert.Equal(t, 5, len(eventsReceived)) + + for instanceID, events := range eventsReceived { + if instanceID.ID == component.NewID(component.MustNewType("test")) { + for i, e := range events { + if i == 0 { + assert.Equal(t, componentstatus.StatusStarting, e.Status()) + } + if i == 1 { + assert.Equal(t, componentstatus.StatusRecoverableError, e.Status()) + } + if i == 2 { + assert.Equal(t, componentstatus.StatusOK, e.Status()) + } + if i == 3 { + assert.Equal(t, componentstatus.StatusStopping, e.Status()) + } + if i == 4 { + assert.Equal(t, componentstatus.StatusStopped, e.Status()) + } + if i >= 5 { + assert.Fail(t, "received too many events") + } + } + } + } +} + +func newReceiverFactory() receiver.Factory { + return receiver.NewFactory( + component.MustNewType("test"), + createDefaultReceiverConfig, + receiver.WithTraces(createTraces, component.StabilityLevelStable), + receiver.WithMetrics(createMetrics, component.StabilityLevelStable), + ) +} + +type testReceiver struct{} + +func (t *testReceiver) Start(_ context.Context, host component.Host) error { + if statusReporter, ok := host.(componentstatus.Reporter); ok { + statusReporter.Report(componentstatus.NewRecoverableErrorEvent(errors.New("test recoverable error"))) + go func() { + statusReporter.Report(componentstatus.NewEvent(componentstatus.StatusOK)) + }() + } + return nil +} + +func (t *testReceiver) Shutdown(_ context.Context) error { + return nil +} + +type receiverConfig struct{} + +func createDefaultReceiverConfig() component.Config { + return &receiverConfig{} +} + +func createTraces( + _ context.Context, + set receiver.Settings, + cfg component.Config, + _ consumer.Traces, +) (receiver.Traces, error) { + oCfg := cfg.(*receiverConfig) + r, err := receivers.LoadOrStore( + oCfg, + func() (*testReceiver, error) { + return &testReceiver{}, nil + }, + &set.TelemetrySettings, + ) + if err != nil { + return nil, err + } + + return r, nil +} + +func createMetrics( + _ context.Context, + set receiver.Settings, + cfg component.Config, + _ consumer.Metrics, +) (receiver.Metrics, error) { + oCfg := cfg.(*receiverConfig) + r, err := receivers.LoadOrStore( + oCfg, + func() (*testReceiver, error) { + return &testReceiver{}, nil + }, + &set.TelemetrySettings, + ) + if err != nil { + return nil, err + } + + return r, nil +} + +var receivers = sharedcomponent.NewMap[*receiverConfig, *testReceiver]() + +func newExtensionFactory() extension.Factory { + return extension.NewFactory( + component.MustNewType("watcher"), + createDefaultExtensionConfig, + createExtension, + component.StabilityLevelStable, + ) +} + +func createExtension(_ context.Context, _ extension.Settings, cfg component.Config) (extension.Extension, error) { + oCfg := cfg.(*extensionConfig) + return &testExtension{ + eventsReceived: oCfg.eventsReceived, + }, nil +} + +type testExtension struct { + eventsReceived map[*componentstatus.InstanceID][]*componentstatus.Event +} + +type extensionConfig struct { + eventsReceived map[*componentstatus.InstanceID][]*componentstatus.Event +} + +func createDefaultExtensionConfig() component.Config { + return &extensionConfig{} +} + +// Start implements the component.Component interface. +func (t *testExtension) Start(_ context.Context, _ component.Host) error { + return nil +} + +// Shutdown implements the component.Component interface. +func (t *testExtension) Shutdown(_ context.Context) error { + return nil +} + +// ComponentStatusChanged implements the extension.StatusWatcher interface. +func (t *testExtension) ComponentStatusChanged( + source *componentstatus.InstanceID, + event *componentstatus.Event, +) { + t.eventsReceived[source] = append(t.eventsReceived[source], event) +} + +// NotifyConfig implements the extension.ConfigWatcher interface. +func (t *testExtension) NotifyConfig(_ context.Context, _ *confmap.Conf) error { + return nil +} + +// Ready implements the extension.PipelineWatcher interface. +func (t *testExtension) Ready() error { + return nil +} + +// NotReady implements the extension.PipelineWatcher interface. +func (t *testExtension) NotReady() error { + return nil +} diff --git a/internal/sharedcomponent/sharedcomponent.go b/internal/sharedcomponent/sharedcomponent.go index 1a3e65878c2..f9c8193c6ff 100644 --- a/internal/sharedcomponent/sharedcomponent.go +++ b/internal/sharedcomponent/sharedcomponent.go @@ -8,9 +8,11 @@ package sharedcomponent // import "go.opentelemetry.io/collector/internal/shared import ( "context" + "slices" "sync" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" ) func NewMap[K comparable, V component.Component]() *Map[K, V] { @@ -31,17 +33,6 @@ func (m *Map[K, V]) LoadOrStore(key K, create func() (V, error), telemetrySettin m.lock.Lock() defer m.lock.Unlock() if c, ok := m.components[key]; ok { - // If we haven't already seen this telemetry settings, this shared component represents - // another instance. Wrap ReportStatus to report for all instances this shared - // component represents. - if _, ok := c.seenSettings[telemetrySettings]; !ok { - c.seenSettings[telemetrySettings] = struct{}{} - prev := c.telemetry.ReportStatus - c.telemetry.ReportStatus = func(ev *component.StatusEvent) { - telemetrySettings.ReportStatus(ev) - prev(ev) - } - } return c, nil } comp, err := create() @@ -57,9 +48,6 @@ func (m *Map[K, V]) LoadOrStore(key K, create func() (V, error), telemetrySettin delete(m.components, key) }, telemetry: telemetrySettings, - seenSettings: map[*component.TelemetrySettings]struct{}{ - telemetrySettings: {}, - }, } m.components[key] = newComp return newComp, nil @@ -74,8 +62,9 @@ type Component[V component.Component] struct { stopOnce sync.Once removeFunc func() - telemetry *component.TelemetrySettings - seenSettings map[*component.TelemetrySettings]struct{} + telemetry *component.TelemetrySettings + + hostWrapper *hostWrapper } // Unwrap returns the original component. @@ -85,18 +74,76 @@ func (c *Component[V]) Unwrap() V { // Start starts the underlying component if it never started before. func (c *Component[V]) Start(ctx context.Context, host component.Host) error { - var err error - c.startOnce.Do(func() { - // It's important that status for a shared component is reported through its - // telemetry settings to keep status in sync and avoid race conditions. This logic duplicates - // and takes priority over the automated status reporting that happens in graph, making the - // status reporting in graph a no-op. - c.telemetry.ReportStatus(component.NewStatusEvent(component.StatusStarting)) - if err = c.component.Start(ctx, host); err != nil { - c.telemetry.ReportStatus(component.NewPermanentErrorEvent(err)) - } - }) - return err + if c.hostWrapper == nil { + var err error + c.startOnce.Do(func() { + c.hostWrapper = &hostWrapper{ + host: host, + sources: make([]componentstatus.Reporter, 0), + previousEvents: make([]*componentstatus.Event, 0), + } + statusReporter, isStatusReporter := host.(componentstatus.Reporter) + if isStatusReporter { + c.hostWrapper.addSource(statusReporter) + } + + // It's important that status for a shared component is reported through its + // telemetry settings to keep status in sync and avoid race conditions. This logic duplicates + // and takes priority over the automated status reporting that happens in graph, making the + // status reporting in graph a no-op. + c.hostWrapper.Report(componentstatus.NewEvent(componentstatus.StatusStarting)) + if err = c.component.Start(ctx, c.hostWrapper); err != nil { + c.hostWrapper.Report(componentstatus.NewPermanentErrorEvent(err)) + } + }) + return err + } + statusReporter, isStatusReporter := host.(componentstatus.Reporter) + if isStatusReporter { + c.hostWrapper.addSource(statusReporter) + } + return nil +} + +var _ component.Host = (*hostWrapper)(nil) +var _ componentstatus.Reporter = (*hostWrapper)(nil) + +type hostWrapper struct { + host component.Host + sources []componentstatus.Reporter + previousEvents []*componentstatus.Event + lock sync.Mutex +} + +func (h *hostWrapper) GetExtensions() map[component.ID]component.Component { + return h.host.GetExtensions() +} + +func (h *hostWrapper) Report(e *componentstatus.Event) { + // Only remember an event if it will be emitted and it has not been sent already. + h.lock.Lock() + if len(h.sources) > 0 && !slices.Contains(h.previousEvents, e) { + h.previousEvents = append(h.previousEvents, e) + } + h.lock.Unlock() + + h.lock.Lock() + for _, s := range h.sources { + s.Report(e) + } + h.lock.Unlock() +} + +func (h *hostWrapper) addSource(s componentstatus.Reporter) { + h.lock.Lock() + for _, e := range h.previousEvents { + s.Report(e) + } + h.lock.Unlock() + + h.lock.Lock() + h.sources = append(h.sources, s) + h.lock.Unlock() } // Shutdown shuts down the underlying component. @@ -107,12 +154,16 @@ func (c *Component[V]) Shutdown(ctx context.Context) error { // telemetry settings to keep status in sync and avoid race conditions. This logic duplicates // and takes priority over the automated status reporting that happens in graph, making the // status reporting in graph a no-op. - c.telemetry.ReportStatus(component.NewStatusEvent(component.StatusStopping)) + if c.hostWrapper != nil { + c.hostWrapper.Report(componentstatus.NewEvent(componentstatus.StatusStopping)) + } err = c.component.Shutdown(ctx) - if err != nil { - c.telemetry.ReportStatus(component.NewPermanentErrorEvent(err)) - } else { - c.telemetry.ReportStatus(component.NewStatusEvent(component.StatusStopped)) + if c.hostWrapper != nil { + if err != nil { + c.hostWrapper.Report(componentstatus.NewPermanentErrorEvent(err)) + } else { + c.hostWrapper.Report(componentstatus.NewEvent(componentstatus.StatusStopped)) + } } c.removeFunc() }) diff --git a/internal/sharedcomponent/sharedcomponent_test.go b/internal/sharedcomponent/sharedcomponent_test.go index 77cda5124db..08841a62479 100644 --- a/internal/sharedcomponent/sharedcomponent_test.go +++ b/internal/sharedcomponent/sharedcomponent_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/component/componenttest" ) @@ -107,123 +108,53 @@ func TestSharedComponent(t *testing.T) { assert.NoError(t, got.Shutdown(context.Background())) assert.Equal(t, 1, calledStop) } -func TestSharedComponentsReportStatus(t *testing.T) { - reportedStatuses := make(map[*component.InstanceID][]component.Status) - newStatusFunc := func() func(*component.StatusEvent) { - instanceID := &component.InstanceID{} - return func(ev *component.StatusEvent) { - if ev.Status() == component.StatusNone { - return - } - reportedStatuses[instanceID] = append(reportedStatuses[instanceID], ev.Status()) - } - } - - comp := &baseComponent{} - comps := NewMap[component.ID, *baseComponent]() - var telemetrySettings *component.TelemetrySettings - - // make a shared component that represents three instances - for i := 0; i < 3; i++ { - telemetrySettings = newNopTelemetrySettings() - telemetrySettings.ReportStatus = newStatusFunc() - // The initial settings for the shared component need to match the ones passed to the first - // invocation of LoadOrStore so that underlying telemetry settings reference can be used to - // wrap ReportStatus for subsequently added "instances". - if i == 0 { - comp.telemetry = telemetrySettings - } - got, err := comps.LoadOrStore( - id, - func() (*baseComponent, error) { return comp, nil }, - telemetrySettings, - ) - require.NoError(t, err) - assert.Len(t, comps.components, 1) - assert.Same(t, comp, got.Unwrap()) - } - - // make sure we don't try to represent a fourth instance if we reuse a telemetrySettings - _, _ = comps.LoadOrStore( - id, - func() (*baseComponent, error) { return comp, nil }, - telemetrySettings, - ) - - comp.telemetry.ReportStatus(component.NewStatusEvent(component.StatusStarting)) - - comp.telemetry.ReportStatus(component.NewStatusEvent(component.StatusOK)) - - // simulate an error - comp.telemetry.ReportStatus(component.NewStatusEvent(component.StatusNone)) - - // stopping - comp.telemetry.ReportStatus(component.NewStatusEvent(component.StatusStopping)) - - // stopped - comp.telemetry.ReportStatus(component.NewStatusEvent(component.StatusStopped)) - - // The shared component represents 3 component instances. Reporting status for the shared - // component should report status for each of the instances it represents. - expectedStatuses := []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, - } - - require.Equal(t, 3, len(reportedStatuses)) - - for _, actualStatuses := range reportedStatuses { - require.Equal(t, expectedStatuses, actualStatuses) - } -} func TestReportStatusOnStartShutdown(t *testing.T) { for _, tc := range []struct { - name string - startErr error - shutdownErr error - expectedStatuses []component.Status + name string + startErr error + shutdownErr error + expectedStatuses []componentstatus.Status + expectedNumReporterInstances int }{ { name: "successful start/stop", startErr: nil, shutdownErr: nil, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusStopped, }, + expectedNumReporterInstances: 3, }, { name: "start error", startErr: assert.AnError, shutdownErr: nil, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusPermanentError, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusPermanentError, }, + expectedNumReporterInstances: 1, }, { name: "shutdown error", shutdownErr: assert.AnError, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusStopping, - component.StatusPermanentError, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusPermanentError, }, + expectedNumReporterInstances: 3, }, } { t.Run(tc.name, func(t *testing.T) { - reportedStatuses := make(map[*component.InstanceID][]component.Status) - newStatusFunc := func() func(*component.StatusEvent) { - instanceID := &component.InstanceID{} - return func(ev *component.StatusEvent) { - reportedStatuses[instanceID] = append(reportedStatuses[instanceID], ev.Status()) - } + reportedStatuses := make(map[*componentstatus.InstanceID][]componentstatus.Status) + newStatusFunc := func(id *componentstatus.InstanceID, ev *componentstatus.Event) { + reportedStatuses[id] = append(reportedStatuses[id], ev.Status()) } base := &baseComponent{} if tc.startErr != nil { @@ -241,7 +172,6 @@ func TestReportStatusOnStartShutdown(t *testing.T) { var err error for i := 0; i < 3; i++ { telemetrySettings := newNopTelemetrySettings() - telemetrySettings.ReportStatus = newStatusFunc() if i == 0 { base.telemetry = telemetrySettings } @@ -253,17 +183,24 @@ func TestReportStatusOnStartShutdown(t *testing.T) { require.NoError(t, err) } - err = comp.Start(context.Background(), componenttest.NewNopHost()) + baseHost := componenttest.NewNopHost() + for i := 0; i < 3; i++ { + err = comp.Start(context.Background(), &testHost{Host: baseHost, InstanceID: &componentstatus.InstanceID{}, newStatusFunc: newStatusFunc}) + if err != nil { + break + } + } + require.Equal(t, tc.startErr, err) if tc.startErr == nil { - comp.telemetry.ReportStatus(component.NewStatusEvent(component.StatusOK)) + comp.hostWrapper.Report(componentstatus.NewEvent(componentstatus.StatusOK)) err = comp.Shutdown(context.Background()) require.Equal(t, tc.shutdownErr, err) } - require.Equal(t, 3, len(reportedStatuses)) + require.Equal(t, tc.expectedNumReporterInstances, len(reportedStatuses)) for _, actualStatuses := range reportedStatuses { require.Equal(t, tc.expectedStatuses, actualStatuses) @@ -277,3 +214,16 @@ func newNopTelemetrySettings() *component.TelemetrySettings { set := componenttest.NewNopTelemetrySettings() return &set } + +var _ component.Host = (*testHost)(nil) +var _ componentstatus.Reporter = (*testHost)(nil) + +type testHost struct { + component.Host + *componentstatus.InstanceID + newStatusFunc func(id *componentstatus.InstanceID, ev *componentstatus.Event) +} + +func (h *testHost) Report(e *componentstatus.Event) { + h.newStatusFunc(h.InstanceID, e) +} diff --git a/otelcol/collector_test.go b/otelcol/collector_test.go index a2dc4083f5f..ea3578e47b4 100644 --- a/otelcol/collector_test.go +++ b/otelcol/collector_test.go @@ -20,6 +20,7 @@ import ( "gopkg.in/yaml.v3" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/extension/extensiontest" "go.opentelemetry.io/collector/processor/processortest" @@ -145,9 +146,9 @@ func TestComponentStatusWatcher(t *testing.T) { factories.Processors[unhealthyProcessorFactory.Type()] = unhealthyProcessorFactory // Keep track of all status changes in a map. - changedComponents := map[*component.InstanceID][]component.Status{} + changedComponents := map[*componentstatus.InstanceID][]componentstatus.Status{} var mux sync.Mutex - onStatusChanged := func(source *component.InstanceID, event *component.StatusEvent) { + onStatusChanged := func(source *componentstatus.InstanceID, event *componentstatus.Event) { if source.ID.Type() != unhealthyProcessorFactory.Type() { return } @@ -174,17 +175,17 @@ func TestComponentStatusWatcher(t *testing.T) { // An unhealthy processor asynchronously reports a recoverable error. Depending on the Go // Scheduler the statuses reported at startup will be one of the two valid sequnces below. - startupStatuses1 := []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusRecoverableError, + startupStatuses1 := []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusRecoverableError, } - startupStatuses2 := []component.Status{ - component.StatusStarting, - component.StatusRecoverableError, + startupStatuses2 := []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusRecoverableError, } // the modulus of the actual statuses will match the modulus of the startup statuses - startupStatuses := func(actualStatuses []component.Status) []component.Status { + startupStatuses := func(actualStatuses []componentstatus.Status) []componentstatus.Status { if len(actualStatuses)%2 == 1 { return startupStatuses1 } @@ -216,8 +217,8 @@ func TestComponentStatusWatcher(t *testing.T) { // Check for additional statuses after Shutdown. for _, v := range changedComponents { - expectedStatuses := append([]component.Status{}, startupStatuses(v)...) - expectedStatuses = append(expectedStatuses, component.StatusStopping, component.StatusStopped) + expectedStatuses := append([]componentstatus.Status{}, startupStatuses(v)...) + expectedStatuses = append(expectedStatuses, componentstatus.StatusStopping, componentstatus.StatusStopped) assert.Equal(t, expectedStatuses, v) } diff --git a/otelcol/go.mod b/otelcol/go.mod index 183627fe6c4..fb628e73a0d 100644 --- a/otelcol/go.mod +++ b/otelcol/go.mod @@ -6,6 +6,7 @@ require ( github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.9.0 go.opentelemetry.io/collector/component v0.107.0 + go.opentelemetry.io/collector/component/componentstatus v0.107.0 go.opentelemetry.io/collector/config/configtelemetry v0.107.0 go.opentelemetry.io/collector/confmap v0.107.0 go.opentelemetry.io/collector/connector v0.107.0 @@ -20,7 +21,7 @@ require ( go.uber.org/multierr v1.11.0 go.uber.org/zap v1.27.0 golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 - golang.org/x/sys v0.24.0 + golang.org/x/sys v0.23.0 google.golang.org/grpc v1.65.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -156,3 +157,5 @@ replace go.opentelemetry.io/collector/consumer/consumertest => ../consumer/consu replace go.opentelemetry.io/collector/component/componentprofiles => ../component/componentprofiles replace go.opentelemetry.io/collector/client => ../client + +replace go.opentelemetry.io/collector/component/componentstatus => ../component/componentstatus diff --git a/otelcol/go.sum b/otelcol/go.sum index 59ff54c32d1..8c88c49f9bb 100644 --- a/otelcol/go.sum +++ b/otelcol/go.sum @@ -176,8 +176,8 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= diff --git a/otelcol/otelcoltest/go.mod b/otelcol/otelcoltest/go.mod index d0bff4aef19..a12076acb20 100644 --- a/otelcol/otelcoltest/go.mod +++ b/otelcol/otelcoltest/go.mod @@ -59,6 +59,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opentelemetry.io/collector v0.107.0 // indirect go.opentelemetry.io/collector/component/componentprofiles v0.107.0 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.107.0 // indirect go.opentelemetry.io/collector/config/configtelemetry v0.107.0 // indirect go.opentelemetry.io/collector/consumer v0.107.0 // indirect go.opentelemetry.io/collector/consumer/consumerprofiles v0.107.0 // indirect @@ -92,7 +93,7 @@ require ( go.uber.org/zap v1.27.0 // indirect golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/net v0.28.0 // indirect - golang.org/x/sys v0.24.0 // indirect + golang.org/x/sys v0.23.0 // indirect golang.org/x/text v0.17.0 // indirect gonum.org/v1/gonum v0.15.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect @@ -171,3 +172,5 @@ replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/co replace go.opentelemetry.io/collector/component/componentprofiles => ../../component/componentprofiles replace go.opentelemetry.io/collector/client => ../../client + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/otelcol/otelcoltest/go.sum b/otelcol/otelcoltest/go.sum index 59ff54c32d1..8c88c49f9bb 100644 --- a/otelcol/otelcoltest/go.sum +++ b/otelcol/otelcoltest/go.sum @@ -176,8 +176,8 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= diff --git a/processor/batchprocessor/go.mod b/processor/batchprocessor/go.mod index 3440de8f375..c04f889d389 100644 --- a/processor/batchprocessor/go.mod +++ b/processor/batchprocessor/go.mod @@ -5,7 +5,7 @@ go 1.22.0 require ( github.com/stretchr/testify v1.9.0 go.opentelemetry.io/collector v0.107.0 - go.opentelemetry.io/collector/client v1.13.0 + go.opentelemetry.io/collector/client v0.107.0 go.opentelemetry.io/collector/component v0.107.0 go.opentelemetry.io/collector/config/configtelemetry v0.107.0 go.opentelemetry.io/collector/confmap v0.107.0 @@ -46,6 +46,7 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.107.0 // indirect go.opentelemetry.io/collector/consumer/consumerprofiles v0.107.0 // indirect go.opentelemetry.io/collector/featuregate v1.13.0 // indirect go.opentelemetry.io/collector/internal/globalgates v0.107.0 // indirect @@ -94,3 +95,5 @@ replace go.opentelemetry.io/collector/pdata/pprofile => ../../pdata/pprofile replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/processor/go.mod b/processor/go.mod index 13308bec517..b9e0f6b8161 100644 --- a/processor/go.mod +++ b/processor/go.mod @@ -7,6 +7,7 @@ require ( github.com/stretchr/testify v1.9.0 go.opentelemetry.io/collector v0.107.0 go.opentelemetry.io/collector/component v0.107.0 + go.opentelemetry.io/collector/component/componentstatus v0.107.0 go.opentelemetry.io/collector/config/configtelemetry v0.107.0 go.opentelemetry.io/collector/consumer v0.107.0 go.opentelemetry.io/collector/consumer/consumerprofiles v0.107.0 @@ -73,3 +74,5 @@ replace go.opentelemetry.io/collector/internal/globalgates => ../internal/global replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../component/componentstatus diff --git a/processor/memorylimiterprocessor/go.mod b/processor/memorylimiterprocessor/go.mod index 0e92dbea1ac..01465b02f1d 100644 --- a/processor/memorylimiterprocessor/go.mod +++ b/processor/memorylimiterprocessor/go.mod @@ -45,6 +45,7 @@ require ( github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.107.0 // indirect go.opentelemetry.io/collector/config/configtelemetry v0.107.0 // indirect go.opentelemetry.io/collector/consumer/consumerprofiles v0.107.0 // indirect go.opentelemetry.io/collector/featuregate v1.13.0 // indirect @@ -98,3 +99,5 @@ replace go.opentelemetry.io/collector/pdata/pprofile => ../../pdata/pprofile replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/processor/processorprofiles/go.mod b/processor/processorprofiles/go.mod index 81d231eb2f9..288462f541e 100644 --- a/processor/processorprofiles/go.mod +++ b/processor/processorprofiles/go.mod @@ -60,3 +60,5 @@ replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consume replace go.opentelemetry.io/collector/featuregate => ../../featuregate replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/processor/processortest/unhealthy_processor.go b/processor/processortest/unhealthy_processor.go index c537ab53498..3db2a10b667 100644 --- a/processor/processortest/unhealthy_processor.go +++ b/processor/processortest/unhealthy_processor.go @@ -7,6 +7,7 @@ import ( "context" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/consumer/consumertest" @@ -62,9 +63,9 @@ type unhealthyProcessor struct { telemetry component.TelemetrySettings } -func (p unhealthyProcessor) Start(context.Context, component.Host) error { +func (p unhealthyProcessor) Start(_ context.Context, host component.Host) error { go func() { - p.telemetry.ReportStatus(component.NewStatusEvent(component.StatusRecoverableError)) + componentstatus.ReportStatus(host, componentstatus.NewEvent(componentstatus.StatusRecoverableError)) }() return nil } diff --git a/receiver/go.mod b/receiver/go.mod index 2db3fe687df..f8d6d72de64 100644 --- a/receiver/go.mod +++ b/receiver/go.mod @@ -74,3 +74,5 @@ replace go.opentelemetry.io/collector/internal/globalgates => ../internal/global replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../component/componentstatus diff --git a/receiver/nopreceiver/go.mod b/receiver/nopreceiver/go.mod index bdd2711e4ed..735cff2cbd7 100644 --- a/receiver/nopreceiver/go.mod +++ b/receiver/nopreceiver/go.mod @@ -84,3 +84,5 @@ replace go.opentelemetry.io/collector/pdata/pprofile => ../../pdata/pprofile replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/receiver/otlpreceiver/go.mod b/receiver/otlpreceiver/go.mod index 56246a36e74..65ed7963301 100644 --- a/receiver/otlpreceiver/go.mod +++ b/receiver/otlpreceiver/go.mod @@ -8,6 +8,7 @@ require ( github.com/stretchr/testify v1.9.0 go.opentelemetry.io/collector v0.107.0 go.opentelemetry.io/collector/component v0.107.0 + go.opentelemetry.io/collector/component/componentstatus v0.107.0 go.opentelemetry.io/collector/config/configauth v0.107.0 go.opentelemetry.io/collector/config/configgrpc v0.107.0 go.opentelemetry.io/collector/config/confighttp v0.107.0 @@ -147,3 +148,5 @@ replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consume replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest replace go.opentelemetry.io/collector/client => ../../client + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/receiver/otlpreceiver/otlp.go b/receiver/otlpreceiver/otlp.go index 95d782b880e..f22c9bc9a7e 100644 --- a/receiver/otlpreceiver/otlp.go +++ b/receiver/otlpreceiver/otlp.go @@ -14,6 +14,7 @@ import ( "google.golang.org/grpc" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/config/confighttp" "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/pdata/plog/plogotlp" @@ -110,7 +111,7 @@ func (r *otlpReceiver) startGRPCServer(host component.Host) error { defer r.shutdownWG.Done() if errGrpc := r.serverGRPC.Serve(gln); errGrpc != nil && !errors.Is(errGrpc, grpc.ErrServerStopped) { - r.settings.ReportStatus(component.NewFatalErrorEvent(errGrpc)) + componentstatus.ReportStatus(host, componentstatus.NewFatalErrorEvent(errGrpc)) } }() return nil @@ -160,7 +161,7 @@ func (r *otlpReceiver) startHTTPServer(ctx context.Context, host component.Host) defer r.shutdownWG.Done() if errHTTP := r.serverHTTP.Serve(hln); errHTTP != nil && !errors.Is(errHTTP, http.ErrServerClosed) { - r.settings.ReportStatus(component.NewFatalErrorEvent(errHTTP)) + componentstatus.ReportStatus(host, componentstatus.NewFatalErrorEvent(errHTTP)) } }() return nil diff --git a/receiver/receiverprofiles/go.mod b/receiver/receiverprofiles/go.mod index 5f58075f88a..3f5041c44dd 100644 --- a/receiver/receiverprofiles/go.mod +++ b/receiver/receiverprofiles/go.mod @@ -60,3 +60,5 @@ replace go.opentelemetry.io/collector/pdata/pprofile => ../../pdata/pprofile replace go.opentelemetry.io/collector/pdata => ../../pdata replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/service/extensions/extensions.go b/service/extensions/extensions.go index b45221ee268..e87de41b6e3 100644 --- a/service/extensions/extensions.go +++ b/service/extensions/extensions.go @@ -13,6 +13,7 @@ import ( "go.uber.org/zap" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/extension" "go.opentelemetry.io/collector/service/internal/components" @@ -26,7 +27,7 @@ const zExtensionName = "zextensionname" type Extensions struct { telemetry component.TelemetrySettings extMap map[component.ID]extension.Extension - instanceIDs map[component.ID]*component.InstanceID + instanceIDs map[component.ID]*componentstatus.InstanceID extensionIDs []component.ID // start order (and reverse stop order) reporter status.Reporter } @@ -41,12 +42,12 @@ func (bes *Extensions) Start(ctx context.Context, host component.Host) error { ext := bes.extMap[extID] bes.reporter.ReportStatus( instanceID, - component.NewStatusEvent(component.StatusStarting), + componentstatus.NewEvent(componentstatus.StatusStarting), ) if err := ext.Start(ctx, host); err != nil { bes.reporter.ReportStatus( instanceID, - component.NewPermanentErrorEvent(err), + componentstatus.NewPermanentErrorEvent(err), ) // We log with zap.AddStacktrace(zap.DPanicLevel) to avoid adding the stack trace to the error log extLogger.WithOptions(zap.AddStacktrace(zap.DPanicLevel)).Error("Failed to start extension", zap.Error(err)) @@ -68,19 +69,19 @@ func (bes *Extensions) Shutdown(ctx context.Context) error { ext := bes.extMap[extID] bes.reporter.ReportStatus( instanceID, - component.NewStatusEvent(component.StatusStopping), + componentstatus.NewEvent(componentstatus.StatusStopping), ) if err := ext.Shutdown(ctx); err != nil { bes.reporter.ReportStatus( instanceID, - component.NewPermanentErrorEvent(err), + componentstatus.NewPermanentErrorEvent(err), ) errs = multierr.Append(errs, err) continue } bes.reporter.ReportStatus( instanceID, - component.NewStatusEvent(component.StatusStopped), + componentstatus.NewEvent(componentstatus.StatusStopped), ) } @@ -122,10 +123,10 @@ func (bes *Extensions) NotifyConfig(ctx context.Context, conf *confmap.Conf) err return errs } -func (bes *Extensions) NotifyComponentStatusChange(source *component.InstanceID, event *component.StatusEvent) { +func (bes *Extensions) NotifyComponentStatusChange(source *componentstatus.InstanceID, event *componentstatus.Event) { for _, extID := range bes.extensionIDs { ext := bes.extMap[extID] - if sw, ok := ext.(extension.StatusWatcher); ok { + if sw, ok := ext.(componentstatus.Watcher); ok { sw.ComponentStatusChanged(source, event) } } @@ -187,7 +188,7 @@ func New(ctx context.Context, set Settings, cfg Config, options ...Option) (*Ext exts := &Extensions{ telemetry: set.Telemetry, extMap: make(map[component.ID]extension.Extension), - instanceIDs: make(map[component.ID]*component.InstanceID), + instanceIDs: make(map[component.ID]*componentstatus.InstanceID), extensionIDs: make([]component.ID, 0, len(cfg)), reporter: &nopReporter{}, } @@ -197,7 +198,7 @@ func New(ctx context.Context, set Settings, cfg Config, options ...Option) (*Ext } for _, extID := range cfg { - instanceID := &component.InstanceID{ + instanceID := &componentstatus.InstanceID{ ID: extID, Kind: component.KindExtension, } @@ -206,7 +207,6 @@ func New(ctx context.Context, set Settings, cfg Config, options ...Option) (*Ext TelemetrySettings: set.Telemetry, BuildInfo: set.BuildInfo, } - extSet.TelemetrySettings.ReportStatus = status.NewReportStatusFunc(instanceID, exts.reporter.ReportStatus) extSet.TelemetrySettings.Logger = components.ExtensionLogger(set.Telemetry.Logger, extID) ext, err := set.Extensions.Create(ctx, extSet) @@ -234,6 +234,6 @@ type nopReporter struct{} func (r *nopReporter) Ready() {} -func (r *nopReporter) ReportStatus(*component.InstanceID, *component.StatusEvent) {} +func (r *nopReporter) ReportStatus(*componentstatus.InstanceID, *componentstatus.Event) {} -func (r *nopReporter) ReportOKIfStarting(*component.InstanceID) {} +func (r *nopReporter) ReportOKIfStarting(*componentstatus.InstanceID) {} diff --git a/service/extensions/extensions_test.go b/service/extensions/extensions_test.go index eaa827663d1..c7c94033b5a 100644 --- a/service/extensions/extensions_test.go +++ b/service/extensions/extensions_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/extension" @@ -354,7 +355,7 @@ func newCreateErrorExtensionFactory() extension.Factory { func TestStatusReportedOnStartupShutdown(t *testing.T) { // compare two slices of status events ignoring timestamp - assertEqualStatuses := func(t *testing.T, evts1, evts2 []*component.StatusEvent) { + assertEqualStatuses := func(t *testing.T, evts1, evts2 []*componentstatus.Event) { assert.Equal(t, len(evts1), len(evts2)) for i := 0; i < len(evts1); i++ { ev1 := evts1[i] @@ -366,37 +367,37 @@ func TestStatusReportedOnStartupShutdown(t *testing.T) { for _, tc := range []struct { name string - expectedStatuses []*component.StatusEvent + expectedStatuses []*componentstatus.Event startErr error shutdownErr error }{ { name: "successful startup/shutdown", - expectedStatuses: []*component.StatusEvent{ - component.NewStatusEvent(component.StatusStarting), - component.NewStatusEvent(component.StatusOK), - component.NewStatusEvent(component.StatusStopping), - component.NewStatusEvent(component.StatusStopped), + expectedStatuses: []*componentstatus.Event{ + componentstatus.NewEvent(componentstatus.StatusStarting), + componentstatus.NewEvent(componentstatus.StatusOK), + componentstatus.NewEvent(componentstatus.StatusStopping), + componentstatus.NewEvent(componentstatus.StatusStopped), }, startErr: nil, shutdownErr: nil, }, { name: "start error", - expectedStatuses: []*component.StatusEvent{ - component.NewStatusEvent(component.StatusStarting), - component.NewPermanentErrorEvent(assert.AnError), + expectedStatuses: []*componentstatus.Event{ + componentstatus.NewEvent(componentstatus.StatusStarting), + componentstatus.NewPermanentErrorEvent(assert.AnError), }, startErr: assert.AnError, shutdownErr: nil, }, { name: "shutdown error", - expectedStatuses: []*component.StatusEvent{ - component.NewStatusEvent(component.StatusStarting), - component.NewStatusEvent(component.StatusOK), - component.NewStatusEvent(component.StatusStopping), - component.NewPermanentErrorEvent(assert.AnError), + expectedStatuses: []*componentstatus.Event{ + componentstatus.NewEvent(componentstatus.StatusStarting), + componentstatus.NewEvent(componentstatus.StatusOK), + componentstatus.NewEvent(componentstatus.StatusStopping), + componentstatus.NewPermanentErrorEvent(assert.AnError), }, startErr: nil, shutdownErr: assert.AnError, @@ -414,8 +415,8 @@ func TestStatusReportedOnStartupShutdown(t *testing.T) { statusType: factory, } - var actualStatuses []*component.StatusEvent - rep := status.NewReporter(func(_ *component.InstanceID, ev *component.StatusEvent) { + var actualStatuses []*componentstatus.Event + rep := status.NewReporter(func(_ *componentstatus.InstanceID, ev *componentstatus.Event) { actualStatuses = append(actualStatuses, ev) }, func(err error) { require.NoError(t, err) diff --git a/service/go.mod b/service/go.mod index d46b24603df..cd6c4a1b111 100644 --- a/service/go.mod +++ b/service/go.mod @@ -11,6 +11,7 @@ require ( github.com/stretchr/testify v1.9.0 go.opentelemetry.io/collector v0.107.0 go.opentelemetry.io/collector/component v0.107.0 + go.opentelemetry.io/collector/component/componentstatus v0.107.0 go.opentelemetry.io/collector/config/confighttp v0.107.0 go.opentelemetry.io/collector/config/configtelemetry v0.107.0 go.opentelemetry.io/collector/confmap v0.107.0 @@ -99,7 +100,7 @@ require ( go.opentelemetry.io/otel/sdk/log v0.4.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect golang.org/x/net v0.28.0 // indirect - golang.org/x/sys v0.24.0 // indirect + golang.org/x/sys v0.23.0 // indirect golang.org/x/text v0.17.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect @@ -114,6 +115,8 @@ replace go.opentelemetry.io/collector/connector => ../connector replace go.opentelemetry.io/collector/component => ../component +replace go.opentelemetry.io/collector/component/componentstatus => ../component/componentstatus + replace go.opentelemetry.io/collector/pdata => ../pdata replace go.opentelemetry.io/collector/pdata/testdata => ../pdata/testdata diff --git a/service/go.sum b/service/go.sum index 47589a225dd..381d45408bc 100644 --- a/service/go.sum +++ b/service/go.sum @@ -168,8 +168,8 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= diff --git a/service/internal/graph/graph.go b/service/internal/graph/graph.go index 002fa3129f6..9839c193022 100644 --- a/service/internal/graph/graph.go +++ b/service/internal/graph/graph.go @@ -25,6 +25,7 @@ import ( "gonum.org/v1/gonum/graph/topo" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/connector" "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/exporter" @@ -60,7 +61,7 @@ type Graph struct { pipelines map[component.ID]*pipelineNodes // Keep track of status source per node - instanceIDs map[int64]*component.InstanceID + instanceIDs map[int64]*componentstatus.InstanceID telemetry component.TelemetrySettings } @@ -71,7 +72,7 @@ func Build(ctx context.Context, set Settings) (*Graph, error) { pipelines := &Graph{ componentGraph: simple.NewDirectedGraph(), pipelines: make(map[component.ID]*pipelineNodes, len(set.PipelineConfigs)), - instanceIDs: make(map[int64]*component.InstanceID), + instanceIDs: make(map[int64]*componentstatus.InstanceID), telemetry: set.Telemetry, } for pipelineID := range set.PipelineConfigs { @@ -200,7 +201,7 @@ func (g *Graph) createReceiver(pipelineID, recvID component.ID) *receiverNode { return node.(*receiverNode) } g.componentGraph.AddNode(rcvrNode) - g.instanceIDs[rcvrNode.ID()] = &component.InstanceID{ + g.instanceIDs[rcvrNode.ID()] = &componentstatus.InstanceID{ ID: recvID, Kind: component.KindReceiver, PipelineIDs: map[component.ID]struct{}{ @@ -213,7 +214,7 @@ func (g *Graph) createReceiver(pipelineID, recvID component.ID) *receiverNode { func (g *Graph) createProcessor(pipelineID, procID component.ID) *processorNode { procNode := newProcessorNode(pipelineID, procID) g.componentGraph.AddNode(procNode) - g.instanceIDs[procNode.ID()] = &component.InstanceID{ + g.instanceIDs[procNode.ID()] = &componentstatus.InstanceID{ ID: procID, Kind: component.KindProcessor, PipelineIDs: map[component.ID]struct{}{ @@ -230,7 +231,7 @@ func (g *Graph) createExporter(pipelineID, exprID component.ID) *exporterNode { return node.(*exporterNode) } g.componentGraph.AddNode(expNode) - g.instanceIDs[expNode.ID()] = &component.InstanceID{ + g.instanceIDs[expNode.ID()] = &componentstatus.InstanceID{ ID: expNode.componentID, Kind: component.KindExporter, PipelineIDs: map[component.ID]struct{}{ @@ -249,7 +250,7 @@ func (g *Graph) createConnector(exprPipelineID, rcvrPipelineID, connID component return node.(*connectorNode) } g.componentGraph.AddNode(connNode) - g.instanceIDs[connNode.ID()] = &component.InstanceID{ + g.instanceIDs[connNode.ID()] = &componentstatus.InstanceID{ ID: connNode.componentID, Kind: component.KindConnector, PipelineIDs: map[component.ID]struct{}{ @@ -299,23 +300,16 @@ func (g *Graph) buildComponents(ctx context.Context, set Settings) error { for i := len(nodes) - 1; i >= 0; i-- { node := nodes[i] - // skipped for capabilitiesNodes and fanoutNodes as they are not assigned componentIDs. - var telemetrySettings component.TelemetrySettings - if instanceID, ok := g.instanceIDs[node.ID()]; ok { - telemetrySettings = set.Telemetry - telemetrySettings.ReportStatus = status.NewReportStatusFunc(instanceID, set.ReportStatus) - } - switch n := node.(type) { case *receiverNode: - err = n.buildComponent(ctx, telemetrySettings, set.BuildInfo, set.ReceiverBuilder, g.nextConsumers(n.ID())) + err = n.buildComponent(ctx, set.Telemetry, set.BuildInfo, set.ReceiverBuilder, g.nextConsumers(n.ID())) case *processorNode: // nextConsumers is guaranteed to be length 1. Either it is the next processor or it is the fanout node for the exporters. - err = n.buildComponent(ctx, telemetrySettings, set.BuildInfo, set.ProcessorBuilder, g.nextConsumers(n.ID())[0]) + err = n.buildComponent(ctx, set.Telemetry, set.BuildInfo, set.ProcessorBuilder, g.nextConsumers(n.ID())[0]) case *exporterNode: - err = n.buildComponent(ctx, telemetrySettings, set.BuildInfo, set.ExporterBuilder) + err = n.buildComponent(ctx, set.Telemetry, set.BuildInfo, set.ExporterBuilder) case *connectorNode: - err = n.buildComponent(ctx, telemetrySettings, set.BuildInfo, set.ConnectorBuilder, g.nextConsumers(n.ID())) + err = n.buildComponent(ctx, set.Telemetry, set.BuildInfo, set.ConnectorBuilder, g.nextConsumers(n.ID())) case *capabilitiesNode: capability := consumer.Capabilities{ // The fanOutNode represents the aggregate capabilities of the exporters in the pipeline. @@ -398,7 +392,11 @@ type pipelineNodes struct { exporters map[int64]graph.Node } -func (g *Graph) StartAll(ctx context.Context, host component.Host, reporter status.Reporter) error { +func (g *Graph) StartAll(ctx context.Context, host *Host) error { + if host == nil { + return errors.New("host cannot be nil") + } + nodes, err := topo.Sort(g.componentGraph) if err != nil { return err @@ -417,15 +415,15 @@ func (g *Graph) StartAll(ctx context.Context, host component.Host, reporter stat } instanceID := g.instanceIDs[node.ID()] - reporter.ReportStatus( + host.Reporter.ReportStatus( instanceID, - component.NewStatusEvent(component.StatusStarting), + componentstatus.NewEvent(componentstatus.StatusStarting), ) - if compErr := comp.Start(ctx, host); compErr != nil { - reporter.ReportStatus( + if compErr := comp.Start(ctx, &HostWrapper{Host: host, InstanceID: instanceID}); compErr != nil { + host.Reporter.ReportStatus( instanceID, - component.NewPermanentErrorEvent(compErr), + componentstatus.NewPermanentErrorEvent(compErr), ) // We log with zap.AddStacktrace(zap.DPanicLevel) to avoid adding the stack trace to the error log g.telemetry.Logger.WithOptions(zap.AddStacktrace(zap.DPanicLevel)). @@ -437,7 +435,7 @@ func (g *Graph) StartAll(ctx context.Context, host component.Host, reporter stat return compErr } - reporter.ReportOKIfStarting(instanceID) + host.Reporter.ReportOKIfStarting(instanceID) } return nil } @@ -465,21 +463,21 @@ func (g *Graph) ShutdownAll(ctx context.Context, reporter status.Reporter) error instanceID := g.instanceIDs[node.ID()] reporter.ReportStatus( instanceID, - component.NewStatusEvent(component.StatusStopping), + componentstatus.NewEvent(componentstatus.StatusStopping), ) if compErr := comp.Shutdown(ctx); compErr != nil { errs = multierr.Append(errs, compErr) reporter.ReportStatus( instanceID, - component.NewPermanentErrorEvent(compErr), + componentstatus.NewPermanentErrorEvent(compErr), ) continue } reporter.ReportStatus( instanceID, - component.NewStatusEvent(component.StatusStopped), + componentstatus.NewEvent(componentstatus.StatusStopped), ) } return errs @@ -580,3 +578,16 @@ func connectorStability(f connector.Factory, expType, recType component.Type) co } return component.StabilityLevelUndefined } + +var _ getExporters = (*HostWrapper)(nil) +var _ component.Host = (*HostWrapper)(nil) +var _ componentstatus.Reporter = (*HostWrapper)(nil) + +type HostWrapper struct { + *Host + InstanceID *componentstatus.InstanceID +} + +func (host *HostWrapper) Report(event *componentstatus.Event) { + host.Reporter.ReportStatus(host.InstanceID, event) +} diff --git a/service/internal/graph/graph_test.go b/service/internal/graph/graph_test.go index fcc2ea6cf89..8b98d020fd5 100644 --- a/service/internal/graph/graph_test.go +++ b/service/internal/graph/graph_test.go @@ -16,6 +16,7 @@ import ( "gonum.org/v1/gonum/graph/simple" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/connector" "go.opentelemetry.io/collector/connector/connectortest" @@ -145,16 +146,16 @@ func TestGraphStartStop(t *testing.T) { pg := &Graph{componentGraph: simple.NewDirectedGraph()} pg.telemetry = componenttest.NewNopTelemetrySettings() - pg.instanceIDs = make(map[int64]*component.InstanceID) + pg.instanceIDs = make(map[int64]*componentstatus.InstanceID) for _, edge := range tt.edges { f, t := &testNode{id: edge[0]}, &testNode{id: edge[1]} - pg.instanceIDs[f.ID()] = &component.InstanceID{} - pg.instanceIDs[t.ID()] = &component.InstanceID{} + pg.instanceIDs[f.ID()] = &componentstatus.InstanceID{} + pg.instanceIDs[t.ID()] = &componentstatus.InstanceID{} pg.componentGraph.SetEdge(simple.Edge{F: f, T: t}) } - require.NoError(t, pg.StartAll(ctx, componenttest.NewNopHost(), statustest.NewNopStatusReporter())) + require.NoError(t, pg.StartAll(ctx, &Host{Reporter: status.NewReporter(func(*componentstatus.InstanceID, *componentstatus.Event) {}, func(error) {})})) for _, edge := range tt.edges { assert.Greater(t, ctx.order[edge[0]], ctx.order[edge[1]]) } @@ -176,7 +177,7 @@ func TestGraphStartStopCycle(t *testing.T) { c1 := &testNode{id: component.MustNewIDWithName("c", "1")} e1 := &testNode{id: component.MustNewIDWithName("e", "1")} - pg.instanceIDs = map[int64]*component.InstanceID{ + pg.instanceIDs = map[int64]*componentstatus.InstanceID{ r1.ID(): {}, p1.ID(): {}, c1.ID(): {}, @@ -188,7 +189,7 @@ func TestGraphStartStopCycle(t *testing.T) { pg.componentGraph.SetEdge(simple.Edge{F: c1, T: e1}) pg.componentGraph.SetEdge(simple.Edge{F: c1, T: p1}) // loop back - err := pg.StartAll(context.Background(), componenttest.NewNopHost(), statustest.NewNopStatusReporter()) + err := pg.StartAll(context.Background(), &Host{Reporter: status.NewReporter(func(*componentstatus.InstanceID, *componentstatus.Event) {}, func(error) {})}) assert.Error(t, err) assert.Contains(t, err.Error(), `topo: no topological ordering: cyclic components`) @@ -208,7 +209,7 @@ func TestGraphStartStopComponentError(t *testing.T) { id: component.MustNewIDWithName("e", "1"), shutdownErr: errors.New("bar"), } - pg.instanceIDs = map[int64]*component.InstanceID{ + pg.instanceIDs = map[int64]*componentstatus.InstanceID{ r1.ID(): {}, e1.ID(): {}, } @@ -216,7 +217,7 @@ func TestGraphStartStopComponentError(t *testing.T) { F: r1, T: e1, }) - assert.EqualError(t, pg.StartAll(context.Background(), componenttest.NewNopHost(), statustest.NewNopStatusReporter()), "foo") + assert.EqualError(t, pg.StartAll(context.Background(), &Host{Reporter: status.NewReporter(func(*componentstatus.InstanceID, *componentstatus.Event) {}, func(error) {})}), "foo") assert.EqualError(t, pg.ShutdownAll(context.Background(), statustest.NewNopStatusReporter()), "bar") } @@ -224,7 +225,7 @@ func TestConnectorPipelinesGraph(t *testing.T) { tests := []struct { name string pipelineConfigs pipelines.Config - expectedPerExporter int // requires symmetry in pipelines + expectedPerExporter int // requires symmetry in Pipelines }{ { name: "pipelines_simple.yaml", @@ -804,7 +805,7 @@ func TestConnectorPipelinesGraph(t *testing.T) { assert.Equal(t, len(test.pipelineConfigs), len(pg.pipelines)) - assert.NoError(t, pg.StartAll(context.Background(), componenttest.NewNopHost(), statustest.NewNopStatusReporter())) + assert.NoError(t, pg.StartAll(context.Background(), &Host{Reporter: status.NewReporter(func(*componentstatus.InstanceID, *componentstatus.Event) {}, func(error) {})})) mutatingPipelines := make(map[component.ID]bool, len(test.pipelineConfigs)) @@ -881,7 +882,7 @@ func TestConnectorPipelinesGraph(t *testing.T) { } } - // Check that connectors are correctly inheriting mutability from downstream pipelines + // Check that Connectors are correctly inheriting mutability from downstream Pipelines for expPipelineID, expPipeline := range pg.pipelines { for _, exp := range expPipeline.exporters { expConn, ok := exp.(*connectorNode) @@ -891,7 +892,7 @@ func TestConnectorPipelinesGraph(t *testing.T) { if expConn.getConsumer().Capabilities().MutatesData { continue } - // find all the pipelines of the same type where this connector is a receiver + // find all the Pipelines of the same type where this connector is a receiver var inheritMutatesData bool for recPipelineID, recPipeline := range pg.pipelines { if recPipelineID == expPipelineID || recPipelineID.Type() != expPipelineID.Type() { @@ -909,9 +910,9 @@ func TestConnectorPipelinesGraph(t *testing.T) { } } - // Push data into the pipelines. The list of receivers is retrieved directly from the overall - // component graph because we do not want to duplicate signal inputs to receivers that are - // shared between pipelines. The `allReceivers` function also excludes connectors, which we do + // Push data into the Pipelines. The list of Receivers is retrieved directly from the overall + // component graph because we do not want to duplicate signal inputs to Receivers that are + // shared between Pipelines. The `allReceivers` function also excludes Connectors, which we do // not want to directly inject with signals. allReceivers := pg.getReceivers() for _, c := range allReceivers[component.DataTypeTraces] { @@ -979,8 +980,8 @@ func TestConnectorPipelinesGraph(t *testing.T) { } } - // Get the list of exporters directly from the overall component graph. Like receivers, - // exclude connectors and validate each exporter once regardless of sharing between pipelines. + // Get the list of Exporters directly from the overall component graph. Like Receivers, + // exclude Connectors and validate each exporter once regardless of sharing between Pipelines. allExporters := pg.GetExporters() for _, e := range allExporters[component.DataTypeTraces] { tracesExporter := e.(*testcomponents.ExampleExporter) @@ -1142,7 +1143,7 @@ func TestConnectorRouter(t *testing.T) { assert.Equal(t, len(set.PipelineConfigs), len(pg.pipelines)) - // Get a handle for the traces receiver and both exporters + // Get a handle for the traces receiver and both Exporters tracesReceiver := allReceivers[component.DataTypeTraces][rcvrID].(*testcomponents.ExampleReceiver) tracesRight := allExporters[component.DataTypeTraces][expRightID].(*testcomponents.ExampleExporter) tracesLeft := allExporters[component.DataTypeTraces][expLeftID].(*testcomponents.ExampleExporter) @@ -1164,7 +1165,7 @@ func TestConnectorRouter(t *testing.T) { assert.Equal(t, 3, len(tracesRight.Traces)) assert.Equal(t, 2, len(tracesLeft.Traces)) - // Get a handle for the metrics receiver and both exporters + // Get a handle for the metrics receiver and both Exporters metricsReceiver := allReceivers[component.DataTypeMetrics][rcvrID].(*testcomponents.ExampleReceiver) metricsRight := allExporters[component.DataTypeMetrics][expRightID].(*testcomponents.ExampleExporter) metricsLeft := allExporters[component.DataTypeMetrics][expLeftID].(*testcomponents.ExampleExporter) @@ -1186,7 +1187,7 @@ func TestConnectorRouter(t *testing.T) { assert.Equal(t, 3, len(metricsRight.Metrics)) assert.Equal(t, 2, len(metricsLeft.Metrics)) - // Get a handle for the logs receiver and both exporters + // Get a handle for the logs receiver and both Exporters logsReceiver := allReceivers[component.DataTypeLogs][rcvrID].(*testcomponents.ExampleReceiver) logsRight := allExporters[component.DataTypeLogs][expRightID].(*testcomponents.ExampleExporter) logsLeft := allExporters[component.DataTypeLogs][expLeftID].(*testcomponents.ExampleExporter) @@ -2193,7 +2194,7 @@ func TestGraphFailToStartAndShutdown(t *testing.T) { } pipelines, err := Build(context.Background(), set) assert.NoError(t, err) - assert.Error(t, pipelines.StartAll(context.Background(), componenttest.NewNopHost(), statustest.NewNopStatusReporter())) + assert.Error(t, pipelines.StartAll(context.Background(), &Host{Reporter: status.NewReporter(func(*componentstatus.InstanceID, *componentstatus.Event) {}, func(error) {})})) assert.Error(t, pipelines.ShutdownAll(context.Background(), statustest.NewNopStatusReporter())) }) @@ -2207,7 +2208,7 @@ func TestGraphFailToStartAndShutdown(t *testing.T) { } pipelines, err := Build(context.Background(), set) assert.NoError(t, err) - assert.Error(t, pipelines.StartAll(context.Background(), componenttest.NewNopHost(), statustest.NewNopStatusReporter())) + assert.Error(t, pipelines.StartAll(context.Background(), &Host{Reporter: status.NewReporter(func(*componentstatus.InstanceID, *componentstatus.Event) {}, func(error) {})})) assert.Error(t, pipelines.ShutdownAll(context.Background(), statustest.NewNopStatusReporter())) }) @@ -2221,7 +2222,7 @@ func TestGraphFailToStartAndShutdown(t *testing.T) { } pipelines, err := Build(context.Background(), set) assert.NoError(t, err) - assert.Error(t, pipelines.StartAll(context.Background(), componenttest.NewNopHost(), statustest.NewNopStatusReporter())) + assert.Error(t, pipelines.StartAll(context.Background(), &Host{Reporter: status.NewReporter(func(*componentstatus.InstanceID, *componentstatus.Event) {}, func(error) {})})) assert.Error(t, pipelines.ShutdownAll(context.Background(), statustest.NewNopStatusReporter())) }) @@ -2241,7 +2242,7 @@ func TestGraphFailToStartAndShutdown(t *testing.T) { } pipelines, err := Build(context.Background(), set) assert.NoError(t, err) - assert.Error(t, pipelines.StartAll(context.Background(), componenttest.NewNopHost(), statustest.NewNopStatusReporter())) + assert.Error(t, pipelines.StartAll(context.Background(), &Host{Reporter: status.NewReporter(func(*componentstatus.InstanceID, *componentstatus.Event) {}, func(error) {})})) assert.Error(t, pipelines.ShutdownAll(context.Background(), statustest.NewNopStatusReporter())) }) } @@ -2258,7 +2259,7 @@ func TestStatusReportedOnStartupShutdown(t *testing.T) { eStErr := &testNode{id: component.MustNewIDWithName("e_st_err", "1"), startErr: assert.AnError} eSdErr := &testNode{id: component.MustNewIDWithName("e_sd_err", "1"), shutdownErr: assert.AnError} - instanceIDs := map[*testNode]*component.InstanceID{ + instanceIDs := map[*testNode]*componentstatus.InstanceID{ rNoErr: {ID: rNoErr.id}, rStErr: {ID: rStErr.id}, rSdErr: {ID: rSdErr.id}, @@ -2268,7 +2269,7 @@ func TestStatusReportedOnStartupShutdown(t *testing.T) { } // compare two maps of status events ignoring timestamp - assertEqualStatuses := func(t *testing.T, evMap1, evMap2 map[*component.InstanceID][]*component.StatusEvent) { + assertEqualStatuses := func(t *testing.T, evMap1, evMap2 map[*componentstatus.InstanceID][]*componentstatus.Event) { assert.Equal(t, len(evMap1), len(evMap2)) for id, evts1 := range evMap1 { evts2 := evMap2[id] @@ -2286,35 +2287,35 @@ func TestStatusReportedOnStartupShutdown(t *testing.T) { for _, tc := range []struct { name string edge [2]*testNode - expectedStatuses map[*component.InstanceID][]*component.StatusEvent + expectedStatuses map[*componentstatus.InstanceID][]*componentstatus.Event startupErr error shutdownErr error }{ { name: "successful startup/shutdown", edge: [2]*testNode{rNoErr, eNoErr}, - expectedStatuses: map[*component.InstanceID][]*component.StatusEvent{ + expectedStatuses: map[*componentstatus.InstanceID][]*componentstatus.Event{ instanceIDs[rNoErr]: { - component.NewStatusEvent(component.StatusStarting), - component.NewStatusEvent(component.StatusOK), - component.NewStatusEvent(component.StatusStopping), - component.NewStatusEvent(component.StatusStopped), + componentstatus.NewEvent(componentstatus.StatusStarting), + componentstatus.NewEvent(componentstatus.StatusOK), + componentstatus.NewEvent(componentstatus.StatusStopping), + componentstatus.NewEvent(componentstatus.StatusStopped), }, instanceIDs[eNoErr]: { - component.NewStatusEvent(component.StatusStarting), - component.NewStatusEvent(component.StatusOK), - component.NewStatusEvent(component.StatusStopping), - component.NewStatusEvent(component.StatusStopped), + componentstatus.NewEvent(componentstatus.StatusStarting), + componentstatus.NewEvent(componentstatus.StatusOK), + componentstatus.NewEvent(componentstatus.StatusStopping), + componentstatus.NewEvent(componentstatus.StatusStopped), }, }, }, { name: "early startup error", edge: [2]*testNode{rNoErr, eStErr}, - expectedStatuses: map[*component.InstanceID][]*component.StatusEvent{ + expectedStatuses: map[*componentstatus.InstanceID][]*componentstatus.Event{ instanceIDs[eStErr]: { - component.NewStatusEvent(component.StatusStarting), - component.NewPermanentErrorEvent(assert.AnError), + componentstatus.NewEvent(componentstatus.StatusStarting), + componentstatus.NewPermanentErrorEvent(assert.AnError), }, }, startupErr: assert.AnError, @@ -2322,16 +2323,16 @@ func TestStatusReportedOnStartupShutdown(t *testing.T) { { name: "late startup error", edge: [2]*testNode{rStErr, eNoErr}, - expectedStatuses: map[*component.InstanceID][]*component.StatusEvent{ + expectedStatuses: map[*componentstatus.InstanceID][]*componentstatus.Event{ instanceIDs[rStErr]: { - component.NewStatusEvent(component.StatusStarting), - component.NewPermanentErrorEvent(assert.AnError), + componentstatus.NewEvent(componentstatus.StatusStarting), + componentstatus.NewPermanentErrorEvent(assert.AnError), }, instanceIDs[eNoErr]: { - component.NewStatusEvent(component.StatusStarting), - component.NewStatusEvent(component.StatusOK), - component.NewStatusEvent(component.StatusStopping), - component.NewStatusEvent(component.StatusStopped), + componentstatus.NewEvent(componentstatus.StatusStarting), + componentstatus.NewEvent(componentstatus.StatusOK), + componentstatus.NewEvent(componentstatus.StatusStopping), + componentstatus.NewEvent(componentstatus.StatusStopped), }, }, startupErr: assert.AnError, @@ -2339,18 +2340,18 @@ func TestStatusReportedOnStartupShutdown(t *testing.T) { { name: "early shutdown error", edge: [2]*testNode{rSdErr, eNoErr}, - expectedStatuses: map[*component.InstanceID][]*component.StatusEvent{ + expectedStatuses: map[*componentstatus.InstanceID][]*componentstatus.Event{ instanceIDs[rSdErr]: { - component.NewStatusEvent(component.StatusStarting), - component.NewStatusEvent(component.StatusOK), - component.NewStatusEvent(component.StatusStopping), - component.NewPermanentErrorEvent(assert.AnError), + componentstatus.NewEvent(componentstatus.StatusStarting), + componentstatus.NewEvent(componentstatus.StatusOK), + componentstatus.NewEvent(componentstatus.StatusStopping), + componentstatus.NewPermanentErrorEvent(assert.AnError), }, instanceIDs[eNoErr]: { - component.NewStatusEvent(component.StatusStarting), - component.NewStatusEvent(component.StatusOK), - component.NewStatusEvent(component.StatusStopping), - component.NewStatusEvent(component.StatusStopped), + componentstatus.NewEvent(componentstatus.StatusStarting), + componentstatus.NewEvent(componentstatus.StatusOK), + componentstatus.NewEvent(componentstatus.StatusStopping), + componentstatus.NewEvent(componentstatus.StatusStopped), }, }, shutdownErr: assert.AnError, @@ -2358,18 +2359,18 @@ func TestStatusReportedOnStartupShutdown(t *testing.T) { { name: "late shutdown error", edge: [2]*testNode{rNoErr, eSdErr}, - expectedStatuses: map[*component.InstanceID][]*component.StatusEvent{ + expectedStatuses: map[*componentstatus.InstanceID][]*componentstatus.Event{ instanceIDs[rNoErr]: { - component.NewStatusEvent(component.StatusStarting), - component.NewStatusEvent(component.StatusOK), - component.NewStatusEvent(component.StatusStopping), - component.NewStatusEvent(component.StatusStopped), + componentstatus.NewEvent(componentstatus.StatusStarting), + componentstatus.NewEvent(componentstatus.StatusOK), + componentstatus.NewEvent(componentstatus.StatusStopping), + componentstatus.NewEvent(componentstatus.StatusStopped), }, instanceIDs[eSdErr]: { - component.NewStatusEvent(component.StatusStarting), - component.NewStatusEvent(component.StatusOK), - component.NewStatusEvent(component.StatusStopping), - component.NewPermanentErrorEvent(assert.AnError), + componentstatus.NewEvent(componentstatus.StatusStarting), + componentstatus.NewEvent(componentstatus.StatusOK), + componentstatus.NewEvent(componentstatus.StatusStopping), + componentstatus.NewPermanentErrorEvent(assert.AnError), }, }, shutdownErr: assert.AnError, @@ -2379,8 +2380,8 @@ func TestStatusReportedOnStartupShutdown(t *testing.T) { pg := &Graph{componentGraph: simple.NewDirectedGraph()} pg.telemetry = componenttest.NewNopTelemetrySettings() - actualStatuses := make(map[*component.InstanceID][]*component.StatusEvent) - rep := status.NewReporter(func(id *component.InstanceID, ev *component.StatusEvent) { + actualStatuses := make(map[*componentstatus.InstanceID][]*componentstatus.Event) + rep := status.NewReporter(func(id *componentstatus.InstanceID, ev *componentstatus.Event) { actualStatuses[id] = append(actualStatuses[id], ev) }, func(error) { }) @@ -2388,13 +2389,13 @@ func TestStatusReportedOnStartupShutdown(t *testing.T) { rep.Ready() e0, e1 := tc.edge[0], tc.edge[1] - pg.instanceIDs = map[int64]*component.InstanceID{ + pg.instanceIDs = map[int64]*componentstatus.InstanceID{ e0.ID(): instanceIDs[e0], e1.ID(): instanceIDs[e1], } pg.componentGraph.SetEdge(simple.Edge{F: e0, T: e1}) - assert.Equal(t, tc.startupErr, pg.StartAll(context.Background(), componenttest.NewNopHost(), rep)) + assert.Equal(t, tc.startupErr, pg.StartAll(context.Background(), &Host{Reporter: rep})) assert.Equal(t, tc.shutdownErr, pg.ShutdownAll(context.Background(), rep)) assertEqualStatuses(t, tc.expectedStatuses, actualStatuses) }) @@ -2424,7 +2425,7 @@ func (g *Graph) getReceivers() map[component.DataType]map[component.ID]component // // Expect one instance of each receiver and exporter, unless it is a connector. // -// For connectors: +// For Connectors: // - Let E equal the number of pipeline types in which the connector is used as an exporter. // - Let R equal the number of pipeline types in which the connector is used as a receiver. // diff --git a/service/internal/graph/host.go b/service/internal/graph/host.go index b954d2b2c58..ef8ab73e6fb 100644 --- a/service/internal/graph/host.go +++ b/service/internal/graph/host.go @@ -10,6 +10,7 @@ import ( "time" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/connector" "go.opentelemetry.io/collector/exporter" "go.opentelemetry.io/collector/extension" @@ -17,6 +18,7 @@ import ( "go.opentelemetry.io/collector/processor" "go.opentelemetry.io/collector/receiver" "go.opentelemetry.io/collector/service/extensions" + "go.opentelemetry.io/collector/service/internal/status" "go.opentelemetry.io/collector/service/internal/zpages" ) @@ -40,6 +42,8 @@ type Host struct { Pipelines *Graph ServiceExtensions *extensions.Extensions + + Reporter status.Reporter } func (host *Host) GetFactory(kind component.Kind, componentType component.Type) component.Factory { @@ -72,9 +76,9 @@ func (host *Host) GetExporters() map[component.DataType]map[component.ID]compone return host.Pipelines.GetExporters() } -func (host *Host) NotifyComponentStatusChange(source *component.InstanceID, event *component.StatusEvent) { +func (host *Host) NotifyComponentStatusChange(source *componentstatus.InstanceID, event *componentstatus.Event) { host.ServiceExtensions.NotifyComponentStatusChange(source, event) - if event.Status() == component.StatusFatalError { + if event.Status() == componentstatus.StatusFatalError { host.AsyncErrorChannel <- event.Err() } } diff --git a/service/internal/status/status.go b/service/internal/status/status.go index 9fac2a8d6bf..0fde07e774c 100644 --- a/service/internal/status/status.go +++ b/service/internal/status/status.go @@ -8,26 +8,26 @@ import ( "fmt" "sync" - "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" ) -// onTransitionFunc receives a component.StatusEvent on a successful state transition -type onTransitionFunc func(*component.StatusEvent) +// onTransitionFunc receives a componentstatus.Event on a successful state transition +type onTransitionFunc func(*componentstatus.Event) // errInvalidStateTransition is returned for invalid state transitions var errInvalidStateTransition = errors.New("invalid state transition") // fsm is a finite state machine that models transitions for component status type fsm struct { - current *component.StatusEvent - transitions map[component.Status]map[component.Status]struct{} + current *componentstatus.Event + transitions map[componentstatus.Status]map[componentstatus.Status]struct{} onTransition onTransitionFunc } // transition will attempt to execute a state transition. If it's successful, it calls the -// onTransitionFunc with a StatusEvent representing the new state. Returns an error if the arguments +// onTransitionFunc with a Event representing the new state. Returns an error if the arguments // result in an invalid status, or if the state transition is not valid. -func (m *fsm) transition(ev *component.StatusEvent) error { +func (m *fsm) transition(ev *componentstatus.Event) error { if _, ok := m.transitions[m.current.Status()][ev.Status()]; !ok { return fmt.Errorf( "cannot transition from %s to %s: %w", @@ -41,56 +41,56 @@ func (m *fsm) transition(ev *component.StatusEvent) error { return nil } -// newFSM creates a state machine with all valid transitions for component.Status. -// The initial state is set to component.StatusNone. +// newFSM creates a state machine with all valid transitions for componentstatus.Status. +// The initial state is set to componentstatus.StatusNone. func newFSM(onTransition onTransitionFunc) *fsm { return &fsm{ - current: component.NewStatusEvent(component.StatusNone), + current: componentstatus.NewEvent(componentstatus.StatusNone), onTransition: onTransition, - transitions: map[component.Status]map[component.Status]struct{}{ - component.StatusNone: { - component.StatusStarting: {}, + transitions: map[componentstatus.Status]map[componentstatus.Status]struct{}{ + componentstatus.StatusNone: { + componentstatus.StatusStarting: {}, }, - component.StatusStarting: { - component.StatusOK: {}, - component.StatusRecoverableError: {}, - component.StatusPermanentError: {}, - component.StatusFatalError: {}, - component.StatusStopping: {}, + componentstatus.StatusStarting: { + componentstatus.StatusOK: {}, + componentstatus.StatusRecoverableError: {}, + componentstatus.StatusPermanentError: {}, + componentstatus.StatusFatalError: {}, + componentstatus.StatusStopping: {}, }, - component.StatusOK: { - component.StatusRecoverableError: {}, - component.StatusPermanentError: {}, - component.StatusFatalError: {}, - component.StatusStopping: {}, + componentstatus.StatusOK: { + componentstatus.StatusRecoverableError: {}, + componentstatus.StatusPermanentError: {}, + componentstatus.StatusFatalError: {}, + componentstatus.StatusStopping: {}, }, - component.StatusRecoverableError: { - component.StatusOK: {}, - component.StatusPermanentError: {}, - component.StatusFatalError: {}, - component.StatusStopping: {}, + componentstatus.StatusRecoverableError: { + componentstatus.StatusOK: {}, + componentstatus.StatusPermanentError: {}, + componentstatus.StatusFatalError: {}, + componentstatus.StatusStopping: {}, }, - component.StatusPermanentError: {}, - component.StatusFatalError: {}, - component.StatusStopping: { - component.StatusRecoverableError: {}, - component.StatusPermanentError: {}, - component.StatusFatalError: {}, - component.StatusStopped: {}, + componentstatus.StatusPermanentError: {}, + componentstatus.StatusFatalError: {}, + componentstatus.StatusStopping: { + componentstatus.StatusRecoverableError: {}, + componentstatus.StatusPermanentError: {}, + componentstatus.StatusFatalError: {}, + componentstatus.StatusStopped: {}, }, - component.StatusStopped: {}, + componentstatus.StatusStopped: {}, }, } } // NotifyStatusFunc is the receiver of status events after successful state transitions -type NotifyStatusFunc func(*component.InstanceID, *component.StatusEvent) +type NotifyStatusFunc func(*componentstatus.InstanceID, *componentstatus.Event) // InvalidTransitionFunc is the receiver of invalid transition errors type InvalidTransitionFunc func(error) // ServiceStatusFunc is the expected type of ReportStatus -type ServiceStatusFunc func(*component.InstanceID, *component.StatusEvent) +type ServiceStatusFunc func(*componentstatus.InstanceID, *componentstatus.Event) // ErrStatusNotReady is returned when trying to report status before service start var ErrStatusNotReady = errors.New("report component status is not ready until service start") @@ -98,14 +98,14 @@ var ErrStatusNotReady = errors.New("report component status is not ready until s // Reporter handles component status reporting type Reporter interface { Ready() - ReportStatus(id *component.InstanceID, ev *component.StatusEvent) - ReportOKIfStarting(id *component.InstanceID) + ReportStatus(id *componentstatus.InstanceID, ev *componentstatus.Event) + ReportOKIfStarting(id *componentstatus.InstanceID) } type reporter struct { mu sync.Mutex ready bool - fsmMap map[*component.InstanceID]*fsm + fsmMap map[*componentstatus.InstanceID]*fsm onStatusChange NotifyStatusFunc onInvalidTransition InvalidTransitionFunc } @@ -114,7 +114,7 @@ type reporter struct { // has changed. func NewReporter(onStatusChange NotifyStatusFunc, onInvalidTransition InvalidTransitionFunc) Reporter { return &reporter{ - fsmMap: make(map[*component.InstanceID]*fsm), + fsmMap: make(map[*componentstatus.InstanceID]*fsm), onStatusChange: onStatusChange, onInvalidTransition: onInvalidTransition, } @@ -129,8 +129,8 @@ func (r *reporter) Ready() { // ReportStatus reports status for the given InstanceID func (r *reporter) ReportStatus( - id *component.InstanceID, - ev *component.StatusEvent, + id *componentstatus.InstanceID, + ev *componentstatus.Event, ) { r.mu.Lock() defer r.mu.Unlock() @@ -143,36 +143,36 @@ func (r *reporter) ReportStatus( } } -func (r *reporter) ReportOKIfStarting(id *component.InstanceID) { +func (r *reporter) ReportOKIfStarting(id *componentstatus.InstanceID) { r.mu.Lock() defer r.mu.Unlock() if !r.ready { r.onInvalidTransition(ErrStatusNotReady) } fsm := r.componentFSM(id) - if fsm.current.Status() == component.StatusStarting { - if err := fsm.transition(component.NewStatusEvent(component.StatusOK)); err != nil { + if fsm.current.Status() == componentstatus.StatusStarting { + if err := fsm.transition(componentstatus.NewEvent(componentstatus.StatusOK)); err != nil { r.onInvalidTransition(err) } } } // Note: a lock must be acquired before calling this method. -func (r *reporter) componentFSM(id *component.InstanceID) *fsm { +func (r *reporter) componentFSM(id *componentstatus.InstanceID) *fsm { fsm, ok := r.fsmMap[id] if !ok { - fsm = newFSM(func(ev *component.StatusEvent) { r.onStatusChange(id, ev) }) + fsm = newFSM(func(ev *componentstatus.Event) { r.onStatusChange(id, ev) }) r.fsmMap[id] = fsm } return fsm } -// NewReportStatusFunc returns a function to be used as ReportStatus for component.TelemetrySettings +// NewReportStatusFunc returns a function to be used as ReportStatus for componentstatus.TelemetrySettings func NewReportStatusFunc( - id *component.InstanceID, + id *componentstatus.InstanceID, srvStatus ServiceStatusFunc, -) func(*component.StatusEvent) { - return func(ev *component.StatusEvent) { +) func(*componentstatus.Event) { + return func(ev *componentstatus.Event) { srvStatus(id, ev) } } diff --git a/service/internal/status/status_test.go b/service/internal/status/status_test.go index c31c649e496..ff61e138242 100644 --- a/service/internal/status/status_test.go +++ b/service/internal/status/status_test.go @@ -10,129 +10,129 @@ import ( "github.com/stretchr/testify/require" - "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" ) func TestStatusFSM(t *testing.T) { for _, tc := range []struct { name string - reportedStatuses []component.Status - expectedStatuses []component.Status + reportedStatuses []componentstatus.Status + expectedStatuses []componentstatus.Status expectedErrorCount int }{ { name: "successful startup and shutdown", - reportedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, + reportedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusStopped, }, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusStopped, }, }, { name: "component recovered", - reportedStatuses: []component.Status{ - component.StatusStarting, - component.StatusRecoverableError, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, + reportedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusRecoverableError, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusStopped, }, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusRecoverableError, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusRecoverableError, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusStopped, }, }, { name: "repeated events are errors", - reportedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusRecoverableError, - component.StatusRecoverableError, - component.StatusRecoverableError, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, + reportedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusRecoverableError, + componentstatus.StatusRecoverableError, + componentstatus.StatusRecoverableError, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusStopped, }, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusRecoverableError, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusRecoverableError, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusStopped, }, expectedErrorCount: 2, }, { name: "PermanentError is terminal", - reportedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusPermanentError, - component.StatusOK, + reportedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusPermanentError, + componentstatus.StatusOK, }, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusPermanentError, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusPermanentError, }, expectedErrorCount: 1, }, { name: "FatalError is terminal", - reportedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusFatalError, - component.StatusOK, + reportedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusFatalError, + componentstatus.StatusOK, }, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusFatalError, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusFatalError, }, expectedErrorCount: 1, }, { name: "Stopped is terminal", - reportedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, - component.StatusOK, + reportedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusStopped, + componentstatus.StatusOK, }, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusStopped, }, expectedErrorCount: 1, }, } { t.Run(tc.name, func(t *testing.T) { - var receivedStatuses []component.Status + var receivedStatuses []componentstatus.Status fsm := newFSM( - func(ev *component.StatusEvent) { + func(ev *componentstatus.Event) { receivedStatuses = append(receivedStatuses, ev.Status()) }, ) errorCount := 0 for _, status := range tc.reportedStatuses { - if err := fsm.transition(component.NewStatusEvent(status)); err != nil { + if err := fsm.transition(componentstatus.NewEvent(status)); err != nil { errorCount++ require.ErrorIs(t, err, errInvalidStateTransition) } @@ -145,33 +145,33 @@ func TestStatusFSM(t *testing.T) { } func TestValidSeqsToStopped(t *testing.T) { - events := []*component.StatusEvent{ - component.NewStatusEvent(component.StatusStarting), - component.NewStatusEvent(component.StatusOK), - component.NewStatusEvent(component.StatusRecoverableError), - component.NewStatusEvent(component.StatusPermanentError), - component.NewStatusEvent(component.StatusFatalError), + events := []*componentstatus.Event{ + componentstatus.NewEvent(componentstatus.StatusStarting), + componentstatus.NewEvent(componentstatus.StatusOK), + componentstatus.NewEvent(componentstatus.StatusRecoverableError), + componentstatus.NewEvent(componentstatus.StatusPermanentError), + componentstatus.NewEvent(componentstatus.StatusFatalError), } for _, ev := range events { - name := fmt.Sprintf("transition from: %s to: %s invalid", ev.Status(), component.StatusStopped) + name := fmt.Sprintf("transition from: %s to: %s invalid", ev.Status(), componentstatus.StatusStopped) t.Run(name, func(t *testing.T) { - fsm := newFSM(func(*component.StatusEvent) {}) - if ev.Status() != component.StatusStarting { - require.NoError(t, fsm.transition(component.NewStatusEvent(component.StatusStarting))) + fsm := newFSM(func(*componentstatus.Event) {}) + if ev.Status() != componentstatus.StatusStarting { + require.NoError(t, fsm.transition(componentstatus.NewEvent(componentstatus.StatusStarting))) } require.NoError(t, fsm.transition(ev)) // skipping to stopped is not allowed - err := fsm.transition(component.NewStatusEvent(component.StatusStopped)) + err := fsm.transition(componentstatus.NewEvent(componentstatus.StatusStopped)) require.ErrorIs(t, err, errInvalidStateTransition) // stopping -> stopped is allowed for non-fatal, non-permanent errors - err = fsm.transition(component.NewStatusEvent(component.StatusStopping)) - if ev.Status() == component.StatusPermanentError || ev.Status() == component.StatusFatalError { + err = fsm.transition(componentstatus.NewEvent(componentstatus.StatusStopping)) + if ev.Status() == componentstatus.StatusPermanentError || ev.Status() == componentstatus.StatusFatalError { require.ErrorIs(t, err, errInvalidStateTransition) } else { require.NoError(t, err) - require.NoError(t, fsm.transition(component.NewStatusEvent(component.StatusStopped))) + require.NoError(t, fsm.transition(componentstatus.NewEvent(componentstatus.StatusStopped))) } }) } @@ -179,31 +179,31 @@ func TestValidSeqsToStopped(t *testing.T) { } func TestStatusFuncs(t *testing.T) { - id1 := &component.InstanceID{} - id2 := &component.InstanceID{} + id1 := &componentstatus.InstanceID{} + id2 := &componentstatus.InstanceID{} - actualStatuses := make(map[*component.InstanceID][]component.Status) - statusFunc := func(id *component.InstanceID, ev *component.StatusEvent) { + actualStatuses := make(map[*componentstatus.InstanceID][]componentstatus.Status) + statusFunc := func(id *componentstatus.InstanceID, ev *componentstatus.Event) { actualStatuses[id] = append(actualStatuses[id], ev.Status()) } - statuses1 := []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, + statuses1 := []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusStopped, } - statuses2 := []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusRecoverableError, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, + statuses2 := []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusRecoverableError, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusStopped, } - expectedStatuses := map[*component.InstanceID][]component.Status{ + expectedStatuses := map[*componentstatus.InstanceID][]componentstatus.Status{ id1: statuses1, id2: statuses2, } @@ -217,20 +217,20 @@ func TestStatusFuncs(t *testing.T) { rep.Ready() for _, st := range statuses1 { - comp1Func(component.NewStatusEvent(st)) + comp1Func(componentstatus.NewEvent(st)) } for _, st := range statuses2 { - comp2Func(component.NewStatusEvent(st)) + comp2Func(componentstatus.NewEvent(st)) } require.Equal(t, expectedStatuses, actualStatuses) } func TestStatusFuncsConcurrent(t *testing.T) { - ids := []*component.InstanceID{{}, {}, {}, {}} + ids := []*componentstatus.InstanceID{{}, {}, {}, {}} count := 0 - statusFunc := func(*component.InstanceID, *component.StatusEvent) { + statusFunc := func(*componentstatus.InstanceID, *componentstatus.Event) { count++ } rep := NewReporter(statusFunc, @@ -246,10 +246,10 @@ func TestStatusFuncsConcurrent(t *testing.T) { id := id go func() { compFn := NewReportStatusFunc(id, rep.ReportStatus) - compFn(component.NewStatusEvent(component.StatusStarting)) + compFn(componentstatus.NewEvent(componentstatus.StatusStarting)) for i := 0; i < 1000; i++ { - compFn(component.NewStatusEvent(component.StatusRecoverableError)) - compFn(component.NewStatusEvent(component.StatusOK)) + compFn(componentstatus.NewEvent(componentstatus.StatusRecoverableError)) + compFn(componentstatus.NewEvent(componentstatus.StatusOK)) } wg.Done() }() @@ -260,78 +260,78 @@ func TestStatusFuncsConcurrent(t *testing.T) { } func TestReporterReady(t *testing.T) { - statusFunc := func(*component.InstanceID, *component.StatusEvent) {} + statusFunc := func(*componentstatus.InstanceID, *componentstatus.Event) {} var err error rep := NewReporter(statusFunc, func(e error) { err = e }) - id := &component.InstanceID{} + id := &componentstatus.InstanceID{} - rep.ReportStatus(id, component.NewStatusEvent(component.StatusStarting)) + rep.ReportStatus(id, componentstatus.NewEvent(componentstatus.StatusStarting)) require.ErrorIs(t, err, ErrStatusNotReady) rep.Ready() err = nil - rep.ReportStatus(id, component.NewStatusEvent(component.StatusStarting)) + rep.ReportStatus(id, componentstatus.NewEvent(componentstatus.StatusStarting)) require.NoError(t, err) } func TestReportComponentOKIfStarting(t *testing.T) { for _, tc := range []struct { name string - initialStatuses []component.Status - expectedStatuses []component.Status + initialStatuses []componentstatus.Status + expectedStatuses []componentstatus.Status }{ { name: "matching condition: StatusStarting", - initialStatuses: []component.Status{ - component.StatusStarting, + initialStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, }, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, }, }, { name: "non-matching condition StatusOK", - initialStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, + initialStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, }, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, }, }, { name: "non-matching condition RecoverableError", - initialStatuses: []component.Status{ - component.StatusStarting, - component.StatusRecoverableError, + initialStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusRecoverableError, }, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusRecoverableError, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusRecoverableError, }, }, { name: "non-matching condition PermanentError", - initialStatuses: []component.Status{ - component.StatusStarting, - component.StatusPermanentError, + initialStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusPermanentError, }, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusPermanentError, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusPermanentError, }, }, } { t.Run(tc.name, func(t *testing.T) { - var receivedStatuses []component.Status + var receivedStatuses []componentstatus.Status rep := NewReporter( - func(_ *component.InstanceID, ev *component.StatusEvent) { + func(_ *componentstatus.InstanceID, ev *componentstatus.Event) { receivedStatuses = append(receivedStatuses, ev.Status()) }, func(err error) { @@ -340,9 +340,9 @@ func TestReportComponentOKIfStarting(t *testing.T) { ) rep.Ready() - id := &component.InstanceID{} + id := &componentstatus.InstanceID{} for _, status := range tc.initialStatuses { - rep.ReportStatus(id, component.NewStatusEvent(status)) + rep.ReportStatus(id, componentstatus.NewEvent(status)) } rep.ReportOKIfStarting(id) diff --git a/service/internal/status/statustest/statustest.go b/service/internal/status/statustest/statustest.go index 3ba40a16298..881db196a49 100644 --- a/service/internal/status/statustest/statustest.go +++ b/service/internal/status/statustest/statustest.go @@ -4,7 +4,7 @@ package statustest // import "go.opentelemetry.io/collector/service/internal/status/statustest" import ( - "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/service/internal/status" ) @@ -16,6 +16,6 @@ type nopStatusReporter struct{} func (r *nopStatusReporter) Ready() {} -func (r *nopStatusReporter) ReportStatus(*component.InstanceID, *component.StatusEvent) {} +func (r *nopStatusReporter) ReportStatus(*componentstatus.InstanceID, *componentstatus.Event) {} -func (r *nopStatusReporter) ReportOKIfStarting(*component.InstanceID) {} +func (r *nopStatusReporter) ReportOKIfStarting(*componentstatus.InstanceID) {} diff --git a/service/service.go b/service/service.go index 642f7393652..a4d18690429 100644 --- a/service/service.go +++ b/service/service.go @@ -71,8 +71,6 @@ type Service struct { telemetrySettings component.TelemetrySettings host *graph.Host collectorConf *confmap.Conf - - reporter status.Reporter } // New creates a new Service, its telemetry, and Components. @@ -136,7 +134,7 @@ func New(ctx context.Context, set Settings, cfg Config) (*Service, error) { // Construct telemetry attributes from build info and config's resource attributes. Resource: pcommonRes, } - srv.reporter = status.NewReporter(srv.host.NotifyComponentStatusChange, func(err error) { + srv.host.Reporter = status.NewReporter(srv.host.NotifyComponentStatusChange, func(err error) { if errors.Is(err, status.ErrStatusNotReady) { logger.Warn("Invalid transition", zap.Error(err)) } @@ -198,7 +196,7 @@ func (srv *Service) Start(ctx context.Context) error { ) // enable status reporting - srv.reporter.Ready() + srv.host.Reporter.Ready() if err := srv.host.ServiceExtensions.Start(ctx, srv.host); err != nil { return fmt.Errorf("failed to start extensions: %w", err) @@ -210,7 +208,7 @@ func (srv *Service) Start(ctx context.Context) error { } } - if err := srv.host.Pipelines.StartAll(ctx, srv.host, srv.reporter); err != nil { + if err := srv.host.Pipelines.StartAll(ctx, srv.host); err != nil { return fmt.Errorf("cannot start pipelines: %w", err) } @@ -261,7 +259,7 @@ func (srv *Service) Shutdown(ctx context.Context) error { errs = multierr.Append(errs, fmt.Errorf("failed to notify that pipeline is not ready: %w", err)) } - if err := srv.host.Pipelines.ShutdownAll(ctx, srv.reporter); err != nil { + if err := srv.host.Pipelines.ShutdownAll(ctx, srv.host.Reporter); err != nil { errs = multierr.Append(errs, fmt.Errorf("failed to shutdown pipelines: %w", err)) } @@ -284,7 +282,7 @@ func (srv *Service) initExtensions(ctx context.Context, cfg extensions.Config) e BuildInfo: srv.buildInfo, Extensions: srv.host.Extensions, } - if srv.host.ServiceExtensions, err = extensions.New(ctx, extensionsSettings, cfg, extensions.WithReporter(srv.reporter)); err != nil { + if srv.host.ServiceExtensions, err = extensions.New(ctx, extensionsSettings, cfg, extensions.WithReporter(srv.host.Reporter)); err != nil { return fmt.Errorf("failed to build extensions: %w", err) } return nil @@ -301,7 +299,7 @@ func (srv *Service) initGraph(ctx context.Context, set Settings, cfg Config) err ExporterBuilder: set.Exporters, ConnectorBuilder: set.Connectors, PipelineConfigs: cfg.Pipelines, - ReportStatus: srv.reporter.ReportStatus, + ReportStatus: srv.host.Reporter.ReportStatus, }); err != nil { return fmt.Errorf("failed to build pipelines: %w", err) } diff --git a/service/service_test.go b/service/service_test.go index 6e1e43159ec..791b733fc3b 100644 --- a/service/service_test.go +++ b/service/service_test.go @@ -21,6 +21,7 @@ import ( "go.uber.org/zap/zapcore" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/config/confighttp" "go.opentelemetry.io/collector/config/configtelemetry" "go.opentelemetry.io/collector/confmap" @@ -443,8 +444,8 @@ func TestServiceFatalError(t *testing.T) { }) go func() { - ev := component.NewFatalErrorEvent(assert.AnError) - srv.host.NotifyComponentStatusChange(&component.InstanceID{}, ev) + ev := componentstatus.NewFatalErrorEvent(assert.AnError) + srv.host.NotifyComponentStatusChange(&componentstatus.InstanceID{}, ev) }() err = <-srv.host.AsyncErrorChannel