496 lines
20 KiB
Go
496 lines
20 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package graph
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"gonum.org/v1/gonum/graph/simple"
|
|
|
|
"go.opentelemetry.io/collector/component"
|
|
"go.opentelemetry.io/collector/component/componentstatus"
|
|
"go.opentelemetry.io/collector/component/componenttest"
|
|
"go.opentelemetry.io/collector/connector"
|
|
"go.opentelemetry.io/collector/connector/connectortest"
|
|
"go.opentelemetry.io/collector/exporter"
|
|
"go.opentelemetry.io/collector/exporter/exportertest"
|
|
"go.opentelemetry.io/collector/pipeline"
|
|
"go.opentelemetry.io/collector/processor"
|
|
"go.opentelemetry.io/collector/processor/processortest"
|
|
"go.opentelemetry.io/collector/receiver"
|
|
"go.opentelemetry.io/collector/receiver/receivertest"
|
|
"go.opentelemetry.io/collector/service/internal/builders"
|
|
"go.opentelemetry.io/collector/service/internal/status"
|
|
"go.opentelemetry.io/collector/service/internal/status/statustest"
|
|
"go.opentelemetry.io/collector/service/pipelines"
|
|
)
|
|
|
|
func TestGraphStartStop(t *testing.T) {
|
|
t.Run("with_internal_telemetry", func(t *testing.T) {
|
|
setObsConsumerGateForTest(t, true)
|
|
testGraphStartStop(t)
|
|
})
|
|
t.Run("without_internal_telemetry", func(t *testing.T) {
|
|
setObsConsumerGateForTest(t, false)
|
|
testGraphStartStop(t)
|
|
})
|
|
}
|
|
|
|
func testGraphStartStop(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
edges [][2]component.ID
|
|
}{
|
|
{
|
|
name: "single",
|
|
edges: [][2]component.ID{
|
|
{component.MustNewIDWithName("r", "1"), component.MustNewIDWithName("p", "1")},
|
|
{component.MustNewIDWithName("r", "2"), component.MustNewIDWithName("p", "1")},
|
|
{component.MustNewIDWithName("p", "1"), component.MustNewIDWithName("p", "2")},
|
|
{component.MustNewIDWithName("p", "2"), component.MustNewIDWithName("e", "1")},
|
|
{component.MustNewIDWithName("p", "1"), component.MustNewIDWithName("e", "2")},
|
|
},
|
|
},
|
|
{
|
|
name: "multi",
|
|
edges: [][2]component.ID{
|
|
// Pipeline 1
|
|
{component.MustNewIDWithName("r", "1"), component.MustNewIDWithName("p", "1")},
|
|
{component.MustNewIDWithName("r", "2"), component.MustNewIDWithName("p", "1")},
|
|
{component.MustNewIDWithName("p", "1"), component.MustNewIDWithName("p", "2")},
|
|
{component.MustNewIDWithName("p", "2"), component.MustNewIDWithName("e", "1")},
|
|
{component.MustNewIDWithName("p", "1"), component.MustNewIDWithName("e", "2")},
|
|
|
|
// Pipeline 2, shares r1 and e2
|
|
{component.MustNewIDWithName("r", "1"), component.MustNewIDWithName("p", "3")},
|
|
{component.MustNewIDWithName("p", "3"), component.MustNewIDWithName("e", "2")},
|
|
},
|
|
},
|
|
{
|
|
name: "connected",
|
|
edges: [][2]component.ID{
|
|
// Pipeline 1
|
|
{component.MustNewIDWithName("r", "1"), component.MustNewIDWithName("p", "1")},
|
|
{component.MustNewIDWithName("r", "2"), component.MustNewIDWithName("p", "1")},
|
|
{component.MustNewIDWithName("p", "1"), component.MustNewIDWithName("p", "2")},
|
|
{component.MustNewIDWithName("p", "2"), component.MustNewIDWithName("e", "1")},
|
|
{component.MustNewIDWithName("p", "1"), component.MustNewIDWithName("c", "1")},
|
|
|
|
// Pipeline 2, shares r1 and c1
|
|
{component.MustNewIDWithName("r", "1"), component.MustNewIDWithName("p", "3")},
|
|
{component.MustNewIDWithName("p", "3"), component.MustNewIDWithName("c", "1")},
|
|
|
|
// Pipeline 3, emits to e2 and c2
|
|
{component.MustNewIDWithName("c", "1"), component.MustNewIDWithName("e", "2")},
|
|
{component.MustNewIDWithName("c", "1"), component.MustNewIDWithName("c", "2")},
|
|
|
|
// Pipeline 4, also emits to e2
|
|
{component.MustNewIDWithName("c", "2"), component.MustNewIDWithName("e", "2")},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range testCases {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
ctx := &contextWithOrder{
|
|
Context: context.Background(),
|
|
order: map[component.ID]int{},
|
|
}
|
|
|
|
pg := &Graph{componentGraph: simple.NewDirectedGraph()}
|
|
pg.telemetry = componenttest.NewNopTelemetrySettings()
|
|
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()] = &componentstatus.InstanceID{}
|
|
pg.instanceIDs[t.ID()] = &componentstatus.InstanceID{}
|
|
pg.componentGraph.SetEdge(simple.Edge{F: f, T: t})
|
|
}
|
|
|
|
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]])
|
|
}
|
|
|
|
ctx.order = map[component.ID]int{}
|
|
require.NoError(t, pg.ShutdownAll(ctx, statustest.NewNopStatusReporter()))
|
|
for _, edge := range tt.edges {
|
|
assert.Less(t, ctx.order[edge[0]], ctx.order[edge[1]])
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGraphStartStopCycle(t *testing.T) {
|
|
t.Run("with_internal_telemetry", func(t *testing.T) {
|
|
setObsConsumerGateForTest(t, true)
|
|
testGraphStartStopCycle(t)
|
|
})
|
|
t.Run("without_internal_telemetry", func(t *testing.T) {
|
|
setObsConsumerGateForTest(t, false)
|
|
testGraphStartStopCycle(t)
|
|
})
|
|
}
|
|
|
|
func testGraphStartStopCycle(t *testing.T) {
|
|
pg := &Graph{componentGraph: simple.NewDirectedGraph()}
|
|
|
|
r1 := &testNode{id: component.MustNewIDWithName("r", "1")}
|
|
p1 := &testNode{id: component.MustNewIDWithName("p", "1")}
|
|
c1 := &testNode{id: component.MustNewIDWithName("c", "1")}
|
|
e1 := &testNode{id: component.MustNewIDWithName("e", "1")}
|
|
|
|
pg.instanceIDs = map[int64]*componentstatus.InstanceID{
|
|
r1.ID(): {},
|
|
p1.ID(): {},
|
|
c1.ID(): {},
|
|
e1.ID(): {},
|
|
}
|
|
|
|
pg.componentGraph.SetEdge(simple.Edge{F: r1, T: p1})
|
|
pg.componentGraph.SetEdge(simple.Edge{F: p1, T: c1})
|
|
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(), &Host{Reporter: status.NewReporter(func(*componentstatus.InstanceID, *componentstatus.Event) {}, func(error) {})})
|
|
require.ErrorContains(t, err, `topo: no topological ordering: cyclic components`)
|
|
|
|
err = pg.ShutdownAll(context.Background(), statustest.NewNopStatusReporter())
|
|
assert.ErrorContains(t, err, `topo: no topological ordering: cyclic components`)
|
|
}
|
|
|
|
func TestGraphStartStopComponentError(t *testing.T) {
|
|
t.Run("with_internal_telemetry", func(t *testing.T) {
|
|
setObsConsumerGateForTest(t, true)
|
|
testGraphStartStopComponentError(t)
|
|
})
|
|
t.Run("without_internal_telemetry", func(t *testing.T) {
|
|
setObsConsumerGateForTest(t, false)
|
|
testGraphStartStopComponentError(t)
|
|
})
|
|
}
|
|
|
|
func testGraphStartStopComponentError(t *testing.T) {
|
|
pg := &Graph{componentGraph: simple.NewDirectedGraph()}
|
|
pg.telemetry = componenttest.NewNopTelemetrySettings()
|
|
r1 := &testNode{
|
|
id: component.MustNewIDWithName("r", "1"),
|
|
startErr: errors.New("foo"),
|
|
}
|
|
e1 := &testNode{
|
|
id: component.MustNewIDWithName("e", "1"),
|
|
shutdownErr: errors.New("bar"),
|
|
}
|
|
pg.instanceIDs = map[int64]*componentstatus.InstanceID{
|
|
r1.ID(): {},
|
|
e1.ID(): {},
|
|
}
|
|
pg.componentGraph.SetEdge(simple.Edge{
|
|
F: r1,
|
|
T: e1,
|
|
})
|
|
require.ErrorIs(t, pg.StartAll(context.Background(), &Host{Reporter: status.NewReporter(func(*componentstatus.InstanceID, *componentstatus.Event) {}, func(error) {})}), r1.startErr)
|
|
assert.EqualError(t, pg.ShutdownAll(context.Background(), statustest.NewNopStatusReporter()), "bar")
|
|
}
|
|
|
|
// This includes all tests from the previous implementation, plus a new one
|
|
// relevant only to the new graph-based implementation.
|
|
func TestGraphFailToStartAndShutdown(t *testing.T) {
|
|
t.Run("with_internal_telemetry", func(t *testing.T) {
|
|
setObsConsumerGateForTest(t, true)
|
|
testGraphFailToStartAndShutdown(t)
|
|
})
|
|
t.Run("without_internal_telemetry", func(t *testing.T) {
|
|
setObsConsumerGateForTest(t, false)
|
|
testGraphFailToStartAndShutdown(t)
|
|
})
|
|
}
|
|
|
|
func testGraphFailToStartAndShutdown(t *testing.T) {
|
|
errReceiverFactory := newErrReceiverFactory()
|
|
errProcessorFactory := newErrProcessorFactory()
|
|
errExporterFactory := newErrExporterFactory()
|
|
errConnectorFactory := newErrConnectorFactory()
|
|
nopReceiverFactory := receivertest.NewNopFactory()
|
|
nopProcessorFactory := processortest.NewNopFactory()
|
|
nopExporterFactory := exportertest.NewNopFactory()
|
|
nopConnectorFactory := connectortest.NewNopFactory()
|
|
|
|
set := Settings{
|
|
Telemetry: componenttest.NewNopTelemetrySettings(),
|
|
BuildInfo: component.NewDefaultBuildInfo(),
|
|
ReceiverBuilder: builders.NewReceiver(
|
|
map[component.ID]component.Config{
|
|
component.NewID(nopReceiverFactory.Type()): nopReceiverFactory.CreateDefaultConfig(),
|
|
component.NewID(errReceiverFactory.Type()): errReceiverFactory.CreateDefaultConfig(),
|
|
},
|
|
map[component.Type]receiver.Factory{
|
|
nopReceiverFactory.Type(): nopReceiverFactory,
|
|
errReceiverFactory.Type(): errReceiverFactory,
|
|
}),
|
|
ProcessorBuilder: builders.NewProcessor(
|
|
map[component.ID]component.Config{
|
|
component.NewID(nopProcessorFactory.Type()): nopProcessorFactory.CreateDefaultConfig(),
|
|
component.NewID(errProcessorFactory.Type()): errProcessorFactory.CreateDefaultConfig(),
|
|
},
|
|
map[component.Type]processor.Factory{
|
|
nopProcessorFactory.Type(): nopProcessorFactory,
|
|
errProcessorFactory.Type(): errProcessorFactory,
|
|
}),
|
|
ExporterBuilder: builders.NewExporter(
|
|
map[component.ID]component.Config{
|
|
component.NewID(nopExporterFactory.Type()): nopExporterFactory.CreateDefaultConfig(),
|
|
component.NewID(errExporterFactory.Type()): errExporterFactory.CreateDefaultConfig(),
|
|
},
|
|
map[component.Type]exporter.Factory{
|
|
nopExporterFactory.Type(): nopExporterFactory,
|
|
errExporterFactory.Type(): errExporterFactory,
|
|
}),
|
|
ConnectorBuilder: builders.NewConnector(
|
|
map[component.ID]component.Config{
|
|
component.NewIDWithName(nopConnectorFactory.Type(), "conn"): nopConnectorFactory.CreateDefaultConfig(),
|
|
component.NewIDWithName(errConnectorFactory.Type(), "conn"): errConnectorFactory.CreateDefaultConfig(),
|
|
},
|
|
map[component.Type]connector.Factory{
|
|
nopConnectorFactory.Type(): nopConnectorFactory,
|
|
errConnectorFactory.Type(): errConnectorFactory,
|
|
}),
|
|
}
|
|
|
|
dataTypes := []pipeline.Signal{pipeline.SignalTraces, pipeline.SignalMetrics, pipeline.SignalLogs}
|
|
for _, dt := range dataTypes {
|
|
t.Run(dt.String()+"/receiver", func(t *testing.T) {
|
|
set.PipelineConfigs = pipelines.Config{
|
|
pipeline.NewID(dt): {
|
|
Receivers: []component.ID{component.MustNewID("nop"), component.MustNewID("err")},
|
|
Processors: []component.ID{component.MustNewID("nop")},
|
|
Exporters: []component.ID{component.MustNewID("nop")},
|
|
},
|
|
}
|
|
pipelines, err := Build(context.Background(), set)
|
|
require.NoError(t, err)
|
|
require.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()))
|
|
})
|
|
|
|
t.Run(dt.String()+"/processor", func(t *testing.T) {
|
|
set.PipelineConfigs = pipelines.Config{
|
|
pipeline.NewID(dt): {
|
|
Receivers: []component.ID{component.MustNewID("nop")},
|
|
Processors: []component.ID{component.MustNewID("nop"), component.MustNewID("err")},
|
|
Exporters: []component.ID{component.MustNewID("nop")},
|
|
},
|
|
}
|
|
pipelines, err := Build(context.Background(), set)
|
|
require.NoError(t, err)
|
|
require.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()))
|
|
})
|
|
|
|
t.Run(dt.String()+"/exporter", func(t *testing.T) {
|
|
set.PipelineConfigs = pipelines.Config{
|
|
pipeline.NewID(dt): {
|
|
Receivers: []component.ID{component.MustNewID("nop")},
|
|
Processors: []component.ID{component.MustNewID("nop")},
|
|
Exporters: []component.ID{component.MustNewID("nop"), component.MustNewID("err")},
|
|
},
|
|
}
|
|
pipelines, err := Build(context.Background(), set)
|
|
require.NoError(t, err)
|
|
require.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()))
|
|
})
|
|
|
|
for _, dt2 := range dataTypes {
|
|
t.Run(dt.String()+"/"+dt2.String()+"/connector", func(t *testing.T) {
|
|
set.PipelineConfigs = pipelines.Config{
|
|
pipeline.NewIDWithName(dt, "in"): {
|
|
Receivers: []component.ID{component.MustNewID("nop")},
|
|
Processors: []component.ID{component.MustNewID("nop")},
|
|
Exporters: []component.ID{component.MustNewID("nop"), component.MustNewIDWithName("err", "conn")},
|
|
},
|
|
pipeline.NewIDWithName(dt2, "out"): {
|
|
Receivers: []component.ID{component.MustNewID("nop"), component.MustNewIDWithName("err", "conn")},
|
|
Processors: []component.ID{component.MustNewID("nop")},
|
|
Exporters: []component.ID{component.MustNewID("nop")},
|
|
},
|
|
}
|
|
pipelines, err := Build(context.Background(), set)
|
|
require.NoError(t, err)
|
|
require.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()))
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestStatusReportedOnStartupShutdown(t *testing.T) {
|
|
t.Run("with_internal_telemetry", func(t *testing.T) {
|
|
setObsConsumerGateForTest(t, true)
|
|
testStatusReportedOnStartupShutdown(t)
|
|
})
|
|
t.Run("without_internal_telemetry", func(t *testing.T) {
|
|
setObsConsumerGateForTest(t, false)
|
|
testStatusReportedOnStartupShutdown(t)
|
|
})
|
|
}
|
|
|
|
func testStatusReportedOnStartupShutdown(t *testing.T) {
|
|
rNoErr := &testNode{id: component.MustNewIDWithName("r_no_err", "1")}
|
|
rStErr := &testNode{id: component.MustNewIDWithName("r_st_err", "1"), startErr: assert.AnError}
|
|
rSdErr := &testNode{id: component.MustNewIDWithName("r_sd_err", "1"), shutdownErr: assert.AnError}
|
|
|
|
eNoErr := &testNode{id: component.MustNewIDWithName("e_no_err", "1")}
|
|
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]*componentstatus.InstanceID{
|
|
rNoErr: componentstatus.NewInstanceID(rNoErr.id, component.KindReceiver),
|
|
rStErr: componentstatus.NewInstanceID(rStErr.id, component.KindReceiver),
|
|
rSdErr: componentstatus.NewInstanceID(rSdErr.id, component.KindReceiver),
|
|
eNoErr: componentstatus.NewInstanceID(eNoErr.id, component.KindExporter),
|
|
eStErr: componentstatus.NewInstanceID(eStErr.id, component.KindExporter),
|
|
eSdErr: componentstatus.NewInstanceID(eSdErr.id, component.KindExporter),
|
|
}
|
|
|
|
// compare two maps of status events ignoring timestamp
|
|
assertEqualStatuses := func(t *testing.T, evMap1, evMap2 map[*componentstatus.InstanceID][]*componentstatus.Event) {
|
|
assert.Len(t, evMap2, len(evMap1))
|
|
for id, evts1 := range evMap1 {
|
|
evts2 := evMap2[id]
|
|
assert.Len(t, evts2, len(evts1))
|
|
for i := 0; i < len(evts1); i++ {
|
|
ev1 := evts1[i]
|
|
ev2 := evts2[i]
|
|
assert.Equal(t, ev1.Status(), ev2.Status())
|
|
assert.Equal(t, ev1.Err(), ev2.Err())
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, tt := range []struct {
|
|
name string
|
|
edge [2]*testNode
|
|
expectedStatuses map[*componentstatus.InstanceID][]*componentstatus.Event
|
|
startupErr error
|
|
shutdownErr error
|
|
}{
|
|
{
|
|
name: "successful startup/shutdown",
|
|
edge: [2]*testNode{rNoErr, eNoErr},
|
|
expectedStatuses: map[*componentstatus.InstanceID][]*componentstatus.Event{
|
|
instanceIDs[rNoErr]: {
|
|
componentstatus.NewEvent(componentstatus.StatusStarting),
|
|
componentstatus.NewEvent(componentstatus.StatusOK),
|
|
componentstatus.NewEvent(componentstatus.StatusStopping),
|
|
componentstatus.NewEvent(componentstatus.StatusStopped),
|
|
},
|
|
instanceIDs[eNoErr]: {
|
|
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[*componentstatus.InstanceID][]*componentstatus.Event{
|
|
instanceIDs[eStErr]: {
|
|
componentstatus.NewEvent(componentstatus.StatusStarting),
|
|
componentstatus.NewPermanentErrorEvent(assert.AnError),
|
|
componentstatus.NewEvent(componentstatus.StatusStopping),
|
|
componentstatus.NewEvent(componentstatus.StatusStopped),
|
|
},
|
|
},
|
|
startupErr: assert.AnError,
|
|
},
|
|
{
|
|
name: "late startup error",
|
|
edge: [2]*testNode{rStErr, eNoErr},
|
|
expectedStatuses: map[*componentstatus.InstanceID][]*componentstatus.Event{
|
|
instanceIDs[rStErr]: {
|
|
componentstatus.NewEvent(componentstatus.StatusStarting),
|
|
componentstatus.NewPermanentErrorEvent(assert.AnError),
|
|
componentstatus.NewEvent(componentstatus.StatusStopping),
|
|
componentstatus.NewEvent(componentstatus.StatusStopped),
|
|
},
|
|
instanceIDs[eNoErr]: {
|
|
componentstatus.NewEvent(componentstatus.StatusStarting),
|
|
componentstatus.NewEvent(componentstatus.StatusOK),
|
|
componentstatus.NewEvent(componentstatus.StatusStopping),
|
|
componentstatus.NewEvent(componentstatus.StatusStopped),
|
|
},
|
|
},
|
|
startupErr: assert.AnError,
|
|
},
|
|
{
|
|
name: "early shutdown error",
|
|
edge: [2]*testNode{rSdErr, eNoErr},
|
|
expectedStatuses: map[*componentstatus.InstanceID][]*componentstatus.Event{
|
|
instanceIDs[rSdErr]: {
|
|
componentstatus.NewEvent(componentstatus.StatusStarting),
|
|
componentstatus.NewEvent(componentstatus.StatusOK),
|
|
componentstatus.NewEvent(componentstatus.StatusStopping),
|
|
componentstatus.NewPermanentErrorEvent(assert.AnError),
|
|
},
|
|
instanceIDs[eNoErr]: {
|
|
componentstatus.NewEvent(componentstatus.StatusStarting),
|
|
componentstatus.NewEvent(componentstatus.StatusOK),
|
|
componentstatus.NewEvent(componentstatus.StatusStopping),
|
|
componentstatus.NewEvent(componentstatus.StatusStopped),
|
|
},
|
|
},
|
|
shutdownErr: assert.AnError,
|
|
},
|
|
{
|
|
name: "late shutdown error",
|
|
edge: [2]*testNode{rNoErr, eSdErr},
|
|
expectedStatuses: map[*componentstatus.InstanceID][]*componentstatus.Event{
|
|
instanceIDs[rNoErr]: {
|
|
componentstatus.NewEvent(componentstatus.StatusStarting),
|
|
componentstatus.NewEvent(componentstatus.StatusOK),
|
|
componentstatus.NewEvent(componentstatus.StatusStopping),
|
|
componentstatus.NewEvent(componentstatus.StatusStopped),
|
|
},
|
|
instanceIDs[eSdErr]: {
|
|
componentstatus.NewEvent(componentstatus.StatusStarting),
|
|
componentstatus.NewEvent(componentstatus.StatusOK),
|
|
componentstatus.NewEvent(componentstatus.StatusStopping),
|
|
componentstatus.NewPermanentErrorEvent(assert.AnError),
|
|
},
|
|
},
|
|
shutdownErr: assert.AnError,
|
|
},
|
|
} {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
pg := &Graph{componentGraph: simple.NewDirectedGraph()}
|
|
pg.telemetry = componenttest.NewNopTelemetrySettings()
|
|
|
|
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) {
|
|
})
|
|
|
|
e0, e1 := tt.edge[0], tt.edge[1]
|
|
pg.instanceIDs = map[int64]*componentstatus.InstanceID{
|
|
e0.ID(): instanceIDs[e0],
|
|
e1.ID(): instanceIDs[e1],
|
|
}
|
|
pg.componentGraph.SetEdge(simple.Edge{F: e0, T: e1})
|
|
|
|
require.ErrorIs(t, pg.StartAll(context.Background(), &Host{Reporter: rep}), tt.startupErr)
|
|
assert.Equal(t, tt.shutdownErr, pg.ShutdownAll(context.Background(), rep))
|
|
assertEqualStatuses(t, tt.expectedStatuses, actualStatuses)
|
|
})
|
|
}
|
|
}
|