Normalize event configuration (#1444)

* Normalize event configuration

This updates event configuration stanzas to more closely match behavior
for listeners and kmses:

* Singular "sink" instead of "sinks" (probably not the most correct
  option, but a consistent option, and one that makes sense for HCL)
* Allows sinks to have the type defined at the start of the block
* Moves type-specific configuration to a specific named block

Co-authored-by: Jim <jlambert@hashicorp.com>
This commit is contained in:
Jeff MitchellandJim authored and GitHub committed 2021-08-04 14:01:58 -04:00
1 parent 00ada3c578
commit c75c97a872
14 files changed
+412 -117

No files matched your search

+1 -1
View File
@@ -11,7 +11,7 @@ require (
github.com/bufbuild/buf v0.37.0
github.com/dhui/dktest v0.3.4
github.com/fatih/color v1.12.0
github.com/fatih/structs v1.1.0 // indirect
github.com/fatih/structs v1.1.0
github.com/favadi/protoc-go-inject-tag v1.1.0
github.com/golang-migrate/migrate/v4 v4.14.1
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe
+2 -2
View File
@@ -37,8 +37,8 @@ func Test_GetOpts(t *testing.T) {
// not a valid sink, but it doesn't need to be to test the
// option is properly supported.
{
Name: "test-sink",
SinkType: "Stderr",
Name: "test-sink",
Type: "Stderr",
},
},
}
+72 -5
View File
@@ -18,6 +18,7 @@ import (
"github.com/hashicorp/go-secure-stdlib/parseutil"
"github.com/hashicorp/go-secure-stdlib/strutil"
"github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/hcl/ast"
"github.com/mitchellh/mapstructure"
)
@@ -277,7 +278,6 @@ func Parse(d string) (*Config, error) {
return nil, err
}
// Nothing to do here right now
result := New()
if err := hcl.DecodeObject(result, obj); err != nil {
return nil, err
@@ -408,17 +408,84 @@ func Parse(d string) (*Config, error) {
}
}
if result.Eventing == nil {
list, ok := obj.Node.(*ast.ObjectList)
if !ok {
return nil, fmt.Errorf("error parsing: file doesn't contain a root object")
}
eventList := list.Filter("events")
switch len(eventList.Items) {
case 0:
result.Eventing = event.DefaultEventerConfig()
} else {
if result.Eventing.Sinks == nil {
result.Eventing.Sinks = []event.SinkConfig{event.DefaultSink()}
case 1:
if result.Eventing, err = parseEventing(eventList.Items[0]); err != nil {
return nil, fmt.Errorf(`error parsing "events": %w`, err)
}
default:
return nil, fmt.Errorf(`too many "events" nodes (max 1, got %d)`, len(eventList.Items))
}
return result, nil
}
func parseEventing(eventObj *ast.ObjectItem) (*event.EventerConfig, error) {
// Decode the outside struct
var result event.EventerConfig
if err := hcl.DecodeObject(&result, eventObj.Val); err != nil {
return nil, fmt.Errorf(`error decoding "events" node: %w`, err)
}
// Now, find the sinks
eventObjType, ok := eventObj.Val.(*ast.ObjectType)
if !ok {
return nil, fmt.Errorf(`error interpreting "events" node as an object type`)
}
list := eventObjType.List
sinkList := list.Filter("sink")
// Go through each sink and decode
for i, item := range sinkList.Items {
var s event.SinkConfig
if err := hcl.DecodeObject(&s, item.Val); err != nil {
return nil, fmt.Errorf("error decoding eventer sink entry %d", i)
}
// Fix up type and validate
switch {
case s.Type != "":
case len(item.Keys) == 1:
s.Type = event.SinkType(item.Keys[0].Token.Value().(string))
default:
switch {
case s.StderrConfig != nil:
// If we haven't found the type any other way, they _must_
// specify this block even though there are no config parameters
s.Type = event.StderrSink
case s.FileConfig != nil:
s.Type = event.FileSink
default:
return nil, fmt.Errorf("sink type could not be determined")
}
}
s.Type = event.SinkType(strings.ToLower(string(s.Type)))
if s.Type == event.StderrSink && s.StderrConfig == nil {
// StderrConfig is optional as it has no values, but ensure it's
// always populated if it's the type
s.StderrConfig = new(event.StderrSinkTypeConfig)
}
if err := s.Validate(); err != nil {
return nil, err
}
// Append to result
result.Sinks = append(result.Sinks, s)
}
if len(result.Sinks) == 0 {
result.Sinks = []event.SinkConfig{event.DefaultSink()}
}
return &result, nil
}
// Sanitized returns a copy of the config with all values that are considered
// sensitive stripped. It also strips all `*Raw` values that are mainly
// used for parsing.
+119 -26
View File
@@ -5,7 +5,6 @@ import (
"os"
"testing"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/observability/event"
"github.com/hashicorp/go-secure-stdlib/configutil"
"github.com/hashicorp/go-secure-stdlib/listenerutil"
@@ -329,9 +328,9 @@ func TestController_EventingConfig(t *testing.T) {
tests := []struct {
name string
config string
config []string
wantEventerConfig *event.EventerConfig
wantErrMatch *errors.Template
wantErr string
}{
{
name: "default",
@@ -339,11 +338,11 @@ func TestController_EventingConfig(t *testing.T) {
},
{
name: "audit-enabled",
config: `
config: []string{`
events {
audit_enabled = true
}
`,
`},
wantEventerConfig: &event.EventerConfig{
AuditEnabled: true,
ObservationsEnabled: false,
@@ -354,11 +353,11 @@ func TestController_EventingConfig(t *testing.T) {
},
{
name: "observations-enabled",
config: `
config: []string{`
events {
observations_enabled = true
}
`,
`},
wantEventerConfig: &event.EventerConfig{
AuditEnabled: false,
ObservationsEnabled: true,
@@ -368,28 +367,120 @@ func TestController_EventingConfig(t *testing.T) {
},
},
{
name: "sinks-configured",
config: `
events {
name: "no-sink-type-determined",
config: []string{
`events {
audit_enabled = false
observations_enabled = true
sinks = [
{
name = "configured-sink"
sink {
format = "cloudevents-json"
name = "configured-sink"
event_types = [ "audit", "observation" ]
}
}`,
},
wantErr: `error parsing "events": sink type could not be determined`,
},
{
name: "sinks-configured",
config: []string{
`events {
audit_enabled = false
observations_enabled = true
sink {
type = "file"
format = "cloudevents-json"
name = "configured-sink"
event_types = [ "audit", "observation" ]
file {
file_name = "file-name"
event_types = [ "audit", "observation" ]
}
]
}
`,
}
sink {
type = "stderr"
format = "hclog-text"
name = "stderr-sink"
event_types = [ "error" ]
}
}`,
`events {
audit_enabled = false
observations_enabled = true
sink "file" {
format = "cloudevents-json"
name = "configured-sink"
event_types = [ "audit", "observation" ]
file {
file_name = "file-name"
}
}
sink "stderr" {
format = "hclog-text"
name = "stderr-sink"
event_types = [ "error" ]
}
}`,
`events {
audit_enabled = false
observations_enabled = true
sink {
format = "cloudevents-json"
name = "configured-sink"
event_types = [ "audit", "observation" ]
file {
file_name = "file-name"
}
}
sink {
format = "hclog-text"
name = "stderr-sink"
event_types = [ "error" ]
stderr = {}
}
}`,
`{
"events": {
"audit_enabled": false,
"observations_enabled": true,
"sink": [
{
"type": "file",
"format": "cloudevents-json",
"name": "configured-sink",
"event_types": ["audit", "observation"],
"file": {
"file_name": "file-name"
}
},
{
"format": "hclog-text",
"name": "stderr-sink",
"event_types": ["error"],
"stderr": {}
}
]
}
}`,
},
wantEventerConfig: &event.EventerConfig{
AuditEnabled: false,
ObservationsEnabled: true,
Sinks: []event.SinkConfig{
{
Type: "file",
Name: "configured-sink",
FileName: "file-name",
Format: "cloudevents-json",
EventTypes: []event.Type{"audit", "observation"},
FileConfig: &event.FileSinkTypeConfig{
FileName: "file-name",
},
},
{
Type: "stderr",
Name: "stderr-sink",
Format: "hclog-text",
EventTypes: []event.Type{"error"},
StderrConfig: &event.StderrSinkTypeConfig{},
},
},
},
@@ -398,16 +489,18 @@ func TestController_EventingConfig(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
c, err := Parse(tt.config)
if tt.wantErrMatch != nil {
for i, conf := range tt.config {
c, err := Parse(conf)
if tt.wantErr != "" {
require.Error(err)
assert.Empty(c)
assert.Equal(tt.wantErr, err.Error(), "config %d want %q and got %q", i, tt.wantErr, err.Error())
return
}
require.NoError(err)
assert.Empty(c)
assert.Truef(errors.Match(tt.wantErrMatch, err), "want %q and got %q", tt.wantErrMatch.Code, err.Error())
return
assert.NotEmpty(c)
assert.Equal(tt.wantEventerConfig, c.Eventing)
}
require.NoError(err)
assert.NotEmpty(c)
assert.Equal(tt.wantEventerConfig, c.Eventing)
})
}
}
+2 -2
View File
@@ -15,8 +15,8 @@ const (
SystemType Type = "system" // SysType represents system events
)
func (et Type) validate() error {
const op = "event.(Type).validate"
func (et Type) Validate() error {
const op = "event.(Type).Validate"
switch et {
case EveryType, ObservationType, AuditType, ErrorType, SystemType:
return nil
+16 -12
View File
@@ -203,7 +203,7 @@ func NewEventer(log hclog.Logger, serializationLock *sync.Mutex, serverName stri
var sinkId eventlogger.NodeID
var sinkNode eventlogger.Node
switch s.SinkType {
switch s.Type {
case StderrSink:
sinkNode = &writer.Sink{
Format: string(s.Format),
@@ -214,23 +214,27 @@ func NewEventer(log hclog.Logger, serializationLock *sync.Mutex, serverName stri
return nil, fmt.Errorf("%s: %w", op, err)
}
sinkId = eventlogger.NodeID(id)
default:
if _, found := allSinkFilenames[s.Path+s.FileName]; found {
return nil, fmt.Errorf("%s: duplicate file sink: %s %s", op, s.Path, s.FileName)
case FileSink:
fsc := s.FileConfig
if _, found := allSinkFilenames[fsc.Path+fsc.FileName]; found {
return nil, fmt.Errorf("%s: duplicate file sink: %s %s: %w", op, fsc.Path, fsc.FileName, ErrInvalidParameter)
}
allSinkFilenames[fsc.Path+fsc.FileName] = true
sinkNode = &eventlogger.FileSink{
Format: string(s.Format),
Path: s.Path,
FileName: s.FileName,
MaxBytes: s.RotateBytes,
MaxDuration: s.RotateDuration,
MaxFiles: s.RotateMaxFiles,
Path: fsc.Path,
FileName: fsc.FileName,
MaxBytes: fsc.RotateBytes,
MaxDuration: fsc.RotateDuration,
MaxFiles: fsc.RotateMaxFiles,
}
id, err := NewId(fmt.Sprintf("file_%s_%s_", s.Path, s.FileName))
id, err := NewId(fmt.Sprintf("file_%s_%s_", fsc.Path, fsc.FileName))
if err != nil {
return nil, fmt.Errorf("%s: %w", op, err)
}
sinkId = eventlogger.NodeID(id)
default:
return nil, fmt.Errorf("%s: unknown sink type %s", op, s.Type)
}
err = e.broker.RegisterNode(sinkId, sinkNode)
if err != nil {
@@ -474,7 +478,7 @@ func DefaultSink() SinkConfig {
Name: "default",
EventTypes: []Type{EveryType},
Format: JSONSinkFormat,
SinkType: StderrSink,
Type: StderrSink,
}
}
@@ -610,7 +614,7 @@ func (e *Eventer) StandardWriter(ctx context.Context, typ Type) (io.Writer, erro
if typ == "" {
return nil, fmt.Errorf("%s: missing type: %w", op, ErrInvalidParameter)
}
if err := typ.validate(); err != nil {
if err := typ.Validate(); err != nil {
return nil, fmt.Errorf("%s: %w", op, err)
}
newEventer := *e
@@ -9,7 +9,7 @@ type EventerConfig struct {
AuditEnabled bool `hcl:"audit_enabled"` // AuditEnabled specifies if audit events should be emitted.
ObservationsEnabled bool `hcl:"observations_enabled"` // ObservationsEnabled specifies if observation events should be emitted.
SysEventsEnabled bool `hcl:"sysevents_enabled"` // SysEventsEnabled specifies if sysevents should be emitted.
Sinks []SinkConfig `hcl:"sinks"` // Sinks are all the configured sinks
Sinks []SinkConfig `hcl:"-"` // Sinks are all the configured sinks
}
// Validate will Validate the config. A config isn't required to have any
@@ -17,7 +17,7 @@ type EventerConfig struct {
func (c *EventerConfig) Validate() error {
const op = "event.(EventerConfig).Validate"
for i, s := range c.Sinks {
if err := s.validate(); err != nil {
if err := s.Validate(); err != nil {
return fmt.Errorf("%s: sink %d is invalid: %w", op, i, err)
}
}
@@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/require"
)
func TestEventerConfig_validate(t *testing.T) {
func TestEventerConfig_Validate(t *testing.T) {
tests := []struct {
name string
c EventerConfig
@@ -19,7 +19,7 @@ func TestEventerConfig_validate(t *testing.T) {
c: EventerConfig{
Sinks: []SinkConfig{
{
SinkType: "invalid",
Type: "invalid",
},
},
},
+25 -2
View File
@@ -113,7 +113,7 @@ func Test_InitSysEventer(t *testing.T) {
Name: "default",
EventTypes: []Type{EveryType},
Format: JSONSinkFormat,
SinkType: StderrSink,
Type: StderrSink,
},
},
},
@@ -396,6 +396,29 @@ func Test_NewEventer(t *testing.T) {
lock: testLock,
wantErrIs: ErrInvalidParameter,
},
{
name: "dup-sink-filename",
config: func() EventerConfig {
dupFileConfig := TestEventerConfig(t, "dup-sink-filename")
dupFileConfig.EventerConfig.Sinks = append(dupFileConfig.EventerConfig.Sinks,
SinkConfig{
Name: "err-file-sink",
Type: FileSink,
EventTypes: []Type{ErrorType},
Format: JSONSinkFormat,
FileConfig: &FileSinkTypeConfig{
Path: "./",
FileName: dupFileConfig.ErrorEvents.Name(),
},
},
)
return dupFileConfig.EventerConfig
}(),
logger: testLogger,
lock: testLock,
serverName: "dup-sink-filename",
wantErrIs: ErrInvalidParameter,
},
{
name: "success-with-default-config",
config: EventerConfig{},
@@ -410,7 +433,7 @@ func Test_NewEventer(t *testing.T) {
Name: "default",
EventTypes: []Type{EveryType},
Format: JSONSinkFormat,
SinkType: StderrSink,
Type: StderrSink,
},
},
},
+54 -19
View File
@@ -7,31 +7,54 @@ import (
// SinkConfig defines the configuration for a Eventer sink
type SinkConfig struct {
Name string `hcl:"name"` // Name defines a name for the sink.
Description string `hcl:"description"` // Description defines a description for the sink.
EventTypes []Type `hcl:"event_types"` // EventTypes defines a list of event types that will be sent to the sink. See the docs for EventTypes for a list of accepted values.
EventSourceUrl string `hcl:"event_source_url"` // EventSource defines an optional event source URL for the sink. If not defined a default source will be composed of the https://hashicorp.com/boundary.io/ServerName/Path/FileName
AllowFilters []string `hcl:"allow_filters"` // AllowFilters define a set predicates for including an event in the sink. If any filter matches, the event will be included. The filter should be in a format supported by hashicorp/go-bexpr.
DenyFilters []string `hcl:"deny_filters"` // DenyFilters define a set predicates for excluding an event in the sink. If any filter matches, the event will be excluded. The filter should be in a format supported by hashicorp/go-bexpr.
SinkType SinkType `hcl:"sink_type"` // SinkType defines the type of sink (StderrSink or FileSink)
Format SinkFormat `hcl:"format"` // Format defines the format for the sink (JSONSinkFormat or TextSinkFormat)
Path string `hcl:"path"` // Path defines the file path for the sink
FileName string `hcl:"file_name"` // FileName defines the file name for the sink
RotateBytes int `hcl:"rotate_bytes"` // RotateByes defines the number of bytes that should trigger rotation of a FileSink
RotateDuration time.Duration `hcl:"rotate_duration"` // RotateDuration defines how often a FileSink should be rotated
RotateMaxFiles int `hcl:"rotate_max_files"` // RotateMaxFiles defines how may historical rotated files should be kept for a FileSink
Name string `hcl:"name"` // Name defines a name for the sink.
Description string `hcl:"description"` // Description defines a description for the sink.
EventTypes []Type `hcl:"event_types"` // EventTypes defines a list of event types that will be sent to the sink. See the docs for EventTypes for a list of accepted values.
EventSourceUrl string `hcl:"event_source_url"` // EventSource defines an optional event source URL for the sink. If not defined a default source will be composed of the https://hashicorp.com/boundary.io/ServerName/Path/FileName.
AllowFilters []string `hcl:"allow_filters"` // AllowFilters define a set predicates for including an event in the sink. If any filter matches, the event will be included. The filter should be in a format supported by hashicorp/go-bexpr.
DenyFilters []string `hcl:"deny_filters"` // DenyFilters define a set predicates for excluding an event in the sink. If any filter matches, the event will be excluded. The filter should be in a format supported by hashicorp/go-bexpr.
Format SinkFormat `hcl:"format"` // Format defines the format for the sink (JSONSinkFormat or TextSinkFormat).
Type SinkType `hcl:"type"` // Type defines the type of sink (StderrSink or FileSink).
StderrConfig *StderrSinkTypeConfig `hcl:"stderr"` // StderrConfig defines parameters for a stderr output.
FileConfig *FileSinkTypeConfig `hcl:"file"` // FileConfig defines parameters for a file output.
}
func (sc *SinkConfig) validate() error {
const op = "event.(SinkConfig).validate"
if err := sc.SinkType.validate(); err != nil {
func (sc *SinkConfig) Validate() error {
const op = "event.(SinkConfig).Validate"
if err := sc.Type.Validate(); err != nil {
return fmt.Errorf("%s: %w", op, err)
}
if err := sc.Format.Validate(); err != nil {
return fmt.Errorf("%s: %w", op, err)
}
if sc.SinkType == FileSink && sc.FileName == "" {
return fmt.Errorf("%s: missing sink file name: %w", op, ErrInvalidParameter)
var foundSinkTypeConfigs int
if sc.StderrConfig != nil {
foundSinkTypeConfigs++
}
if sc.FileConfig != nil {
foundSinkTypeConfigs++
}
if foundSinkTypeConfigs > 1 {
return fmt.Errorf("%s: too many sink type config blocks: %w", op, ErrInvalidParameter)
}
switch sc.Type {
case StderrSink:
// It's okay for StderrConfig to be nil if the type is stderr, but it's
// not okay for something else to be set
if foundSinkTypeConfigs == 1 && sc.StderrConfig == nil {
return fmt.Errorf("%s: mismatch between sink type and sink configuration block: %w", op, ErrInvalidParameter)
}
case FileSink:
// Unlike in the stderr case, this can't be nil, so if it's not nil
// we've now verified it's the only block populated
if sc.FileConfig == nil {
return fmt.Errorf(`%s: missing "file" block: %w`, op, ErrInvalidParameter)
}
if sc.FileConfig.FileName == "" {
return fmt.Errorf("%s: missing file name: %w", op, ErrInvalidParameter)
}
}
if sc.Name == "" {
return fmt.Errorf("%s: missing sink name: %w", op, ErrInvalidParameter)
@@ -40,13 +63,25 @@ func (sc *SinkConfig) validate() error {
return fmt.Errorf("%s: missing event types: %w", op, ErrInvalidParameter)
}
for _, et := range sc.EventTypes {
if err := et.validate(); err != nil {
if err := et.Validate(); err != nil {
return fmt.Errorf("%s: %w", op, err)
}
}
return nil
}
// StderrSinkTypeConfig contains configuration structures for file sink types
type StderrSinkTypeConfig struct{}
// FileSinkTypeConfig contains configuration structures for file sink types
type FileSinkTypeConfig struct {
Path string `hcl:"path" mapstructure:"path"` // Path defines the file path for the sink
FileName string `hcl:"file_name" mapstructure:"file_name"` // FileName defines the file name for the sink
RotateBytes int `hcl:"rotate_bytes" mapstructure:"rotate_bytes"` // RotateByes defines the number of bytes that should trigger rotation of a FileSink
RotateDuration time.Duration `hcl:"rotate_duration" mapstructure:"rotate_duration"` // RotateDuration defines how often a FileSink should be rotated
RotateMaxFiles int `hcl:"rotate_max_files" mapstructure:"rotate_max_files"` // RotateMaxFiles defines how may historical rotated files should be kept for a FileSink
}
// FilterType defines a type for filters (allow or deny)
type FilterType string
@@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/require"
)
func TestSinkConfig_validate(t *testing.T) {
func TestSinkConfig_Validate(t *testing.T) {
t.Parallel()
tests := []struct {
name string
@@ -19,9 +19,11 @@ func TestSinkConfig_validate(t *testing.T) {
name: "missing-name",
sc: SinkConfig{
EventTypes: []Type{EveryType},
SinkType: FileSink,
FileName: "tmp.file",
Format: JSONSinkFormat,
Type: FileSink,
FileConfig: &FileSinkTypeConfig{
FileName: "tmp.file",
},
Format: JSONSinkFormat,
},
wantErrIs: ErrInvalidParameter,
wantErrContains: "missing sink name",
@@ -29,10 +31,12 @@ func TestSinkConfig_validate(t *testing.T) {
{
name: "missing-EventType",
sc: SinkConfig{
Name: "sink-name",
SinkType: FileSink,
FileName: "tmp.file",
Format: JSONSinkFormat,
Name: "sink-name",
Type: FileSink,
FileConfig: &FileSinkTypeConfig{
FileName: "tmp.file",
},
Format: JSONSinkFormat,
},
wantErrIs: ErrInvalidParameter,
wantErrContains: "missing event types",
@@ -42,9 +46,11 @@ func TestSinkConfig_validate(t *testing.T) {
sc: SinkConfig{
Name: "sink-name",
EventTypes: []Type{"invalid"},
SinkType: FileSink,
FileName: "tmp.file",
Format: JSONSinkFormat,
Type: FileSink,
FileConfig: &FileSinkTypeConfig{
FileName: "tmp.file",
},
Format: JSONSinkFormat,
},
wantErrIs: ErrInvalidParameter,
wantErrContains: "not a valid event type",
@@ -54,8 +60,10 @@ func TestSinkConfig_validate(t *testing.T) {
sc: SinkConfig{
Name: "sink-name",
EventTypes: []Type{EveryType},
FileName: "tmp.file",
Format: JSONSinkFormat,
FileConfig: &FileSinkTypeConfig{
FileName: "tmp.file",
},
Format: JSONSinkFormat,
},
wantErrIs: ErrInvalidParameter,
wantErrContains: "not a valid sink type",
@@ -65,9 +73,11 @@ func TestSinkConfig_validate(t *testing.T) {
sc: SinkConfig{
Name: "sink-name",
EventTypes: []Type{EveryType},
SinkType: "invalid",
FileName: "tmp.file",
Format: JSONSinkFormat,
Type: "invalid",
FileConfig: &FileSinkTypeConfig{
FileName: "tmp.file",
},
Format: JSONSinkFormat,
},
wantErrIs: ErrInvalidParameter,
wantErrContains: "not a valid sink type",
@@ -76,9 +86,11 @@ func TestSinkConfig_validate(t *testing.T) {
name: "missing-format",
sc: SinkConfig{
Name: "sink-name",
SinkType: FileSink,
Type: FileSink,
EventTypes: []Type{EveryType},
FileName: "tmp.file",
FileConfig: &FileSinkTypeConfig{
FileName: "tmp.file",
},
},
wantErrIs: ErrInvalidParameter,
wantErrContains: "not a valid sink format",
@@ -88,9 +100,11 @@ func TestSinkConfig_validate(t *testing.T) {
sc: SinkConfig{
Name: "sink-name",
Format: "invalid",
SinkType: FileSink,
Type: FileSink,
EventTypes: []Type{EveryType},
FileName: "tmp.file",
FileConfig: &FileSinkTypeConfig{
FileName: "tmp.file",
},
},
wantErrIs: ErrInvalidParameter,
wantErrContains: "not a valid sink format",
@@ -99,27 +113,76 @@ func TestSinkConfig_validate(t *testing.T) {
name: "file-sink-with-no-file-name",
sc: SinkConfig{
EventTypes: []Type{EveryType},
SinkType: FileSink,
Type: FileSink,
Format: JSONSinkFormat,
FileConfig: &FileSinkTypeConfig{},
},
wantErrIs: ErrInvalidParameter,
wantErrContains: "missing sink file name",
wantErrContains: "missing file name",
},
{
name: "type mismatch file type stderr config",
sc: SinkConfig{
EventTypes: []Type{EveryType},
Type: FileSink,
Format: JSONSinkFormat,
StderrConfig: &StderrSinkTypeConfig{},
},
wantErrIs: ErrInvalidParameter,
wantErrContains: `missing "file" block`,
},
{
name: "type mismatch stderr type file config",
sc: SinkConfig{
EventTypes: []Type{EveryType},
Type: StderrSink,
Format: JSONSinkFormat,
FileConfig: &FileSinkTypeConfig{},
},
wantErrIs: ErrInvalidParameter,
wantErrContains: `mismatch between sink type and sink configuration block`,
},
{
name: "type mismatch both types file config",
sc: SinkConfig{
EventTypes: []Type{EveryType},
Type: FileSink,
Format: JSONSinkFormat,
StderrConfig: &StderrSinkTypeConfig{},
FileConfig: &FileSinkTypeConfig{},
},
wantErrIs: ErrInvalidParameter,
wantErrContains: `too many sink type config blocks`,
},
{
name: "type mismatch both types stderr config",
sc: SinkConfig{
EventTypes: []Type{EveryType},
Type: StderrSink,
Format: JSONSinkFormat,
StderrConfig: &StderrSinkTypeConfig{},
FileConfig: &FileSinkTypeConfig{},
},
wantErrIs: ErrInvalidParameter,
wantErrContains: `too many sink type config blocks`,
},
{
name: "valid",
sc: SinkConfig{
Name: "valid",
EventTypes: []Type{EveryType},
SinkType: FileSink,
FileName: "tmp.file",
Format: JSONSinkFormat,
Type: FileSink,
FileConfig: &FileSinkTypeConfig{
FileName: "tmp.file",
},
Format: JSONSinkFormat,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
err := tt.sc.validate()
err := tt.sc.Validate()
if tt.wantErrIs != nil {
require.Error(err)
assert.ErrorIs(err, tt.wantErrIs)
+1 -1
View File
@@ -11,7 +11,7 @@ const (
type SinkType string // SinkType defines the type of sink in a config stanza (file, stderr)
func (t SinkType) validate() error {
func (t SinkType) Validate() error {
const op = "event.(SinkType).validate"
switch t {
case StderrSink, FileSink:
+26 -16
View File
@@ -79,25 +79,29 @@ func TestEventerConfig(t *testing.T, testName string, opt ...Option) TestConfig
Sinks: []SinkConfig{
{
Name: "every-type-file-sink",
SinkType: FileSink,
Type: FileSink,
EventTypes: []Type{EveryType},
Format: opts.withSinkFormat,
Path: "./",
FileName: tmpAllFile.Name(),
FileConfig: &FileSinkTypeConfig{
Path: "./",
FileName: tmpAllFile.Name(),
},
},
{
Name: "stderr",
SinkType: StderrSink,
Type: StderrSink,
EventTypes: []Type{EveryType},
Format: opts.withSinkFormat,
},
{
Name: "err-file-sink",
SinkType: FileSink,
Type: FileSink,
EventTypes: []Type{ErrorType},
Format: opts.withSinkFormat,
Path: "./",
FileName: tmpErrFile.Name(),
FileConfig: &FileSinkTypeConfig{
Path: "./",
FileName: tmpErrFile.Name(),
},
},
},
},
@@ -112,11 +116,13 @@ func TestEventerConfig(t *testing.T, testName string, opt ...Option) TestConfig
})
c.EventerConfig.Sinks = append(c.EventerConfig.Sinks, SinkConfig{
Name: "audit-file-sink",
SinkType: FileSink,
Type: FileSink,
EventTypes: []Type{AuditType},
Format: opts.withSinkFormat,
Path: "./",
FileName: tmpFile.Name(),
FileConfig: &FileSinkTypeConfig{
Path: "./",
FileName: tmpFile.Name(),
},
})
c.AuditEvents = tmpFile
}
@@ -128,11 +134,13 @@ func TestEventerConfig(t *testing.T, testName string, opt ...Option) TestConfig
})
c.EventerConfig.Sinks = append(c.EventerConfig.Sinks, SinkConfig{
Name: "err-observation-sink",
SinkType: FileSink,
Type: FileSink,
EventTypes: []Type{ObservationType},
Format: opts.withSinkFormat,
Path: "./",
FileName: tmpFile.Name(),
FileConfig: &FileSinkTypeConfig{
Path: "./",
FileName: tmpFile.Name(),
},
})
c.ObservationEvents = tmpFile
}
@@ -144,11 +152,13 @@ func TestEventerConfig(t *testing.T, testName string, opt ...Option) TestConfig
})
c.EventerConfig.Sinks = append(c.EventerConfig.Sinks, SinkConfig{
Name: "err-sysevents-sink",
SinkType: FileSink,
Type: FileSink,
EventTypes: []Type{SystemType},
Format: opts.withSinkFormat,
Path: "./",
FileName: tmpFile.Name(),
FileConfig: &FileSinkTypeConfig{
Path: "./",
FileName: tmpFile.Name(),
},
})
}
return c
+1 -1
View File
@@ -240,7 +240,7 @@ func NewTestWorker(t *testing.T, opts *TestWorkerOpts) *TestWorker {
}
tw.name = opts.Config.Worker.Name
if err := tw.b.SetupEventing(tw.b.Logger, tw.b.StderrLock, opts.Config.Worker.Name+"/test-worker", base.WithEventerConfig(opts.Config.Eventing)); err != nil {
if err := tw.b.SetupEventing(tw.b.Logger, tw.b.StderrLock, opts.Config.Worker.Name, base.WithEventerConfig(opts.Config.Eventing)); err != nil {
t.Fatal(err)
}