From 67653877f7b92dcfabf862760450bcc1b7055b59 Mon Sep 17 00:00:00 2001 From: Tyler Helmuth <12352919+TylerHelmuth@users.noreply.github.com> Date: Wed, 31 Jul 2024 14:54:44 -0600 Subject: [PATCH] Remove ReportStatus from component.TelemetrySettings --- ...nent-remove-reporting-from-struct-2-2.yaml | 25 ++ ...ponent-remove-reporting-from-struct-2.yaml | 25 ++ cmd/builder/internal/builder/main_test.go | 1 + cmd/builder/test/core.builder.yaml | 1 + cmd/mdatagen/go.mod | 2 + cmd/otelcorecol/builder-config.yaml | 1 + cmd/otelcorecol/go.mod | 3 + component/component.go | 7 - component/componentstatus/status.go | 19 + component/componenttest/nop_telemetry.go | 2 - component/status.go | 200 ----------- component/status_test.go | 330 ------------------ component/telemetry.go | 6 - config/configauth/go.mod | 2 + config/configgrpc/go.mod | 2 + config/confighttp/go.mod | 2 + config/internal/go.mod | 2 + connector/forwardconnector/go.mod | 2 + connector/go.mod | 2 + exporter/debugexporter/go.mod | 2 + exporter/exporterprofiles/go.mod | 2 + exporter/go.mod | 3 + exporter/loggingexporter/go.mod | 2 + exporter/nopexporter/go.mod | 2 + exporter/otlpexporter/go.mod | 2 + exporter/otlphttpexporter/go.mod | 2 + extension/auth/go.mod | 2 + extension/ballastextension/go.mod | 3 + extension/extension.go | 11 - .../extensiontest/statuswatcher_extension.go | 7 +- .../statuswatcher_extension_test.go | 6 +- extension/go.mod | 3 + extension/memorylimiterextension/go.mod | 3 + extension/zpagesextension/go.mod | 3 + extension/zpagesextension/zpagesextension.go | 4 +- go.mod | 3 + internal/e2e/go.mod | 36 +- internal/e2e/go.sum | 110 ++++++ internal/e2e/status_test.go | 262 ++++++++++++++ internal/sharedcomponent/sharedcomponent.go | 112 ++++-- .../sharedcomponent/sharedcomponent_test.go | 146 +++----- otelcol/collector_test.go | 25 +- otelcol/go.mod | 3 + otelcol/otelcoltest/go.mod | 3 + processor/batchprocessor/go.mod | 3 + processor/go.mod | 3 + processor/memorylimiterprocessor/go.mod | 3 + processor/processorprofiles/go.mod | 2 + .../processortest/unhealthy_processor.go | 5 +- receiver/go.mod | 2 + receiver/nopreceiver/go.mod | 2 + receiver/otlpreceiver/go.mod | 3 + receiver/otlpreceiver/otlp.go | 6 +- receiver/receiverprofiles/go.mod | 2 + service/extensions/extensions.go | 26 +- service/extensions/extensions_test.go | 36 +- service/go.mod | 3 + service/host.go | 74 ---- service/internal/graph/graph.go | 66 ++-- service/internal/graph/graph_test.go | 146 ++++---- service/internal/graph/host.go | 168 +++++++++ service/internal/status/status.go | 108 +++--- service/internal/status/status_test.go | 288 +++++++-------- .../internal/status/statustest/statustest.go | 6 +- service/service.go | 46 ++- service/service_test.go | 9 +- service/zpages.go | 99 ------ 67 files changed, 1254 insertions(+), 1243 deletions(-) create mode 100644 .chloggen/component-remove-reporting-from-struct-2-2.yaml create mode 100644 .chloggen/component-remove-reporting-from-struct-2.yaml delete mode 100644 component/status.go delete mode 100644 component/status_test.go create mode 100644 internal/e2e/status_test.go delete mode 100644 service/host.go create mode 100644 service/internal/graph/host.go delete mode 100644 service/zpages.go 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.yaml b/.chloggen/component-remove-reporting-from-struct-2.yaml new file mode 100644 index 00000000000..31a9079763d --- /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` + +# 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 3c6a614e609..62e5ef207fa 100644 --- a/cmd/builder/internal/builder/main_test.go +++ b/cmd/builder/internal/builder/main_test.go @@ -41,6 +41,7 @@ var ( replaceModules = []string{ "", "/component", + "/component/componentstatus", "/client", "/config/configauth", "/config/configcompression", diff --git a/cmd/builder/test/core.builder.yaml b/cmd/builder/test/core.builder.yaml index 33c8d3ed39a..cf0be568117 100644 --- a/cmd/builder/test/core.builder.yaml +++ b/cmd/builder/test/core.builder.yaml @@ -21,6 +21,7 @@ replaces: - go.opentelemetry.io/collector => ${WORKSPACE_DIR} - go.opentelemetry.io/collector/client => ${WORKSPACE_DIR}/client - go.opentelemetry.io/collector/component => ${WORKSPACE_DIR}/component + - 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 945d2d13c8a..7467157a81f 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 ec2501f5706..f0ad3fa9c5a 100644 --- a/cmd/otelcorecol/builder-config.yaml +++ b/cmd/otelcorecol/builder-config.yaml @@ -45,6 +45,7 @@ replaces: - go.opentelemetry.io/collector/client => ../../client - go.opentelemetry.io/collector/otelcol => ../../otelcol - go.opentelemetry.io/collector/component => ../../component + - 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 a66b1d94807..bff1d653999 100644 --- a/cmd/otelcorecol/go.mod +++ b/cmd/otelcorecol/go.mod @@ -82,6 +82,7 @@ require ( go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/collector v0.106.1 // indirect go.opentelemetry.io/collector/client v0.106.1 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.106.1 // indirect go.opentelemetry.io/collector/config/configauth v0.106.1 // indirect go.opentelemetry.io/collector/config/configcompression v1.12.0 // indirect go.opentelemetry.io/collector/config/configgrpc v0.106.1 // indirect @@ -148,6 +149,8 @@ replace go.opentelemetry.io/collector/otelcol => ../../otelcol replace go.opentelemetry.io/collector/component => ../../component +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/componentstatus/status.go b/component/componentstatus/status.go index 6f016cf0797..7e2c7d9dd40 100644 --- a/component/componentstatus/status.go +++ b/component/componentstatus/status.go @@ -11,8 +11,18 @@ package componentstatus // import "go.opentelemetry.io/collector/component/compo import ( "time" + + "go.opentelemetry.io/collector/component" ) +type Reporter interface { + // Report 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 + Report(*Event) +} + // Watcher is an extra interface for Extension hosted by the OpenTelemetry // Collector that is to be implemented by extensions interested in changes to component // status. @@ -137,3 +147,12 @@ func StatusIsError(status Status) bool { status == StatusPermanentError || status == StatusFatalError } + +// ReportStatus is a helper function that handles checking if the component.Host has implemented Reporter. +// If it has, the Event is reported. Otherwise, nothing happens. +func ReportStatus(host component.Host, e *Event) { + statusReporter, ok := host.(Reporter) + if ok { + statusReporter.Report(e) + } +} 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 726afb2fb6d..f74768c6891 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 41fcc197e8d..70f866261c0 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 daf3d0bab62..433ec2400c7 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 6fbb3e70c35..c6325b0d2ae 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/forwardconnector/go.mod b/connector/forwardconnector/go.mod index 91422951a2f..c99d337ab6b 100644 --- a/connector/forwardconnector/go.mod +++ b/connector/forwardconnector/go.mod @@ -90,3 +90,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/connector/go.mod b/connector/go.mod index e7e28230e29..b9c315a3c5c 100644 --- a/connector/go.mod +++ b/connector/go.mod @@ -73,3 +73,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/exporter/debugexporter/go.mod b/exporter/debugexporter/go.mod index 0e694fafcbe..2d73fdfaa6a 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 671b966730f..a1b40f3cc3d 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 ba1f152b002..55bf628c853 100644 --- a/exporter/go.mod +++ b/exporter/go.mod @@ -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.106.1 // indirect go.opentelemetry.io/collector/confmap v0.106.1 // indirect go.opentelemetry.io/collector/featuregate v1.12.0 // indirect go.opentelemetry.io/collector/internal/globalgates v0.106.1 // 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/loggingexporter/go.mod b/exporter/loggingexporter/go.mod index 237ff759c93..1a3643cddde 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 a666d54d430..ca8c5db3e3a 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 618afb9ab21..9eaa6c4e369 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 401fc7f6da4..06680a4d1c9 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 a818c203cdc..bad3080727e 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 b4637b67229..f31a2360e1f 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.106.1 // indirect go.opentelemetry.io/collector/config/configtelemetry v0.106.1 // indirect go.opentelemetry.io/collector/featuregate v1.12.0 // indirect go.opentelemetry.io/collector/internal/globalgates v0.106.1 // 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 f52b45547d9..ef6260b140f 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.106.1 + go.opentelemetry.io/collector/component/componentstatus v0.0.0-20240731165531-6d32c09daf9f go.opentelemetry.io/collector/confmap v0.106.1 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 8d4b7d1a870..a7a751a80e1 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.106.1 // indirect go.opentelemetry.io/collector/config/configtelemetry v0.106.1 // indirect go.opentelemetry.io/collector/featuregate v1.12.0 // indirect go.opentelemetry.io/collector/internal/globalgates v0.106.1 // 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 1f2c7fdb509..64e9a40979d 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.106.1 go.opentelemetry.io/collector/component v0.106.1 + go.opentelemetry.io/collector/component/componentstatus v0.106.1 go.opentelemetry.io/collector/config/configauth v0.106.1 go.opentelemetry.io/collector/config/confighttp v0.106.1 go.opentelemetry.io/collector/confmap v0.106.1 @@ -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..8ed162f198f 100644 --- a/extension/zpagesextension/zpagesextension.go +++ b/extension/zpagesextension/zpagesextension.go @@ -13,6 +13,8 @@ import ( "go.opentelemetry.io/otel/sdk/trace" "go.uber.org/zap" + "go.opentelemetry.io/collector/component/componentstatus" + "go.opentelemetry.io/collector/component" ) @@ -83,7 +85,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 24a3101b7c5..b5fe55baf09 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/shirou/gopsutil/v4 v4.24.6 github.com/stretchr/testify v1.9.0 go.opentelemetry.io/collector/component v0.106.1 + go.opentelemetry.io/collector/component/componentstatus v0.106.1 go.opentelemetry.io/collector/confmap v0.106.1 go.opentelemetry.io/collector/consumer v0.106.1 go.opentelemetry.io/collector/consumer/consumertest v0.106.1 @@ -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 61d2b8fc65a..58eddfa9558 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.106.1 go.opentelemetry.io/collector/component v0.106.1 + go.opentelemetry.io/collector/component/componentstatus v0.106.1 go.opentelemetry.io/collector/config/configgrpc v0.106.1 go.opentelemetry.io/collector/config/confighttp v0.106.1 go.opentelemetry.io/collector/config/configopaque v1.12.0 go.opentelemetry.io/collector/config/configretry v1.12.0 + go.opentelemetry.io/collector/config/configtelemetry v0.106.1 go.opentelemetry.io/collector/config/configtls v1.12.0 go.opentelemetry.io/collector/confmap v0.106.1 + go.opentelemetry.io/collector/connector v0.106.1 go.opentelemetry.io/collector/consumer v0.106.1 go.opentelemetry.io/collector/consumer/consumertest v0.106.1 go.opentelemetry.io/collector/exporter v0.106.1 go.opentelemetry.io/collector/exporter/otlpexporter v0.106.1 go.opentelemetry.io/collector/exporter/otlphttpexporter v0.106.1 + go.opentelemetry.io/collector/extension v0.106.1 go.opentelemetry.io/collector/pdata v1.12.0 go.opentelemetry.io/collector/pdata/testdata v0.106.1 + go.opentelemetry.io/collector/processor v0.106.1 go.opentelemetry.io/collector/receiver v0.106.1 go.opentelemetry.io/collector/receiver/otlpreceiver v0.106.1 + go.opentelemetry.io/collector/service v0.106.1 go.uber.org/goleak v1.3.0 + go.uber.org/zap v1.27.0 ) require ( @@ -33,8 +40,10 @@ 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.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect @@ -44,6 +53,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,27 +61,35 @@ 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.6 // 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.opencensus.io v0.24.0 // indirect go.opentelemetry.io/collector/client v0.106.1 // indirect go.opentelemetry.io/collector/config/configauth v0.106.1 // indirect go.opentelemetry.io/collector/config/configcompression v1.12.0 // indirect go.opentelemetry.io/collector/config/confignet v0.106.1 // indirect - go.opentelemetry.io/collector/config/configtelemetry v0.106.1 // indirect go.opentelemetry.io/collector/config/internal v0.106.1 // indirect go.opentelemetry.io/collector/consumer/consumerprofiles v0.106.1 // indirect - go.opentelemetry.io/collector/extension v0.106.1 // indirect go.opentelemetry.io/collector/extension/auth v0.106.1 // indirect go.opentelemetry.io/collector/featuregate v1.12.0 // indirect go.opentelemetry.io/collector/internal/globalgates v0.106.1 // indirect go.opentelemetry.io/collector/pdata/pprofile v0.106.1 // indirect + go.opentelemetry.io/collector/semconv v0.106.1 // 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/bridge/opencensus 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 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0 // indirect @@ -89,10 +107,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.27.0 // indirect golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.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 +175,15 @@ 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 diff --git a/internal/e2e/go.sum b/internal/e2e/go.sum index 9b0eaa55127..91676949fee 100644 --- a/internal/e2e/go.sum +++ b/internal/e2e/go.sum @@ -1,12 +1,21 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= @@ -16,15 +25,39 @@ 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.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc= github.com/go-viper/mapstructure/v2 v2.0.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/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 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.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= @@ -47,6 +80,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,8 +97,11 @@ 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.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= @@ -74,20 +112,45 @@ 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.6 h1:9qqCSYF2pgOU+t+NgJtp7Co5+5mHF/HyKBUckySQL64= +github.com/shirou/gopsutil/v4 v4.24.6/go.mod h1:aoebb2vxetJ/yIDZISmduFvVNPHqXQ9SEJwRXxkf0RA= +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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 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.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= 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/bridge/opencensus v1.28.0 h1:/BcyAV1bUJjSVxoeKwTQL9cS4X1iC6izZ9mheeuVSCU= +go.opentelemetry.io/otel/bridge/opencensus v1.28.0/go.mod h1:FZp2xE+46yAyp3DfLFALze58nY0iIE8zs+mCgkPAzq0= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.4.0 h1:zBPZAISA9NOc5cE8zydqDiS0itvg/P/0Hn9m72a5gvM= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.4.0/go.mod h1:gcj2fFjEsqpV3fXuzAA+0Ze1p2/4MJ4T7d77AmkvueQ= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 h1:U2guen0GhqH8o/G2un8f/aG/y++OuW6MyCo6hT9prXk= @@ -129,20 +192,39 @@ 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-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +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/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 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-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -150,6 +232,10 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -157,16 +243,40 @@ 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/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 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= google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/e2e/status_test.go b/internal/e2e/status_test.go new file mode 100644 index 00000000000..2eefb0017f2 --- /dev/null +++ b/internal/e2e/status_test.go @@ -0,0 +1,262 @@ +// 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.LevelBasic, + Address: "localhost:8888", + }, + }, + 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(10 * time.Second) + + 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.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() { + time.Sleep(5 * time.Second) + 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..2d928ce8eb2 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,71 @@ 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 +} + +func (h *hostWrapper) GetFactory(kind component.Kind, componentType component.Type) component.Factory { + // nolint + return h.host.GetFactory(kind, componentType) +} + +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. + if len(h.sources) > 0 && !slices.Contains(h.previousEvents, e) { + h.previousEvents = append(h.previousEvents, e) + } + + for _, s := range h.sources { + s.Report(e) + } +} + +func (h *hostWrapper) addSource(s componentstatus.Reporter) { + for _, e := range h.previousEvents { + s.Report(e) + } + h.sources = append(h.sources, s) } // Shutdown shuts down the underlying component. @@ -107,12 +149,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 e2df95feaca..6ea68e6eba1 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.106.1 + go.opentelemetry.io/collector/component/componentstatus v0.106.1 go.opentelemetry.io/collector/config/configtelemetry v0.106.1 go.opentelemetry.io/collector/confmap v0.106.1 go.opentelemetry.io/collector/connector v0.106.1 @@ -156,3 +157,5 @@ replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../consumer/c 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/otelcol/otelcoltest/go.mod b/otelcol/otelcoltest/go.mod index bd15729ca12..c3fded0e7e8 100644 --- a/otelcol/otelcoltest/go.mod +++ b/otelcol/otelcoltest/go.mod @@ -61,6 +61,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/collector v0.106.1 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.106.1 // indirect go.opentelemetry.io/collector/config/configtelemetry v0.106.1 // indirect go.opentelemetry.io/collector/consumer v0.106.1 // indirect go.opentelemetry.io/collector/consumer/consumerprofiles v0.106.1 // indirect @@ -174,3 +175,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/processor/batchprocessor/go.mod b/processor/batchprocessor/go.mod index 53acb98af2a..1e74f1e5d7f 100644 --- a/processor/batchprocessor/go.mod +++ b/processor/batchprocessor/go.mod @@ -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.106.1 // indirect go.opentelemetry.io/collector/consumer/consumerprofiles v0.106.1 // indirect go.opentelemetry.io/collector/featuregate v1.12.0 // indirect go.opentelemetry.io/collector/internal/globalgates v0.106.1 // 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 c4e4e22bdfc..55a3373b1dc 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.106.1 go.opentelemetry.io/collector/component v0.106.1 + go.opentelemetry.io/collector/component/componentstatus v0.106.1 go.opentelemetry.io/collector/config/configtelemetry v0.106.1 go.opentelemetry.io/collector/consumer v0.106.1 go.opentelemetry.io/collector/consumer/consumerprofiles v0.106.1 @@ -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 acec3017700..23cc25dcc26 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.106.1 // indirect go.opentelemetry.io/collector/config/configtelemetry v0.106.1 // indirect go.opentelemetry.io/collector/consumer/consumerprofiles v0.106.1 // indirect go.opentelemetry.io/collector/featuregate v1.12.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 36416c45010..7a39ee90f41 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 c85c9f676f4..109112b9df2 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 925179a8d18..8ec929aea93 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 c3afa449410..be677900489 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.106.1 go.opentelemetry.io/collector/component v0.106.1 + go.opentelemetry.io/collector/component/componentstatus v0.106.1 go.opentelemetry.io/collector/config/configauth v0.106.1 go.opentelemetry.io/collector/config/configgrpc v0.106.1 go.opentelemetry.io/collector/config/confighttp v0.106.1 @@ -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..c992b6ac0d2 100644 --- a/receiver/otlpreceiver/otlp.go +++ b/receiver/otlpreceiver/otlp.go @@ -13,6 +13,8 @@ import ( "go.uber.org/zap" "google.golang.org/grpc" + "go.opentelemetry.io/collector/component/componentstatus" + "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/config/confighttp" "go.opentelemetry.io/collector/consumer" @@ -110,7 +112,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 +162,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 7bec7bd5513..43944ae7126 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 8b40d8c90f7..a13dc3d6d27 100644 --- a/service/extensions/extensions_test.go +++ b/service/extensions/extensions_test.go @@ -11,6 +11,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component/componentstatus" + "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/confmap" @@ -360,7 +362,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] @@ -372,37 +374,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, @@ -420,8 +422,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 108ac0ebfea..b270bfddb92 100644 --- a/service/go.mod +++ b/service/go.mod @@ -12,6 +12,7 @@ require ( go.opencensus.io v0.24.0 go.opentelemetry.io/collector v0.106.1 go.opentelemetry.io/collector/component v0.106.1 + go.opentelemetry.io/collector/component/componentstatus v0.106.1 go.opentelemetry.io/collector/config/confighttp v0.106.1 go.opentelemetry.io/collector/config/configtelemetry v0.106.1 go.opentelemetry.io/collector/confmap v0.106.1 @@ -116,6 +117,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/host.go b/service/host.go deleted file mode 100644 index ce8dc530d40..00000000000 --- a/service/host.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package service // import "go.opentelemetry.io/collector/service" - -import ( - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/connector" - "go.opentelemetry.io/collector/exporter" - "go.opentelemetry.io/collector/extension" - "go.opentelemetry.io/collector/processor" - "go.opentelemetry.io/collector/receiver" - "go.opentelemetry.io/collector/service/extensions" - "go.opentelemetry.io/collector/service/internal/graph" -) - -// TODO: remove as part of https://github.com/open-telemetry/opentelemetry-collector/issues/7370 for service 1.0 -type getExporters interface { - GetExporters() map[component.DataType]map[component.ID]component.Component -} - -var _ getExporters = (*serviceHost)(nil) -var _ component.Host = (*serviceHost)(nil) - -type serviceHost struct { - asyncErrorChannel chan error - receivers *receiver.Builder - processors *processor.Builder - exporters *exporter.Builder - connectors *connector.Builder - extensions *extension.Builder - - buildInfo component.BuildInfo - - pipelines *graph.Graph - serviceExtensions *extensions.Extensions -} - -func (host *serviceHost) GetFactory(kind component.Kind, componentType component.Type) component.Factory { - switch kind { - case component.KindReceiver: - return host.receivers.Factory(componentType) - case component.KindProcessor: - return host.processors.Factory(componentType) - case component.KindExporter: - return host.exporters.Factory(componentType) - case component.KindConnector: - return host.connectors.Factory(componentType) - case component.KindExtension: - return host.extensions.Factory(componentType) - } - return nil -} - -func (host *serviceHost) GetExtensions() map[component.ID]component.Component { - return host.serviceExtensions.GetExtensions() -} - -// Deprecated: [0.79.0] This function will be removed in the future. -// Several components in the contrib repository use this function so it cannot be removed -// before those cases are removed. In most cases, use of this function can be replaced by a -// connector. See https://github.com/open-telemetry/opentelemetry-collector/issues/7370 and -// https://github.com/open-telemetry/opentelemetry-collector/pull/7390#issuecomment-1483710184 -// for additional information. -func (host *serviceHost) GetExporters() map[component.DataType]map[component.ID]component.Component { - return host.pipelines.GetExporters() -} - -func (host *serviceHost) notifyComponentStatusChange(source *component.InstanceID, event *component.StatusEvent) { - host.serviceExtensions.NotifyComponentStatusChange(source, event) - if event.Status() == component.StatusFatalError { - host.asyncErrorChannel <- event.Err() - } -} diff --git a/service/internal/graph/graph.go b/service/internal/graph/graph.go index 002fa3129f6..50bb8efcf2f 100644 --- a/service/internal/graph/graph.go +++ b/service/internal/graph/graph.go @@ -24,6 +24,8 @@ import ( "gonum.org/v1/gonum/graph/simple" "gonum.org/v1/gonum/graph/topo" + "go.opentelemetry.io/collector/component/componentstatus" + "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/connector" "go.opentelemetry.io/collector/consumer" @@ -60,7 +62,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 +73,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 +202,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 +215,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 +232,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 +251,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 +301,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 +393,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 +416,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 +436,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 +464,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 +579,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 e20b85b2961..ecfe1477596 100644 --- a/service/internal/graph/graph_test.go +++ b/service/internal/graph/graph_test.go @@ -15,6 +15,8 @@ import ( "github.com/stretchr/testify/require" "gonum.org/v1/gonum/graph/simple" + "go.opentelemetry.io/collector/component/componentstatus" + "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/connector" @@ -145,16 +147,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 +178,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 +190,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 +210,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 +218,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 +226,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", @@ -768,7 +770,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)) @@ -845,7 +847,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) @@ -855,7 +857,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() { @@ -873,9 +875,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] { @@ -943,15 +945,15 @@ 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) assert.Equal(t, test.expectedPerExporter, len(tracesExporter.Traces)) expected := testdata.GenerateTraces(1) if len(allExporters[component.DataTypeTraces]) > 1 { - expected.MarkReadOnly() // multiple read-only exporters should get read-only pdata + expected.MarkReadOnly() // multiple read-only Exporters should get read-only pdata } for i := 0; i < test.expectedPerExporter; i++ { assert.EqualValues(t, expected, tracesExporter.Traces[i]) @@ -962,7 +964,7 @@ func TestConnectorPipelinesGraph(t *testing.T) { assert.Equal(t, test.expectedPerExporter, len(metricsExporter.Metrics)) expected := testdata.GenerateMetrics(1) if len(allExporters[component.DataTypeMetrics]) > 1 { - expected.MarkReadOnly() // multiple read-only exporters should get read-only pdata + expected.MarkReadOnly() // multiple read-only Exporters should get read-only pdata } for i := 0; i < test.expectedPerExporter; i++ { assert.EqualValues(t, expected, metricsExporter.Metrics[i]) @@ -973,7 +975,7 @@ func TestConnectorPipelinesGraph(t *testing.T) { assert.Equal(t, test.expectedPerExporter, len(logsExporter.Logs)) expected := testdata.GenerateLogs(1) if len(allExporters[component.DataTypeLogs]) > 1 { - expected.MarkReadOnly() // multiple read-only exporters should get read-only pdata + expected.MarkReadOnly() // multiple read-only Exporters should get read-only pdata } for i := 0; i < test.expectedPerExporter; i++ { assert.EqualValues(t, expected, logsExporter.Logs[i]) @@ -1097,7 +1099,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) @@ -1119,7 +1121,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) @@ -1141,7 +1143,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) @@ -2148,7 +2150,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())) }) @@ -2162,7 +2164,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())) }) @@ -2176,7 +2178,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())) }) @@ -2196,7 +2198,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())) }) } @@ -2213,7 +2215,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}, @@ -2223,7 +2225,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] @@ -2241,35 +2243,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, @@ -2277,16 +2279,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, @@ -2294,18 +2296,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, @@ -2313,18 +2315,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, @@ -2334,8 +2336,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) { }) @@ -2343,13 +2345,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) }) @@ -2379,7 +2381,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 new file mode 100644 index 00000000000..75c5f31432f --- /dev/null +++ b/service/internal/graph/host.go @@ -0,0 +1,168 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package graph // import "go.opentelemetry.io/collector/service/internal/graph" + +import ( + "net/http" + "path" + "runtime" + "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" + "go.opentelemetry.io/collector/featuregate" + "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" +) + +const ( + // Paths + zServicePath = "servicez" + zPipelinePath = "pipelinez" + zExtensionPath = "extensionz" + zFeaturePath = "featurez" +) + +var ( + // InfoVar is a singleton instance of the Info struct. + runtimeInfoVar [][2]string +) + +func init() { + runtimeInfoVar = [][2]string{ + {"StartTimestamp", time.Now().String()}, + {"Go", runtime.Version()}, + {"OS", runtime.GOOS}, + {"Arch", runtime.GOARCH}, + // Add other valuable runtime information here. + } +} + +// TODO: remove as part of https://github.com/open-telemetry/opentelemetry-collector/issues/7370 for service 1.0 +type getExporters interface { + GetExporters() map[component.DataType]map[component.ID]component.Component +} + +var _ getExporters = (*Host)(nil) +var _ component.Host = (*Host)(nil) + +type Host struct { + AsyncErrorChannel chan error + Receivers *receiver.Builder + Processors *processor.Builder + Exporters *exporter.Builder + Connectors *connector.Builder + Extensions *extension.Builder + + BuildInfo component.BuildInfo + + Pipelines *Graph + ServiceExtensions *extensions.Extensions + + Reporter status.Reporter +} + +func (host *Host) GetFactory(kind component.Kind, componentType component.Type) component.Factory { + switch kind { + case component.KindReceiver: + return host.Receivers.Factory(componentType) + case component.KindProcessor: + return host.Processors.Factory(componentType) + case component.KindExporter: + return host.Exporters.Factory(componentType) + case component.KindConnector: + return host.Connectors.Factory(componentType) + case component.KindExtension: + return host.Extensions.Factory(componentType) + } + return nil +} + +func (host *Host) GetExtensions() map[component.ID]component.Component { + return host.ServiceExtensions.GetExtensions() +} + +// Deprecated: [0.79.0] This function will be removed in the future. +// Several components in the contrib repository use this function so it cannot be removed +// before those cases are removed. In most cases, use of this function can be replaced by a +// connector. See https://github.com/open-telemetry/opentelemetry-collector/issues/7370 and +// https://github.com/open-telemetry/opentelemetry-collector/pull/7390#issuecomment-1483710184 +// for additional information. +func (host *Host) GetExporters() map[component.DataType]map[component.ID]component.Component { + return host.Pipelines.GetExporters() +} + +func (host *Host) NotifyComponentStatusChange(source *componentstatus.InstanceID, event *componentstatus.Event) { + host.ServiceExtensions.NotifyComponentStatusChange(source, event) + if event.Status() == componentstatus.StatusFatalError { + host.AsyncErrorChannel <- event.Err() + } +} + +func (host *Host) RegisterZPages(mux *http.ServeMux, pathPrefix string) { + mux.HandleFunc(path.Join(pathPrefix, zServicePath), host.zPagesRequest) + mux.HandleFunc(path.Join(pathPrefix, zPipelinePath), host.Pipelines.HandleZPages) + mux.HandleFunc(path.Join(pathPrefix, zExtensionPath), host.ServiceExtensions.HandleZPages) + mux.HandleFunc(path.Join(pathPrefix, zFeaturePath), handleFeaturezRequest) +} + +func (host *Host) zPagesRequest(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + zpages.WriteHTMLPageHeader(w, zpages.HeaderData{Title: "Service " + host.BuildInfo.Command}) + zpages.WriteHTMLPropertiesTable(w, zpages.PropertiesTableData{Name: "Build Info", Properties: getBuildInfoProperties(host.BuildInfo)}) + zpages.WriteHTMLPropertiesTable(w, zpages.PropertiesTableData{Name: "Runtime Info", Properties: runtimeInfoVar}) + zpages.WriteHTMLComponentHeader(w, zpages.ComponentHeaderData{ + Name: "Pipelines", + ComponentEndpoint: zPipelinePath, + Link: true, + }) + zpages.WriteHTMLComponentHeader(w, zpages.ComponentHeaderData{ + Name: "Extensions", + ComponentEndpoint: zExtensionPath, + Link: true, + }) + zpages.WriteHTMLComponentHeader(w, zpages.ComponentHeaderData{ + Name: "Features", + ComponentEndpoint: zFeaturePath, + Link: true, + }) + zpages.WriteHTMLPageFooter(w) +} + +func handleFeaturezRequest(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + zpages.WriteHTMLPageHeader(w, zpages.HeaderData{Title: "Feature Gates"}) + zpages.WriteHTMLFeaturesTable(w, getFeaturesTableData()) + zpages.WriteHTMLPageFooter(w) +} + +func getFeaturesTableData() zpages.FeatureGateTableData { + data := zpages.FeatureGateTableData{} + featuregate.GlobalRegistry().VisitAll(func(gate *featuregate.Gate) { + data.Rows = append(data.Rows, zpages.FeatureGateTableRowData{ + ID: gate.ID(), + Enabled: gate.IsEnabled(), + Description: gate.Description(), + Stage: gate.Stage().String(), + FromVersion: gate.FromVersion(), + ToVersion: gate.ToVersion(), + ReferenceURL: gate.ReferenceURL(), + }) + }) + return data +} + +func getBuildInfoProperties(buildInfo component.BuildInfo) [][2]string { + return [][2]string{ + {"Command", buildInfo.Command}, + {"Description", buildInfo.Description}, + {"Version", buildInfo.Version}, + } +} 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 320a7068bcf..54698d86fc0 100644 --- a/service/service.go +++ b/service/service.go @@ -70,10 +70,8 @@ type Settings struct { type Service struct { buildInfo component.BuildInfo telemetrySettings component.TelemetrySettings - host *serviceHost + host *graph.Host collectorConf *confmap.Conf - - reporter status.Reporter } // New creates a new Service, its telemetry, and Components. @@ -82,14 +80,14 @@ func New(ctx context.Context, set Settings, cfg Config) (*Service, error) { extendedConfig := obsreportconfig.UseOtelWithSDKConfigurationForInternalTelemetryFeatureGate.IsEnabled() srv := &Service{ buildInfo: set.BuildInfo, - host: &serviceHost{ - receivers: set.Receivers, - processors: set.Processors, - exporters: set.Exporters, - connectors: set.Connectors, - extensions: set.Extensions, - buildInfo: set.BuildInfo, - asyncErrorChannel: set.AsyncErrorChannel, + host: &graph.Host{ + Receivers: set.Receivers, + Processors: set.Processors, + Exporters: set.Exporters, + Connectors: set.Connectors, + Extensions: set.Extensions, + BuildInfo: set.BuildInfo, + AsyncErrorChannel: set.AsyncErrorChannel, }, collectorConf: set.CollectorConf, } @@ -140,7 +138,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)) } @@ -202,23 +200,23 @@ 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 { + if err := srv.host.ServiceExtensions.Start(ctx, srv.host); err != nil { return fmt.Errorf("failed to start extensions: %w", err) } if srv.collectorConf != nil { - if err := srv.host.serviceExtensions.NotifyConfig(ctx, srv.collectorConf); err != nil { + if err := srv.host.ServiceExtensions.NotifyConfig(ctx, srv.collectorConf); err != nil { return err } } - 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) } - if err := srv.host.serviceExtensions.NotifyPipelineReady(); err != nil { + if err := srv.host.ServiceExtensions.NotifyPipelineReady(); err != nil { return err } @@ -261,15 +259,15 @@ func (srv *Service) Shutdown(ctx context.Context) error { // Begin shutdown sequence. srv.telemetrySettings.Logger.Info("Starting shutdown...") - if err := srv.host.serviceExtensions.NotifyPipelineNotReady(); err != nil { + if err := srv.host.ServiceExtensions.NotifyPipelineNotReady(); err != nil { 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)) } - if err := srv.host.serviceExtensions.Shutdown(ctx); err != nil { + if err := srv.host.ServiceExtensions.Shutdown(ctx); err != nil { errs = multierr.Append(errs, fmt.Errorf("failed to shutdown extensions: %w", err)) } @@ -286,9 +284,9 @@ func (srv *Service) initExtensions(ctx context.Context, cfg extensions.Config) e extensionsSettings := extensions.Settings{ Telemetry: srv.telemetrySettings, BuildInfo: srv.buildInfo, - Extensions: srv.host.extensions, + 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 @@ -297,7 +295,7 @@ func (srv *Service) initExtensions(ctx context.Context, cfg extensions.Config) e // Creates the pipeline graph. func (srv *Service) initGraph(ctx context.Context, set Settings, cfg Config) error { var err error - if srv.host.pipelines, err = graph.Build(ctx, graph.Settings{ + if srv.host.Pipelines, err = graph.Build(ctx, graph.Settings{ Telemetry: srv.telemetrySettings, BuildInfo: srv.buildInfo, ReceiverBuilder: set.Receivers, @@ -305,7 +303,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 b0a952724be..8f2e3dc79a7 100644 --- a/service/service_test.go +++ b/service/service_test.go @@ -20,6 +20,8 @@ import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" + "go.opentelemetry.io/collector/component/componentstatus" + "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/config/confighttp" "go.opentelemetry.io/collector/config/configtelemetry" @@ -227,6 +229,7 @@ func TestServiceGetExporters(t *testing.T) { assert.NoError(t, srv.Shutdown(context.Background())) }) + // nolint expMap := srv.host.GetExporters() assert.Len(t, expMap, 3) assert.Len(t, expMap[component.DataTypeTraces], 1) @@ -437,11 +440,11 @@ 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 + err = <-srv.host.AsyncErrorChannel require.ErrorIs(t, err, assert.AnError) } diff --git a/service/zpages.go b/service/zpages.go deleted file mode 100644 index c7f7b494ad3..00000000000 --- a/service/zpages.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package service // import "go.opentelemetry.io/collector/service" - -import ( - "net/http" - "path" - "runtime" - "time" - - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/featuregate" - "go.opentelemetry.io/collector/service/internal/zpages" -) - -const ( - // Paths - zServicePath = "servicez" - zPipelinePath = "pipelinez" - zExtensionPath = "extensionz" - zFeaturePath = "featurez" -) - -var ( - // InfoVar is a singleton instance of the Info struct. - runtimeInfoVar [][2]string -) - -func init() { - runtimeInfoVar = [][2]string{ - {"StartTimestamp", time.Now().String()}, - {"Go", runtime.Version()}, - {"OS", runtime.GOOS}, - {"Arch", runtime.GOARCH}, - // Add other valuable runtime information here. - } -} - -func (host *serviceHost) RegisterZPages(mux *http.ServeMux, pathPrefix string) { - mux.HandleFunc(path.Join(pathPrefix, zServicePath), host.zPagesRequest) - mux.HandleFunc(path.Join(pathPrefix, zPipelinePath), host.pipelines.HandleZPages) - mux.HandleFunc(path.Join(pathPrefix, zExtensionPath), host.serviceExtensions.HandleZPages) - mux.HandleFunc(path.Join(pathPrefix, zFeaturePath), handleFeaturezRequest) -} - -func (host *serviceHost) zPagesRequest(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "text/html; charset=utf-8") - zpages.WriteHTMLPageHeader(w, zpages.HeaderData{Title: "Service " + host.buildInfo.Command}) - zpages.WriteHTMLPropertiesTable(w, zpages.PropertiesTableData{Name: "Build Info", Properties: getBuildInfoProperties(host.buildInfo)}) - zpages.WriteHTMLPropertiesTable(w, zpages.PropertiesTableData{Name: "Runtime Info", Properties: runtimeInfoVar}) - zpages.WriteHTMLComponentHeader(w, zpages.ComponentHeaderData{ - Name: "Pipelines", - ComponentEndpoint: zPipelinePath, - Link: true, - }) - zpages.WriteHTMLComponentHeader(w, zpages.ComponentHeaderData{ - Name: "Extensions", - ComponentEndpoint: zExtensionPath, - Link: true, - }) - zpages.WriteHTMLComponentHeader(w, zpages.ComponentHeaderData{ - Name: "Features", - ComponentEndpoint: zFeaturePath, - Link: true, - }) - zpages.WriteHTMLPageFooter(w) -} - -func handleFeaturezRequest(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "text/html; charset=utf-8") - zpages.WriteHTMLPageHeader(w, zpages.HeaderData{Title: "Feature Gates"}) - zpages.WriteHTMLFeaturesTable(w, getFeaturesTableData()) - zpages.WriteHTMLPageFooter(w) -} - -func getFeaturesTableData() zpages.FeatureGateTableData { - data := zpages.FeatureGateTableData{} - featuregate.GlobalRegistry().VisitAll(func(gate *featuregate.Gate) { - data.Rows = append(data.Rows, zpages.FeatureGateTableRowData{ - ID: gate.ID(), - Enabled: gate.IsEnabled(), - Description: gate.Description(), - Stage: gate.Stage().String(), - FromVersion: gate.FromVersion(), - ToVersion: gate.ToVersion(), - ReferenceURL: gate.ReferenceURL(), - }) - }) - return data -} - -func getBuildInfoProperties(buildInfo component.BuildInfo) [][2]string { - return [][2]string{ - {"Command", buildInfo.Command}, - {"Description", buildInfo.Description}, - {"Version", buildInfo.Version}, - } -}