Don't error in ReleaseGate if the context used for the original event has been cancelled. Any error in ReleaseGate causes the controller to terminate its startup. If a user was trying to send a request to the Boundary controller as it started up, the context tied to the request would be reused to attempt logging the observation associated with the request after the logging gate was released. This would always fail, as the context associated with the request was canceled. We now use a new context timeout for events logged after the release gate is released, if the original context was canceled.
207 lines
4.5 KiB
Go
207 lines
4.5 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package event
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/go-secure-stdlib/strutil"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestEventer_Gating(t *testing.T) {
|
|
t.Parallel()
|
|
require := require.New(t)
|
|
|
|
buffer := new(bytes.Buffer)
|
|
eventerConfig := EventerConfig{
|
|
AuditEnabled: true,
|
|
ObservationsEnabled: true,
|
|
SysEventsEnabled: true,
|
|
Sinks: []*SinkConfig{
|
|
{
|
|
Name: "test-sink",
|
|
EventTypes: []Type{EveryType},
|
|
Format: TextHclogSinkFormat,
|
|
Type: WriterSink,
|
|
WriterConfig: &WriterSinkTypeConfig{
|
|
Writer: buffer,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
testLock := &sync.Mutex{}
|
|
testLogger := testLogger(t, testLock)
|
|
|
|
eventer, err := NewEventer(
|
|
testLogger,
|
|
testLock,
|
|
"TestEventer_Gating",
|
|
eventerConfig,
|
|
WithGating(true),
|
|
)
|
|
require.NoError(err)
|
|
|
|
ctx, err := NewEventerContext(context.Background(), eventer)
|
|
require.NoError(err)
|
|
|
|
// This test sends a series of events of different types. The tests are
|
|
// meant to be in order as we want to send some that should be gated and
|
|
// some that shouldn't and ensure correct behavior at each step.
|
|
var totalEvents int
|
|
tests := []struct {
|
|
name string
|
|
eventFn func()
|
|
expectedGatedEvents int
|
|
}{
|
|
{
|
|
name: "system-event-1",
|
|
eventFn: func() {
|
|
WriteSysEvent(ctx, "system-event-1", "system-event-1")
|
|
totalEvents++
|
|
},
|
|
expectedGatedEvents: 1,
|
|
},
|
|
{
|
|
name: "system-event-2",
|
|
eventFn: func() {
|
|
WriteSysEvent(ctx, "system-event-2", "system-event-2")
|
|
totalEvents++
|
|
},
|
|
expectedGatedEvents: 2,
|
|
},
|
|
{
|
|
name: "audit-1",
|
|
eventFn: func() {
|
|
require.NoError(WriteAudit(ctx, "audit-1"))
|
|
totalEvents++
|
|
},
|
|
expectedGatedEvents: 2,
|
|
},
|
|
{
|
|
name: "observation-1",
|
|
eventFn: func() {
|
|
require.NoError(WriteObservation(ctx, "observation-1", WithId("observation-1"), WithHeader("foo", "bar")))
|
|
totalEvents++
|
|
},
|
|
expectedGatedEvents: 2,
|
|
},
|
|
{
|
|
name: "error-1",
|
|
eventFn: func() {
|
|
WriteError(ctx, "error-1", fmt.Errorf("error-1"))
|
|
totalEvents++
|
|
},
|
|
expectedGatedEvents: 3,
|
|
},
|
|
{
|
|
name: "error-2",
|
|
eventFn: func() {
|
|
WriteError(ctx, "error-2", fmt.Errorf("error-2"))
|
|
totalEvents++
|
|
},
|
|
expectedGatedEvents: 4,
|
|
},
|
|
// This should result in all events being flushed so none gated
|
|
{
|
|
name: "release-gate",
|
|
eventFn: func() {
|
|
require.NoError(eventer.ReleaseGate())
|
|
},
|
|
expectedGatedEvents: 0,
|
|
},
|
|
// From here on out we're verifying that all events of all types go through
|
|
{
|
|
name: "system-event-3",
|
|
eventFn: func() {
|
|
WriteSysEvent(ctx, "system-event-3", "system-event-3")
|
|
totalEvents++
|
|
},
|
|
expectedGatedEvents: 0,
|
|
},
|
|
{
|
|
name: "audit-2",
|
|
eventFn: func() {
|
|
require.NoError(WriteAudit(ctx, "audit-2"))
|
|
totalEvents++
|
|
},
|
|
expectedGatedEvents: 0,
|
|
},
|
|
{
|
|
name: "observation-2",
|
|
eventFn: func() {
|
|
require.NoError(WriteObservation(ctx, "observation-2", WithId("observation-2"), WithHeader("foo", "bar")))
|
|
totalEvents++
|
|
},
|
|
expectedGatedEvents: 0,
|
|
},
|
|
{
|
|
name: "error-2",
|
|
eventFn: func() {
|
|
WriteError(ctx, "error-2", fmt.Errorf("error-2"))
|
|
totalEvents++
|
|
},
|
|
expectedGatedEvents: 0,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
assert := assert.New(t)
|
|
tt.eventFn()
|
|
assert.Len(strutil.RemoveEmpty(strings.Split(buffer.String(), "\n")), totalEvents-tt.expectedGatedEvents, buffer.String())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestReleaseGate_NoError_CanceledContext(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
buffer := new(bytes.Buffer)
|
|
eventerConfig := EventerConfig{
|
|
AuditEnabled: true,
|
|
ObservationsEnabled: true,
|
|
SysEventsEnabled: true,
|
|
Sinks: []*SinkConfig{
|
|
{
|
|
Name: "test-sink",
|
|
EventTypes: []Type{EveryType},
|
|
Format: TextHclogSinkFormat,
|
|
Type: WriterSink,
|
|
WriterConfig: &WriterSinkTypeConfig{
|
|
Writer: buffer,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
testLock := &sync.Mutex{}
|
|
testLogger := testLogger(t, testLock)
|
|
|
|
eventer, err := NewEventer(
|
|
testLogger,
|
|
testLock,
|
|
"TestEventer_Gating",
|
|
eventerConfig,
|
|
WithGating(true),
|
|
)
|
|
require.NoError(err)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
t.Cleanup(cancel)
|
|
ctx, err = NewEventerContext(ctx, eventer)
|
|
require.NoError(err)
|
|
|
|
WriteError(ctx, "error-1", fmt.Errorf("error-1"))
|
|
_ = WriteObservation(ctx, "observation-1", WithId("observation-1"), WithHeader("foo", "bar"))
|
|
|
|
cancel()
|
|
|
|
require.NoError(eventer.ReleaseGate())
|
|
}
|