Files
threshold/internal/observability/event/testing.go
T
Jeff MitchellandJim 7ab7c8eaf4 Have fallback eventing use json format (#2216)
* Have fallback eventing use json format

The server will initialize a logger via InitFallbackLogger(...)
after setting up the server's logging (and honoring the related
cmd flags).  This fallback logger will be used if when no eventer
is initialized.

If no fallback logger is initialized, then a event.defaultLogger() will
be used if no eventer is defined.

Co-authored-by: Jim <jlambert@hashicorp.com>
2022-07-11 16:12:02 -04:00

327 lines
7.9 KiB
Go

package event
import (
"context"
"io/ioutil"
"os"
"sync"
"testing"
"time"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/groups"
"github.com/hashicorp/eventlogger"
"github.com/hashicorp/go-hclog"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/wrapperspb"
)
// TestWithoutEventing allows the caller to "disable" all eventing for a test.
// You must not run the test in parallel (no calls to t.Parallel) since the
// function relies on modifying the system wide default eventer.
func TestWithoutEventing(t testing.TB) *Eventer {
t.Helper()
require := require.New(t)
testConfig := EventerConfig{
AuditEnabled: false,
ObservationsEnabled: false,
SysEventsEnabled: false,
}
testLock := &sync.Mutex{}
testLogger := hclog.New(&hclog.LoggerOptions{
Mutex: testLock,
Output: ioutil.Discard,
})
testEventer, err := NewEventer(testLogger, testLock, "TestWithoutEventing", testConfig, withNoDefaultSink(t))
require.NoError(err)
require.NoError(InitSysEventer(testLogger, testLock, "TestWithoutEventing", WithEventer(testEventer)))
return testEventer
}
// TestGetEventerConfig is a test accessor for the eventer's config
func TestGetEventerConfig(t testing.TB, e *Eventer) EventerConfig {
t.Helper()
return e.conf
}
// TestResetSysEventer will reset event.syseventer to an uninitialized state.
func TestResetSystEventer(t testing.TB) {
t.Helper()
sysEventerLock.Lock()
defer sysEventerLock.Unlock()
sysEventer = nil
}
type TestConfig struct {
EventerConfig EventerConfig
AllEvents *os.File
ErrorEvents *os.File
ObservationEvents *os.File
AuditEvents *os.File
}
// TestEventerConfig creates a test config and registers a cleanup func for its
// test tmp files.
func TestEventerConfig(t testing.TB, testName string, opt ...Option) TestConfig {
t.Helper()
require := require.New(t)
tmpAllFile, err := ioutil.TempFile("./", "tmp-all-events-"+testName)
require.NoError(err)
tmpErrFile, err := ioutil.TempFile("./", "tmp-errors-"+testName)
require.NoError(err)
t.Cleanup(func() {
os.Remove(tmpAllFile.Name())
os.Remove(tmpErrFile.Name())
})
opts := getOpts(opt...)
if opts.withSinkFormat == "" {
opts.withSinkFormat = JSONSinkFormat
}
c := TestConfig{
EventerConfig: EventerConfig{
ObservationsEnabled: true,
AuditEnabled: true,
Sinks: []*SinkConfig{
{
Name: "every-type-file-sink",
Type: FileSink,
EventTypes: []Type{EveryType},
Format: opts.withSinkFormat,
FileConfig: &FileSinkTypeConfig{
Path: "./",
FileName: tmpAllFile.Name(),
},
AuditConfig: DefaultAuditConfig(),
},
{
Name: "stderr",
Type: StderrSink,
EventTypes: []Type{EveryType},
Format: opts.withSinkFormat,
AuditConfig: DefaultAuditConfig(),
},
{
Name: "err-file-sink",
Type: FileSink,
EventTypes: []Type{ErrorType},
Format: opts.withSinkFormat,
FileConfig: &FileSinkTypeConfig{
Path: "./",
FileName: tmpErrFile.Name(),
},
},
},
},
AllEvents: tmpAllFile,
ErrorEvents: tmpErrFile,
}
if opts.withAuditSink {
tmpFile, err := ioutil.TempFile("./", "tmp-audit-"+testName)
require.NoError(err)
t.Cleanup(func() {
os.Remove(tmpFile.Name())
})
c.EventerConfig.Sinks = append(c.EventerConfig.Sinks, &SinkConfig{
Name: "audit-file-sink",
Type: FileSink,
EventTypes: []Type{AuditType},
Format: opts.withSinkFormat,
FileConfig: &FileSinkTypeConfig{
Path: "./",
FileName: tmpFile.Name(),
},
AuditConfig: DefaultAuditConfig(),
})
c.AuditEvents = tmpFile
}
if opts.withObservationSink {
tmpFile, err := ioutil.TempFile("./", "tmp-observation-"+testName)
require.NoError(err)
t.Cleanup(func() {
os.Remove(tmpFile.Name())
})
c.EventerConfig.Sinks = append(c.EventerConfig.Sinks, &SinkConfig{
Name: "err-observation-sink",
Type: FileSink,
EventTypes: []Type{ObservationType},
Format: opts.withSinkFormat,
FileConfig: &FileSinkTypeConfig{
Path: "./",
FileName: tmpFile.Name(),
},
})
c.ObservationEvents = tmpFile
}
if opts.withSysSink {
tmpFile, err := ioutil.TempFile("./", "tmp-sysevents-"+testName)
require.NoError(err)
t.Cleanup(func() {
os.Remove(tmpFile.Name())
})
c.EventerConfig.Sinks = append(c.EventerConfig.Sinks, &SinkConfig{
Name: "err-sysevents-sink",
Type: FileSink,
EventTypes: []Type{SystemType},
Format: opts.withSinkFormat,
FileConfig: &FileSinkTypeConfig{
Path: "./",
FileName: tmpFile.Name(),
},
})
}
return c
}
// TestRequestInfo provides a test RequestInfo
func TestRequestInfo(t testing.TB) *RequestInfo {
t.Helper()
return &RequestInfo{
EventId: "test-event-id",
Id: "test-request-info",
Method: "POST",
Path: "/test/request/info",
PublicId: "public-id",
}
}
func testAuth(t testing.TB) *Auth {
t.Helper()
return &Auth{
UserEmail: "test-auth@example.com",
UserName: "test-auth-user-name",
}
}
func testRequest(t testing.TB) *Request {
t.Helper()
return &Request{
Operation: "op",
Endpoint: "/group/<id>",
Details: &pbs.GetGroupRequest{
Id: "group-id",
},
}
}
func testResponse(t testing.TB) *Response {
t.Helper()
return &Response{
StatusCode: 200,
Details: &pbs.GetGroupResponse{
Item: &groups.Group{
Id: "group-id",
ScopeId: "org-id",
Name: &wrapperspb.StringValue{
Value: "group-name",
},
},
},
}
}
// TestWithBroker is an unexported and a test option for passing in an optional broker
func TestWithBroker(t testing.TB, b broker) Option {
t.Helper()
return func(o *options) {
o.withBroker = b
}
}
// TestWithObservationSink is a test option
func TestWithObservationSink(t testing.TB) Option {
t.Helper()
return func(o *options) {
o.withObservationSink = true
}
}
// TestWithAuditSink is a test option
func TestWithAuditSink(t testing.TB) Option {
t.Helper()
return func(o *options) {
o.withAuditSink = true
}
}
// TestWithSysSink is a test option
func TestWithSysSink(t testing.TB) Option {
t.Helper()
return func(o *options) {
o.withSysSink = true
}
}
// withNoDefaultSink is an unexported test option
func withNoDefaultSink(t testing.TB) Option {
t.Helper()
return func(o *options) {
o.withNoDefaultSink = true
}
}
// testWithSinkFormat is an unexported and a test option
func testWithSinkFormat(t testing.TB, fmt SinkFormat) Option {
t.Helper()
return func(o *options) {
o.withSinkFormat = fmt
}
}
type testMockBroker struct {
reopened bool
stopTimeAt time.Time
registeredNodeIds []eventlogger.NodeID
successThresholds map[eventlogger.EventType]int
pipelines []eventlogger.Pipeline
errorOnSend error
}
func (b *testMockBroker) Reopen(ctx context.Context) error {
b.reopened = true
return nil
}
func (b *testMockBroker) RegisterPipeline(def eventlogger.Pipeline) error {
b.pipelines = append(b.pipelines, def)
return nil
}
func (b *testMockBroker) Send(ctx context.Context, t eventlogger.EventType, payload interface{}) (eventlogger.Status, error) {
if b.errorOnSend != nil {
return eventlogger.Status{}, b.errorOnSend
}
return eventlogger.Status{}, nil
}
func (b *testMockBroker) StopTimeAt(t time.Time) {
b.stopTimeAt = t
}
func (b *testMockBroker) RegisterNode(id eventlogger.NodeID, node eventlogger.Node) error {
b.registeredNodeIds = append(b.registeredNodeIds, id)
return nil
}
func (b *testMockBroker) SetSuccessThreshold(t eventlogger.EventType, successThreshold int) error {
if b.successThresholds == nil {
b.successThresholds = map[eventlogger.EventType]int{}
}
b.successThresholds[t] = successThreshold
return nil
}
func testLogger(t *testing.T, testLock hclog.Locker) hclog.Logger {
t.Helper()
return hclog.New(&hclog.LoggerOptions{
Mutex: testLock,
Name: "test",
JSONFormat: true,
})
}